How do I preserve data objects in Android/Java?

2019-05-20 12:09发布

问题:

I'm a computer hobbyist who is working on a very small trivia game for the Android platform.

In this game, I move the player between multiple activity/view pages (entry, question page, answer page, ultimate score page). I've created a question object that contains all data related to a single trivia question (question, four answers, pointer to correct answer, related comments that the computer makes when responding to the player's guess), and I've successfully populated an ArrayList of Question objects with data that I DOM parsed from an XML file.

What is an acceptable way of passing this data back and forth between activities as the game progresses? How can I keep these data objects alive as the user moves through the game?

I understand that global variables are highly discouraged. I have also learned that the Singleton design pattern poses many of the problems said to be associated with global variables. I'm stumped when it comes to finding alternatives to those approaches.

I understand the basics of the MVC approach. My activity *.java files are the controller files, and they're linked to the layouts (views) I've created with XML. I have two "model" objects that need to be maintained and modified as the game progresses: (1) the question-bank ArrayList mentioned above and (2) some sort of "PlayerProgress" object that contains all data related to the player's progress in the game.

These data objects are first instantiated at the beginning of the game, but I don't know how to keep them alive as the user moves between activities. I know that I can pass information between activities as EXTRAS, but EXTRA don't seem to be intended for this purpose. Even if EXTRAS worked for the player progress properties, I don't think I can use them to pass an ArrayList of 25-50 question objects between activities.

I have looked into serialization and parcelable, but it seems weird (and possibly inefficient) to basically decompose and then recompose my data model objects every time the user moves back and forth between different activities/views. If one of these is an acceptable/common way to accomplish this, I can happily plunge forward, but I wanted to check with others first.

I keep butting my head against this in different programming languages, and suspect that there is some bigger part of the picture that I'm failing to understand. I've read through the descriptions of objects and application life cycles in many different resources, but I still haven't managed to figure out the solution to this basic question.

I'm asking this question ("What is an acceptable way of passing data objects back and forth between activities as the game progresses") in the context of my silly trivia game, but I'm really concerned with getting a handle on the bigger picture. If folks don't have the time to spell out something so seemingly basic, perhaps you can point me to descriptions in other books that you found useful? (I've got a few slots open in my Safari bookshelf, and can track down copies of almost any technical publication.)

Thanks.

回答1:

I haven't got enough votes to answer directly in 323go's post about static variables... but that implementation is exactly Singleton. I wanted to point this out because you stated in your question that you also learned that the Singleton design pattern poses many of the problems said to be associated with global variables.

I admit extras are quite weird first time if you're used to call a function or a program with its parameters inline, but it seems it's the usual way in Android. I don't see what's the problem using extras, since it's almost transparent for you if your model objects are not really complex. I mean you just need to implement "Serializable" interface and it's done. There are some data types that are not working "by itself" with Serializable (as lists) and you'll need to implement Parcelable, but you can declare arrays insted. It seems Parcelable is "far more efficient", so if you wanna better performing you'll need to implement Parcelable anyway as someone said here: Android: Difference between Parcelable and Serializable?

I don't think Shared Preferences are intended to be used as a "passing arguments tool" since it wouldn't be so efficient as it has to write and read to physical memory, what use to be "hard job" in any computer system; while exras would be sent through the memory stack (Disclaimer: I didn't tested, but it's what logic tells me). I use dannyroa's way (storing json objects) for time-persisting issues as user preferences, internet-retrieved hardly-updated-data, scorings, savegames....

And, finally... maybe you can restructure your code. Are you opening a new activity for each question? Maybe you can use just one Activity and use fragments. Then, you can have your global variable in the activity and any fragment inside can access there with getActivity().

((MyActivity)getActivity()).myGlobalVariable();


回答2:

You can put an array of parcelable objects in Intent Extras, so it's possible to send an array of parcelable question objects between activities.

Personally I'd use a sqlite database for storing the questions and pull them from the DB through a content provider.

May be overkill for your currently app but if you ever expand the question set you could alter the SQL to pull out the relevant subset of questions for each activity.



回答3:

What I have tried is saving them in SharedPreferences.

To save objects, I use Gson library to convert it to a JSON string & convert the JSON string back into the object.

However, this means it would only work for Serializable objects & there's an overhead with the reading and writing from SharedPreferences.

Ideal solution is a combination of global static & SharedPreferences. In the event that the static variable is null, get it from SharedPreferences. Make sure to clear the static variable when the value in SharedPreferences is updated.

static String userInfo = null;
public static String getUserInfo() {
    if (userInfo != null) {
        return userInfo;
    }
    return _getStringFromPref("userInfo");
}

public static String setUserInfo(String val) {
    _putStringToPref("userInfo", val);
    userInfo = val; //or you can just set userInfo to null;
}

You can also use a local database (sqlite) instead of SharedPreferences.



回答4:

In my original post, I promised to circle back after investigating all options.

Just wanted to report that the easiest solution, as Ender suggests, is the use of multiple fragments and a single activity page. This makes it possible to completely avoid global static objects since all fragments can access the MainActivity class.