【问题标题】:Using Application context everywhere?到处使用应用程序上下文?
【发布时间】:2010-11-02 11:47:55
【问题描述】:

在一个Android应用中,下面的方法有什么问题吗:

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

并在需要上下文的任何地方(例如 SQLiteOpenHelper)传递它(当然不会泄漏)?

【问题讨论】:

  • 只是为了详细说明实现此功能的其他人,您可以修改 AndroidManifest.xml 文件的 <application> 节点以包含以下属性定义:android:name="MyApp"。 MyApp 需要位于清单引用的同一包下。
  • 解决向 SQLiteOpenHelper 提供上下文问题的好方法!我已经实现了一个单例“SQLiteManager”并且被困在“我如何获得单例的上下文?”
  • 只是为了让您知道您正在通过其超级接口之一返回您的应用程序,因此如果您在 MyApp 中提供了其他方法,您将无法使用它们。你的 getContext() 应该有一个 MyApp 的返回类型,这样你就可以使用以后添加的方法,以及 ContextWrapper 和 Context 中的所有方法。
  • 另见goo.gl/uKcFn - 这是另一个与类似帖子相关的回复。最好在 onCreate 而不是 c'tor 中设置静态变量。
  • @ChuongPham 如果框架杀死了你的应用程序,将不会有任何访问空上下文的东西......

标签: android android-context


【解决方案1】:

这种方法存在一些潜在问题,尽管在很多情况下(例如您的示例)它会很好地工作。

在处理需要 ContextGUI 的任何事情时,您尤其应该小心。例如,如果您将应用程序上下文传递给 LayoutInflater,您将得到一个异常。一般来说,您的方法非常好:最好在 Activity 中使用 Activity's ContextApplication Context strong> 将超出 Activity 范围的上下文传递给 avoid memory leaks

此外,作为模式的替代,您可以使用在 Context 对象(例如 Activity)上调用 getApplicationContext() 的快捷方式来获取应用程序上下文。

【讨论】:

  • 感谢您的鼓舞人心的回答。我想我只会将这种方法用于持久层(因为我不想使用内容提供者)。想知道以期望提供上下文而不是从应用程序本身获取上下文的方式设计 SQLiteOpenHelper 背后的动机是什么。附言你的书很棒!
  • 使用LayoutInflator 的应用程序上下文对我有用。必须在过去三年内进行过更改。
  • @JacobPhillips 在没有活动上下文的情况下使用 LayoutInflator 将错过该活动的样式。所以它在某种意义上是有效的,但不是另一种意义上的。
  • @MarkCarter 你的意思是使用 Application Context 会错过 Activity 的样式吗?
  • @JacobPhillips 是的,Application Context 不能有样式,因为每个 Activity 的样式都可能不同。
【解决方案2】:

根据我的经验,这种方法应该没有必要。如果您需要任何内容​​的上下文,您通常可以通过调用View.getContext() 来获取它,并使用在那里获得的Context,您可以调用Context.getApplicationContext() 来获取Application 上下文。如果您试图从Activity 获取Application 上下文,您可以随时调用Activity.getApplication(),它应该能够作为调用Context 所需的Context 传递给SQLiteOpenHelper()

总体而言,您在这种情况下的方法似乎没有问题,但是在处理Context 时,请确保您没有按照官方Google Android Developers blog 的描述在任何地方泄漏内存。

【讨论】:

    【解决方案3】:

    有人问:单例如何返回空指针? 我正在回答这个问题。 (我无法在评论中回答,因为我需要发布代码。)

    它可能会在两个事件之间返回 null:(1)加载类,以及(2)创建该类的对象。这是一个例子:

    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);
        }
    }
    

    让我们运行代码:

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

    第二行显示Y.xinstanceX.yinstancenull;它们为空,因为变量 X.xinstanceY.yinstance 在它们为空时被读取。

    这可以解决吗?是的,

    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);
        }
    }
    

    并且这段代码没有显示异常:

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

    但是这不是 Android Application 对象的选项:程序员无法控制创建它的时间。

    再次重申:第一个示例和第二个示例的区别在于,如果静态指针为空,则第二个示例创建一个实例。但是程序员不能在系统决定之前创建 Android 应用程序对象。

    更新

    另一个令人费解的例子,初始化的静态字段恰好是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());
        }
    }
    

    你会得到:

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

    注意,静态变量声明不能上移一行,代码将无法编译。

    【讨论】:

    • 有用的例子;很高兴知道有这样一个洞。我从中得出的结论是,在任何类的静态初始化期间都应该避免引用这样的静态变量。
    【解决方案4】:

    应用类:

    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;
        }
    
    }
    

    在 AndroidManifest 中声明应用程序:

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

    用法:

    MyApplication.getAppContext()
    

    【讨论】:

    • 容易发生内存泄漏。你永远不应该这样做。
    • @Dragas,不,它不容易泄漏。您可以随时这样做。
    • @Dragas getApplicationContext() 实际上是之前泄露的,所以使用这个例子不会有问题。
    【解决方案5】:

    您正在尝试创建一个包装器来获取应用程序上下文,它可能会返回“null”指针。

    根据我的理解,我想它是更好的调用方法——这两种方法中的任何一种 Context.getApplicationContext()Activity.getApplication()

    【讨论】:

    • 什么时候返回null?
    • 我知道没有静态 Context.getApplicationContext() 方法。我错过了什么吗?
    • 我也在我的应用程序中实现了相同的方法,但是在 SQLiteOpenHelper 中调用时,它返回空指针。这种情况的任何答案。
    • 如果您在应用之前加载的 contentprovider 中调用 SQLiteOpenHelper 可能会出现这种情况。
    【解决方案6】:

    这是一个很好的方法。我自己也用它。我只建议覆盖onCreate 来设置单例而不是使用构造函数。

    既然你提到了SQLiteOpenHelper:在onCreate ()你也可以打开数据库。

    我个人认为文档中说通常不需要继承 Application 是错误的。我认为恰恰相反:你应该始终继承 Application。

    【讨论】:

      【解决方案7】:

      我将使用应用程序上下文在构造函数中获取系统服务。这简化了测试并从组合中受益

      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
      
      }
      

      然后测试类将使用重载的构造函数。

      Android 将使用默认构造函数。

      【讨论】:

        【解决方案8】:

        我喜欢它,但我建议改为单例:

        package com.mobidrone;
        
        import android.app.Application;
        import android.content.Context;
        
        public class ApplicationContext extends Application
        {
            private static ApplicationContext instance = null;
        
            private ApplicationContext()
            {
                instance = this;
            }
        
            public static Context getInstance()
            {
                if (null == instance)
                {
                    instance = new ApplicationContext();
                }
        
                return instance;
            }
        }
        

        【讨论】:

        • 扩展 android.app.application 已经保证了单例,所以这是不必要的
        • 如果你想从非活动类访问怎么办?
        • 你不应该自己new应用程序(单元测试可能例外)。操作系统会这样做。你也不应该有一个构造函数。这就是onCreate 的用途。
        • @Vincent:你能发布一些链接吗?最好是代码 - 我在这里问:stackoverflow.com/questions/19365797/…
        • @radzio 为什么我们不应该在构造函数中这样做?
        【解决方案9】:

        我用的是同样的方法,我建议把单例写好一点:

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

        但我不是到处都在使用,我可以使用getContext()getApplicationContext()

        【讨论】:

        • 所以,请写一条评论来解释为什么你对答案投了反对票,这样我才能理解。单例方法被广泛用于获取活动或视图主体之外的有效上下文...
        • 不需要,因为操作系统会确保应用程序只被实例化一次。如果有的话,我建议在 onCreate () 中设置 Singelton。
        • 一种延迟初始化单例的线程安全的好方法,但这里不是必需的。
        • 哇,就在我以为人们终于停止使用双重检查锁定的时候...cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
        猜你喜欢
        • 2015-08-31
        • 1970-01-01
        • 2016-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多