Using Application context everywhere?

后端 未结 9 831
难免孤独
难免孤独 2020-11-22 01:52

In an Android app, is there anything wrong with the following approach:

public class MyApp extends android.app.Application {

    private static MyApp instan         


        
相关标签:
9条回答
  • 2020-11-22 01:55

    Some people have asked: how can the singleton return a null pointer? I'm answering that question. (I cannot answer in a comment because I need to post code.)

    It may return null in between two events: (1) the class is loaded, and (2) the object of this class is created. Here's an example:

    class X {
        static X xinstance;
        static Y yinstance = Y.yinstance;
        X() {xinstance=this;}
    }
    class Y {
        static X xinstance = X.xinstance;
        static Y yinstance;
        Y() {yinstance=this;}
    }
    
    public class A {
        public static void main(String[] p) {
        X x = new X();
        Y y = new Y();
        System.out.println("x:"+X.xinstance+" y:"+Y.yinstance);
        System.out.println("x:"+Y.xinstance+" y:"+X.yinstance);
        }
    }
    

    Let's run the code:

    $ javac A.java 
    $ java A
    x:X@a63599 y:Y@9036e
    x:null y:null
    

    The second line shows that Y.xinstance and X.yinstance are null; they are null because the variables X.xinstance ans Y.yinstance were read when they were null.

    Can this be fixed? Yes,

    class X {
        static Y y = Y.getInstance();
        static X theinstance;
        static X getInstance() {if(theinstance==null) {theinstance = new X();} return theinstance;}
    }
    class Y {
        static X x = X.getInstance();
        static Y theinstance;
        static Y getInstance() {if(theinstance==null) {theinstance = new Y();} return theinstance;}
    }
    
    public class A {
        public static void main(String[] p) {
        System.out.println("x:"+X.getInstance()+" y:"+Y.getInstance());
        System.out.println("x:"+Y.x+" y:"+X.y);
        }
    }
    

    and this code shows no anomaly:

    $ javac A.java 
    $ java A
    x:X@1c059f6 y:Y@152506e
    x:X@1c059f6 y:Y@152506e
    

    BUT this is not an option for the Android Application object: the programmer does not control the time when it is created.

    Once again: the difference between the first example and the second one is that the second example creates an instance if the static pointer is null. But a programmer cannot create the Android application object before the system decides to do it.

    UPDATE

    One more puzzling example where initialized static fields happen to be null.

    Main.java:

    enum MyEnum {
        FIRST,SECOND;
        private static String prefix="<", suffix=">";
        String myName;
        MyEnum() {
            myName = makeMyName();
        }
        String makeMyName() {
            return prefix + name() + suffix;
        }
        String getMyName() {
            return myName;
        }
    }
    public class Main {
        public static void main(String args[]) {
            System.out.println("first: "+MyEnum.FIRST+" second: "+MyEnum.SECOND);
            System.out.println("first: "+MyEnum.FIRST.makeMyName()+" second: "+MyEnum.SECOND.makeMyName());
            System.out.println("first: "+MyEnum.FIRST.getMyName()+" second: "+MyEnum.SECOND.getMyName());
        }
    }
    

    And you get:

    $ javac Main.java
    $ java Main
    first: FIRST second: SECOND
    first: <FIRST> second: <SECOND>
    first: nullFIRSTnull second: nullSECONDnull
    

    Note that you cannot move the static variable declaration one line upper, the code will not compile.

    0 讨论(0)
  • 2020-11-22 01:55

    I would use Application Context to get a System Service in the constructor. This eases testing & benefits from composition

    public class MyActivity extends Activity {
    
        private final NotificationManager notificationManager;
    
        public MyActivity() {
           this(MyApp.getContext().getSystemService(NOTIFICATION_SERVICE));
        }
    
        public MyActivity(NotificationManager notificationManager) {
           this.notificationManager = notificationManager;
        }
    
        // onCreate etc
    
    }
    

    Test class would then use the overloaded constructor.

    Android would use the default constructor.

    0 讨论(0)
  • 2020-11-22 01:55

    I'm using the same approach, I suggest to write the singleton a little better:

    public static MyApp getInstance() {
    
        if (instance == null) {
            synchronized (MyApp.class) {
                if (instance == null) {
                    instance = new MyApp ();
                }
            }
        }
    
        return instance;
    }
    

    but I'm not using everywhere, I use getContext() and getApplicationContext() where I can do it!

    0 讨论(0)
  • 2020-11-22 01:59

    Application Class:

    import android.app.Application;
    import android.content.Context;
    
    public class MyApplication extends Application {
    
        private static Context mContext;
    
        public void onCreate() {
            super.onCreate();
            mContext = getApplicationContext();
        }
    
        public static Context getAppContext() {
            return mContext;
        }
    
    }
    

    Declare the Application in the AndroidManifest:

    <application android:name=".MyApplication"
        ...
    />
    

    Usage:

    MyApplication.getAppContext()
    
    0 讨论(0)
  • 2020-11-22 02:05

    In my experience this approach shouldn't be necessary. If you need the context for anything you can usually get it via a call to View.getContext() and using the Context obtained there you can call Context.getApplicationContext() to get the Application context. If you are trying to get the Application context this from an Activity you can always call Activity.getApplication() which should be able to be passed as the Context needed for a call to SQLiteOpenHelper().

    Overall there doesn't seem to be a problem with your approach for this situation, but when dealing with Context just make sure you are not leaking memory anywhere as described on the official Google Android Developers blog.

    0 讨论(0)
  • 2020-11-22 02:09

    It is a good approach. I use it myself as well. I would only suggest to override onCreate to set the singleton instead of using a constructor.

    And since you mentioned SQLiteOpenHelper: In onCreate () you can open the database as well.

    Personally I think the documentation got it wrong in saying that There is normally no need to subclass Application. I think the opposite is true: You should always subclass Application.

    0 讨论(0)
提交回复
热议问题