在各地使用应用程序

yan*_*nko 468 android android-context

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

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}
Run Code Online (Sandbox Code Playgroud)

并传递它到处(例如SQLiteOpenHelper)需要上下文(当然不泄漏)?

Ret*_*ier 405

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

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

此外,作为一种替代你的方式,你可以使用电话的快捷键getApplicationContext()上的Context对象(如活动)来获取应用程序上下文.

  • 谢谢你的回答.我想我会将此方法仅用于持久层(因为我不想与内容提供商一起使用).想知道设计SQLiteOpenHelper的动机是什么,期望提供Context而不是从Application本身获取它.PS你的书很棒! (22认同)
  • 使用'LayoutInflator`的应用程序上下文对我来说很有用.必须在过去三年内改变. (7认同)
  • @JacobPhillips使用没有活动上下文的LayoutInflator将错过该Activity的样式.所以它在某种意义上是有效的,但不是另一种. (5认同)

snc*_*tln 28

根据我的经验,这种方法不应该是必要的.如果你需要任何东西的上下文,你通常可以通过调用View.getContext()并使用在那里获得的Context来获取它,你可以调用Context.getApplicationContext()来获取Application上下文.如果你试图从一个Activity获取Appication上下文,你可以随时调用Activity.getApplication(),它应该能够作为调用SQLiteOpenHelper()所需的Context传递

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


184*_*615 13

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

它可能在两个事件之间返回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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我们运行代码:

$ javac A.java 
$ java A
x:X@a63599 y:Y@9036e
x:null y:null
Run Code Online (Sandbox Code Playgroud)

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

这可以修复吗?是,

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

$ javac A.java 
$ java A
x:X@1c059f6 y:Y@152506e
x:X@1c059f6 y:Y@152506e
Run Code Online (Sandbox Code Playgroud)

,这不是针对Android的选择Application对象:在创建时,程序员不控制时间.

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

UPDATE

还有一个令人费解的例子,其中初始化的静态字段恰好是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());
    }
}
Run Code Online (Sandbox Code Playgroud)

你得到:

$ javac Main.java
$ java Main
first: FIRST second: SECOND
first: <FIRST> second: <SECOND>
first: nullFIRSTnull second: nullSECONDnull
Run Code Online (Sandbox Code Playgroud)

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

  • 有用的例子; 很高兴知道有这样一个洞.我从中得到的是,在任何类的静态初始化期间,应该避免引用这样的静态变量. (3认同)

小智 9

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

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

  • 我没有静态的Context.getApplicationContext()方法.我错过了什么吗? (25认同)
  • 什么时候应该返回null? (13认同)
  • 如果您在app之前加载的contentprovider中调用SQLiteOpenHelper,则可能就是这种情况. (2认同)

toh*_*oha 9

申请类别:

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

}
Run Code Online (Sandbox Code Playgroud)

在AndroidManifest中声明应用程序:

<application android:name=".MyApplication"
    ...
/>
Run Code Online (Sandbox Code Playgroud)

用法:

MyApplication.getAppContext()
Run Code Online (Sandbox Code Playgroud)

  • 容易发生内存泄漏。你永远不应该这样做。 (2认同)
  • @Dragas,不,它不容易泄漏。您可以随时这样做。 (2认同)

Mar*_*tin 5

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

并且因为您提到SQLiteOpenHelperonCreate ()您也可以在其中打开数据库。

就我个人而言,我认为文档错误地说通常不需要子类化 Application。我认为恰恰相反:您应该始终子类化 Application。