在Android中获取“上下文”’Context’ 的静态方法?

Static way to get 'Context' in Android?

有没有办法在静态方法中获取当前的Context实例?

我正在寻找这种方法,因为我讨厌每次"上下文"实例更改时都保存它。


这样做:

在Android清单文件中,声明以下内容。

1
</application>

然后写下课程:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyApplication extends Application {

    private static Context context;

    public void onCreate() {
        super.onCreate();
        MyApplication.context = getApplicationContext();
    }

    public static Context getAppContext() {
        return MyApplication.context;
    }
}

现在,所有地方都调用MyApplication.getAppContext()静态获取应用程序上下文。


大多数应用程序需要一种方便的方法来获取应用程序上下文,它们创建自己的类来扩展android.app.Application

指南

您可以通过在项目中创建一个类来实现这一点,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import android.app.Application;
import android.content.Context;

public class App extends Application {

    private static Application sApplication;

    public static Application getApplication() {
        return sApplication;
    }

    public static Context getContext() {
        return getApplication().getApplicationContext();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sApplication = this;
    }
}

然后,在androidmanifest中,您应该在androidmanifest.xml的标记中指定类的名称:

1
2
3
4
5
<application
    ...
    android:name="com.example.App">
    ...
</application>

然后,可以使用以下方法检索任何静态方法中的应用程序上下文:

1
2
3
public static void someMethod() {
    Context context = App.getContext();
}

警告

在将上述内容添加到项目中之前,您应该考虑文档中的内容:

There is normally no need to subclass Application. In most situation,
static singletons can provide the same functionality in a more modular
way. If your singleton needs a global context (for example to register
broadcast receivers), the function to retrieve it can be given a
Context which internally uses Context.getApplicationContext() when
first constructing the singleton.

反射

还有另一种使用反射获取应用程序上下文的方法。在Android中,反射经常被忽视,我个人认为这不应该用于生产。

要检索应用程序上下文,我们必须对自API 1以来可用的隐藏类(ActivityThread)调用一个方法:

1
2
3
4
public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.ActivityThread")
            .getMethod("currentApplication").invoke(null, (Object[]) null);
}

还有一个隐藏类(AppGlobals),它提供了一种以静态方式获取应用程序上下文的方法。它使用ActivityThread获取上下文,因此以下方法与上面发布的方法没有区别:

1
2
3
4
public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.AppGlobals")
            .getMethod("getInitialApplication").invoke(null, (Object[]) null);
}

快乐编码!


不,我想没有。不幸的是,您无法从ActivityContext的其他子类中调用getApplicationContext()。而且,这个问题有些关联。


假设我们在讨论获取应用程序上下文,我按照@rohitghatol扩展应用程序的建议实现了它。然后发生的事情是,无法保证以这种方式检索的上下文总是非空的。在您需要它的时候,通常是因为您想要初始化一个助手或获取一个资源,所以您不能延迟;处理空的情况对您没有帮助。所以我明白我基本上是在和android体系结构作斗争,如docs所述。

Note: There is normally no need to subclass Application. In most situations, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), include Context.getApplicationContext() as a Context argument when invoking your singleton's getInstance() method.

由黛安·哈克伯恩解释

The only reason Application exists as something you can derive from is because during the pre-1.0 development one of our application developers was continually bugging me about needing to have a top-level application object they can derive from so they could have a more"normal" to them application model, and I eventually gave in.
I will forever regret giving in on that one. :)

她还建议解决这个问题:

If what you want is some global state that can be shared across different parts of your app, use a singleton. [...] And this leads more naturally to how you should be managing these things -- initializing them on demand.

因此,我所做的就是去掉扩展应用程序,并将上下文直接传递给singleton helper的getInstance(),同时在私有构造函数中保存对应用程序上下文的引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static MyHelper instance;
private final Context mContext;    

private MyHelper(@NonNull Context context) {
    mContext = context.getApplicationContext();
}

public static MyHelper getInstance(@NonNull Context context) {
    synchronized(MyHelper.class) {
        if (instance == null) {
            instance = new MyHelper(context);
        }
        return instance;
    }
}

然后调用方将本地上下文传递给帮助程序:

1
Helper.getInstance(myCtx).doSomething();

因此,要正确回答这个问题:有一些方法可以静态地访问应用程序上下文,但它们都不应该被鼓励,您应该更喜欢将本地上下文传递给singleton的getInstance()。

对于任何感兴趣的人,您可以在fwd blog上阅读更详细的版本。


下面是一种从UI线程中的任何位置获取应用程序(上下文)的未经记录的方法。它依赖于隐藏的静态方法ActivityThread.currentApplication()。它至少应该在Android 4.x上工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try {
    final Class<?> activityThreadClass =
            Class.forName("android.app.ActivityThread");
    final Method method = activityThreadClass.getMethod("currentApplication");
    return (Application) method.invoke(null, (Object[]) null);
} catch (final ClassNotFoundException e) {
    // handle exception
} catch (final NoSuchMethodException e) {
    // handle exception
} catch (final IllegalArgumentException e) {
    // handle exception
} catch (final IllegalAccessException e) {
    // handle exception
} catch (final InvocationTargetException e) {
    // handle exception
}

请注意,此方法可能返回空值,例如,当您在UI线程外部调用方法时,或者应用程序未绑定到线程时。

如果可以更改应用程序代码,最好使用@rohitghatol的解决方案。


这取决于您使用上下文的目的。我至少可以想到这种方法的一个缺点:

如果您试图用AlertDialog.Builder创建一个AlertDialog,那么Application上下文将不起作用。我相信你需要现有的背景资料。


如果您愿意使用roboguice,那么您可以将上下文注入您想要的任何类中。下面是一个如何使用roboguice 2.0(本文撰写时是beta 4)的小示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import android.content.Context;
import android.os.Build;
import roboguice.inject.ContextSingleton;

import javax.inject.Inject;

@ContextSingleton
public class DataManager {
    @Inject
    public DataManager(Context context) {
            Properties properties = new Properties();
            properties.load(context.getResources().getAssets().open("data.properties"));
        } catch (IOException e) {
        }
    }
}

我曾经用过这个:

1
2
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();

这是我在获取系统服务和工作时使用的有效上下文。

但是,我只在框架/基础修改中使用它,并没有在Android应用程序中尝试它。

您必须知道的警告:在此上下文中注册广播接收器时,它将不起作用,您将得到:

java.lang.SecurityException: Given caller package android is not running in process ProcessRecord


科特林

1
2
3
4
5
6
7
8
9
10
11
12
13
open class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        mInstance = this
    }

    companion object {
        lateinit var mInstance: MyApp
        fun getContext(): Context? {
            return mInstance.applicationContext
        }
    }
}

让上下文像

1
MyApp.mInstance

1
MyApp.getContext()

根据这个源代码,您可以通过扩展ContextWrapper来获取自己的上下文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SomeClass extends ContextWrapper {

    public SomeClass(Context base) {
      super(base);
    }

    public void someMethod() {
        // notice how I can use"this" for Context
        // this works because this class has it's own Context just like an Activity or Service
        startActivity(this, SomeRealActivity.class);

        //would require context too
        File cacheDir = getCacheDir();
    }
}

用于ContextWrapper的JavaDoc

Proxying implementation of Context that simply delegates all of its calls to another Context. Can be subclassed to modify behavior without changing the original Context.


您可以使用以下内容:

1
MainActivity.this.getApplicationContext();

mainactivity.java:主要活动.java

1
2
3
4
5
6
7
8
...
public class MainActivity ... {
    static MainActivity ma;
...
    public void onCreate(Bundle b) {
         super...
         ma=this;
         ...

任何其他类别:

1
2
3
public ...
    public ANY_METHOD... {
         Context c = MainActivity.ma.getApplicationContext();


我想你需要一具尸体来做getAppContext()法:

1
2
public static Context getAppContext()
   return MyApplication.context;

Kotlin之道:

显示:

1
</application>

我的应用.kt

1
2
3
4
5
6
7
8
9
10
11
12
class MyApplication: Application() {

    override fun onCreate() {
        super.onCreate()
        instance = this
    }

    companion object {
        lateinit var instance: MyApplication
            private set
    }
}

然后您可以通过myapplication.instance访问该属性。


如果不想修改清单文件,可以在初始活动的静态变量中手动存储上下文:

1
2
3
4
5
6
7
8
9
10
11
public class App {
    private static Context context;

    public static void setContext(Context cntxt) {
        context = cntxt;
    }

    public static Context getContext() {
        return context;
    }
}

并在活动开始时设置上下文:

1
2
3
4
5
6
7
8
9
10
11
12
// MainActivity

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Set Context
    App.setContext(getApplicationContext());

    // Other stuff
}

注意:和所有其他答案一样,这是一个潜在的内存泄漏。


我使用了一个单件设计模式的变体来帮助我完成这个任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import android.app.Activity;
import android.content.Context;

public class ApplicationContextSingleton {
    private static Activity gContext;

    public static void setContext( Activity activity) {
        gContext = activity;
    }

    public static Activity getActivity() {
        return gContext;
    }

    public static Context getContext() {
        return gContext;
    }
}

然后我在活动中调用ApplicationContextSingleton.setContext( this );,oncreate()和ondestory()中调用ApplicationContextSingleton.setContext( null );


如果出于某种原因,您希望在任何类中包含应用程序上下文,而不仅仅是那些扩展应用程序/活动的类,可能是对于某些工厂类或助手类。您可以将以下单例添加到您的应用程序中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class GlobalAppContextSingleton {
    private static GlobalAppContextSingleton mInstance;
    private Context context;

    public static GlobalAppContextSingleton getInstance() {
        if (mInstance == null) mInstance = getSync();
        return mInstance;
    }

    private static synchronized GlobalAppContextSingleton getSync() {
        if (mInstance == null) mInstance =
                new GlobalAppContextSingleton();
        return mInstance;
    }

    public void initialize(Context context) {
        this.context = context;
    }

    public Context getApplicationContext() {
        return context;
    }
}

然后在应用程序类的onCreate中初始化它

1
GlobalAppContextSingleton.getInstance().initialize(this);

打电话到任何地方都可以使用

1
GlobalAppContextSingleton.getInstance().getApplicationContext()

但是,除了应用程序上下文之外,我不推荐这种方法。因为它会导致内存泄漏。


我刚刚发布了一个基于jquery的Android框架,叫做vapor api,旨在使应用程序开发更简单。

中央EDCOX1×10×Futd类维护一个EDCOX1×11(链接到Ethan Nicholas的关于这个惊人的Java博客文章)到当前EDCOX1 1上下文,您可以通过调用来检索:

1
$.act()

WeakReference在不阻止垃圾回收回收回收原始对象的情况下维护引用,因此不应该出现内存泄漏问题。

当然,不利的一面是,您有可能面临$.act()可能返回空值的风险。不过,我还没有遇到这种情况,所以这可能只是一个最小的风险,值得一提。

如果不使用VaporActivity作为Activity类,也可以手动设置上下文:

1
$.act(Activity);

而且,许多vapor api框架都固有地使用这个存储上下文,这可能意味着如果您决定使用这个框架,就不需要自己存储它。有关更多信息和示例,请访问网站。

希望有帮助:)


所以我修改了接受的答案,因为它导致了内存泄漏,这就是我想到的……

AndroidManifest.xml

1
2
3
...

    </application>

MyApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
public class MyBakingAppContext extends Application {
    private static Object mContext;

    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
    }

    public static Context getAppContext() {
        return (Context)mContext;
    }
}

我实际做的是,为一个对象分配一个上下文,并将该对象作为上下文返回(将其转换为上下文)。希望能有所帮助。


罗希特的回答似乎是正确的。但是,请注意,据我所知,AndroidStudio的"即时运行"依赖于代码中没有static Context属性。