关于android:到处使用应用程序上下文?

Using Application context everywhere?

在Android应用中,以下方法有什么问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

并将其传递到任何地方(例如SQLiteOpenHelper),其中需要上下文(当然不泄漏)?


这种方法存在一些潜在的问题,但在很多情况下(例如你的例子)它会很好用。

特别是在处理需要ContextGUI的任何事情时,你应该小心。例如,如果将应用程序Context传递给LayoutInflater,则会出现异常。一般来说,您的方法非常好:最好在Activity中使用Activity's Context,并在超出Activity范围的上下文时使用Application Context以避免内存泄漏。

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


根据我的经验,这种方法不应该是必要的。如果您需要任何上下文,通常可以通过调用View.getContext()并使用在此获得的Context来获取它,您可以调用Context.getApplicationContext()来获取Application上下文。如果您尝试从Activity获取Application上下文,则可以始终调用Activity.getApplication(),该应该能够作为调用SQLiteOpenHelper()所需的Context传递。

总的来说,对于这种情况,您的方法似乎没有问题,但在处理Context时,请确保您没有像官方Google Android开发人员博客中所描述的那样泄漏内存。


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
    }
}

我们运行代码:

1
2
3
4
$ javac A.java
$ java A
x:X@a63599 y:Y@9036e
x:null y:null

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

这可以修复吗?是,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
    }
}

并且此代码没有显示任何异常:

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

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

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

UPDATE

另一个令人费解的例子,其中初始化的静态字段碰巧是null

Main.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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());
    }
}

你得到:

1
2
3
4
5
$ javac Main.java
$ java Main
first: FIRST second: SECOND
first: <FIRST> second: <SECOND>
first: nullFIRSTnull second: nullSECONDnull

注意,你不能将静态变量声明移到一行上面,代码不会编译。


申请类别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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中声明应用程序:

1
2
3
<application android:name=".MyApplication"
    ...
/>

用法:

1
MyApplication.getAppContext()


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

根据我的理解,我猜它更好的方法来调用2中的任何一个
Context.getApplicationContext()Activity.getApplication()


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

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

我个人认为文档说错了通常不需要子类Application。我认为情况恰恰相反:你应该总是将Application子类化。


我将使用Application Context在构造函数中获取系统服务。这简化了测试和从构图中获益

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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会使用默认构造函数。


我使用相同的方法,我建议更好地编写单例:

1
2
3
4
5
6
7
8
9
10
11
12
public static MyApp getInstance() {

    if (instance == null) {
        synchronized (MyApp.class) {
            if (instance == null) {
                instance = new MyApp ();
            }
        }
    }

    return instance;
}

但是我没有到处使用,我使用getContext()getApplicationContext()我可以做到的!


我喜欢它,但我会建议一个单身人士:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
    }
}