16

I frequently run into the problem that I have to preserve state between several invocations of an activity (i.e. going through several onCreate()/onDelete() cycles). Unfortunately, Android's support for doing that is really poor.

As an easy way to preserve state, I thought that since the class is only loaded once by the class loader, that it would be safe to store temporary data that's shared between several instances of an activity in a static Bundle field.

However, occasionally, when instance A creates the static bundle and stores data in it, then gets destroyed, and instance B tries to read from it, the static field is suddenly NULL.

Doesn't that mean that the class had been removed and reloaded by the classloader while the activity was going through a create/destroy cycle? How else could a static field suddenly become NULL when it was referencing an object before?

mxk
  • 43,056
  • 28
  • 105
  • 132
  • Not sure the answer to your question, but I know that the Android developers recommend you do NOT keep static references unnecessarily, as they are prone to memory leaks: http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html – I82Much Oct 28 '09 at 13:25
  • 2
    well, that article is about leaking Contexts. That's only a concern if the Context is strongly reachable through that static reference, which is the case for e.g. Views, but not for Bundles (as far as I know?). But generally I would assume that a static reference, once the class has been loaded, lives as long as the app, which doesn't seem to be true in Android. – mxk Oct 28 '09 at 13:46

2 Answers2

15

The first part of this answer is really old -- see below for the right way to do it

You can use the Application object to store application persistent objects. This Android FAQ talks about this problem as well.

Something like this:

public class MyApplication extends Application{
    private String thing = null;

    public String getThing(){
        return thing;
    }

    public void setThing( String thing ){
        this.thing = thing;
    }
}

public class MyActivity extends Activity {
    private MyApplication app;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        app = ((MyApplication)getApplication());

        String thing = app.getThing();
    }
}

The right way:

When this answer was first written, the documentation for the Activity lifecycle was not as good as it is now. Reading Saving Activity State section on the Activity document helps us understand how Android wants us to save state. Essentially, there are two circumstances under which your activity starts: (1) as a new activity and (2) because of a configuration change or when it's recreated after being destroyed due to memory pressure. When your activity starts because it's a new activity, then saveInstanceState is null. It's not null otherwise. If it's null, then your activity should initialize itself from scratch. Fragments are very similar to Activities, and I covered this concept in detail for my AnDevCon-14 slide deck. You can also take a look at the sample code for my AnDevCon-14 presentation for more details.

Reworking my previous example would look something like the code below. I do change the semantics a bit -- in this second version I assume the string thing is specific to the activity within a specific android task, in the previous example it's ambiguous. If you do want to keep the same data around for multiple android tasks, then using either the Application object or another singleton is still your best bet.

public class MyActivity extends Activity {
    private static final String THING = "THING";

    private String thing;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState==null) {
            // First time here (since we last backed out at least)
            thing = initializeThing(); // somehow we init it
        } else {
            // Rehydrate this new instance of the Activity
            thing = savedInstanceState.getString(THING);
        }

        String thing = app.getThing();
    }

    protected void onSaveInstanceState(Bundle outState) {
        outState.putString(THING, thing);
    }
}
JohnnyLambada
  • 12,700
  • 11
  • 57
  • 61
  • 4
    that's a pretty good and simple idea actually... as long as it doesn't turn into the god object anti-pattern – mxk Oct 29 '09 at 09:57
  • Hehe, I hadn't heard of the god object [1] pattern before. Pretty funny stuff. I probably should have said that this is just a simple example. For anything more than a trivial app, your Application class should simply have other objects that hold the real data, not be the repository of the data itself. It's a convenient place in Android to store other objects. [1] http://en.wikipedia.org/wiki/God_object – JohnnyLambada Oct 29 '09 at 16:31
  • 1
    I'm moving away from modifying the Application class and moving toward using singletons. See Dianne Hackbod's Answer here: http://stackoverflow.com/questions/3826905/singletons-vs-application-context-in-android – JohnnyLambada May 04 '11 at 20:53
  • what about this in docs >>> A public static field/method An alternate way to make data accessible across Activities/Services is to use public static fields and/or methods. You can access these static fields from any other class in your application. To share an object, the activity which creates your object sets a static field to point to this object and any other activity that wants to use this object just accesses this static field. – Mohammed Subhi Sheikh Quroush Feb 18 '14 at 12:42
  • 1
    I am confused. You have suggested another way. Does that mean the answer to the OP's question is "No"? I should not use static fields in an Activity? – Damn Vegetables Oct 20 '14 at 03:34
  • @MohammedSubhiSheikhQuroush's while that would work, mutable public static fields are essentially [evil global variables](http://c2.com/cgi/wiki?GlobalVariablesAreBad). – JohnnyLambada Oct 21 '14 at 23:10
  • @SinJeong-hun the OP's suggestion of a single static bundle is not a good one. His assertion that Android has poor support for this is not true (however, at the time he wrote it, the _documentation_ may have been poor). The "proper" way to save state across Activity lifecycle events is using the `savedInstanceState` `Bundle` as I suggest in the revised part of my answer. – JohnnyLambada Oct 21 '14 at 23:14
1

The other, also evil, way to keep static data is to have you activity spin up a singleton class. This singleton would keep a static reference to itself.

class EvilSingleton{
    private static EvilSingleton instance;

    //put your data as non static variables here

    public static EvilSingleton getInstance()
    {
        if(instance == null)
            instance = new EvilSingleton();
        return instance;
    }
}

In the onCreate() method of your activity you can access/build the singleton and any data you might need. That way, your activity or application can get destroyed or recreated any number of times and as long as your process' memory space is preserved you should be ok.

This is an evil subversive hack, so no promises ;-)

haseman
  • 11,213
  • 8
  • 41
  • 38
  • 2
    thanks, but no thanks. I've had enough fun with singletons. It's the most abused design pattern of them all, and in most cases I've seen it being used, it was actually an anti-pattern. – mxk Oct 29 '09 at 09:58
  • Fair enough. It's not much of a pattern, I've used them from time under heavy schedule pressure but I agree, in a large, well maintained project, they should be used sparingly at best. – haseman Oct 29 '09 at 16:36
  • In defence of Singleton, in Android there's a good use case for it: the SqliteOpenHelper instance. Other than that, I agree with you guys. – Henrique de Sousa Sep 23 '15 at 11:04