1

I've spent a while reading up on this and have yet to find an answer.

I have a few activities all within one app. I'd like to share some data between them that is not suitable for intents or serialized data files. I know I can use the overall Application for this purpose, but I'm not fully understanding how it works.

We'll call my overall application class "MyApplication" and the main activity "MyActivity". I would expect that once the app begins from a cold start, it first instantiates and initializes MyApplication (calling onCreate()) and then goes on to instantiate and start my activity (MyActivity) (which was marked with the intent android.intent.action.MAIN). The logs seem to bear this out.

But... if I try to set the following Activity-wide variable, it gets set to null: private final MyApplication THISAPP = (MyApplication)getApplication();

This is peculiar because the application definitely exists at this point. (Also, I can't delay setting a final variable til MyActivity.onCreate() gets called - that's disallowed ("The final field...cannot be assigned")).

Note that this works if I don't call THISAPP "final" and I assign it in onCreate(). That of course defeats the purpose of protecting a variable with "final" though, and it seems like it should be possible to do.

So, why isn't getApplication() producing a non-null value before onCreate()? Or is this some strange matter of context? (I've found a vague reference to context not being valid til after onCreate() is done, but would that apply to the parent Application's context as seen from a child Activity? I'd expect the parent to already have that set at that point.)

Edit: to emphasize, I can get this to work if I don't try to use final and if I remember to put the "." before the application name in the manifest (a mistake I made and corrected well-before this I wrote this question). It just isn't clear why getApplication() (or getApplicationContext() for that matter) aren't usable before onCreate() in a child Activity... unless "that's just how it is".

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
MartyMacGyver
  • 9,483
  • 11
  • 47
  • 67
  • 1
    This may be helpful/insightful: http://stackoverflow.com/questions/708012/android-how-to-declare-global-variables – WilHall Oct 16 '11 at 17:13
  • I've been looking at that post but it doesn't answer why this happens as I'm seeing it (in fact, it's part of how I got this working at all, but not optimally IMHO). And it's not clear how getApplicationContext() differs from getApplication() if at all... I've tried both but they simply won't work in an Activity as part of a final variable initializer. – MartyMacGyver Oct 16 '11 at 17:25

4 Answers4

3

I'd like to share some data between them that is not suitable for intents or serialized data files. I know I can use the overall Application for this purpose, but I'm not fully understanding how it works.

I'd just use static data members as a cache for your persistent store, rather than fussing around with Application. Application gives you little advantage over static data members and has one big cost: there can only be one Application, whereas you can organize your static data members/singletons however you like.

I would expect that once the app begins from a cold start, it first instantiates and initializes MyApplication (calling onCreate()) and then goes on to instantiate and start my activity (MyActivity) (which was marked with the intent android.intent.action.MAIN). The logs seem to bear this out.

Correct.

But... if I try to set the following Activity-wide variable, it gets set to null: private final MyApplication THISAPP = (MyApplication)getApplication();

Of course. The activity object has not yet been initialized. You are welcome to use initializers like this for constants or things you get from static methods (e.g., Calendar.getInstance()) or other constructors (e.g., new ArrayList<Foo>()). Do not call a superclass method an expect it to work at this point, since the constructor chain has not yet been called on the object being instantiated. This is just Java, nothing particular to Android.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Fair enough; now that I see this is related to the constructor chain then it makes more sense. I'm not yet sure how I'd access static static data members readily (I assume you mean public members of the respective Activities, or do you mean something else?) I have no problem using the Application object if the only "disadvantage" is that it's global (it seems to be an advantage here, in terms of data visibility... I'm not touting globals as virtuous, but they'll be FAR less cumbersome for passing complex data between Activities.) – MartyMacGyver Oct 16 '11 at 18:00
  • @MartyMacGyver: If it is complex, either it should be part of your data model (in which case, a static cache backed by persistent storage is one strategy), or it should not be passed between activities (e.g., steps in a wizard should all be part of one activity). I can think of no scenario in which data that is "complex" should be eligible for loss because the user navigates away from your app and your process gets terminated. – CommonsWare Oct 16 '11 at 18:52
2

Have you done it the following way?

Created a class that implements Application

public class MySuperApplication extends Application 
{ 
    public String SomeSetting; 
}

Defined which is your Application class in manifest (important!)

<application android:name=".MySuperApplication" android:icon="@drawable/icon"

and then accessed it in your activities

app = (MySuperApplication) getApplication();
app.SomeSetting = "Test";

This should work. It does in my case.

Drejc
  • 14,196
  • 16
  • 71
  • 106
  • Yep... as stated originally, I can do that (**before** I posted the question I also left the "." out of the android:name designation... a subtle but major mistake!) Still, I cannot make THISAPP a final variable (getApplication() appears to be invalid before onCreate() is called) and I'm not seeing why that should be. – MartyMacGyver Oct 16 '11 at 17:29
2

AFAIK, You cannot declare a empty final variable and initialize it onCreate. In java it is initialized during declaring the variable or in the constructor.

THISAPP cannot be initialized to Application object during declaration because the Application doesn't exist during compile time. Because onCreate is not a constructor, you cannot declare a empty final variable and initialize it in onCreate.

500865
  • 6,920
  • 7
  • 44
  • 87
  • I figured that out - it was quickly clear that initializing anywhere other than the definition and the constructor was not going to work (since normally I only do that in the definition it'd been a while since I bumped into it... and since you can't override Activity constructors it was clear that the only place I can do it was the one place it wouldn't work.) – MartyMacGyver Oct 16 '11 at 18:03
2

Instance variables are created when the object (in this case an Activity object) is constructed. For getApplication() to produce a valid value then the Activity object would have to have been provided enough context (little 'c') at instantiation for this to occur.

The source for getApplication() in Activity.java is just this:

/** Return the application that owns this activity. */
public final Application getApplication() {
    return mApplication;
}

Here is where it is set:

final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
        Application application, Intent intent, ActivityInfo info, CharSequence title, 
        Activity parent, String id, Object lastNonConfigurationInstance,
        Configuration config) {
    // other code
    mApplication = application;
    // other code
}

Since mApplication is not set until after attach is called, it won't be available to getApplication() on instantiation of your Activity.

goto10
  • 4,370
  • 1
  • 22
  • 33
  • Ah... that's interesting and more detailed than what I've found elsewhere. At what point in the process does that attach() actually happen? – MartyMacGyver Oct 16 '11 at 19:16