在Android中获取“Context”的静态方法?

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

我正在寻找这种方式,因为我讨厌每次更改时保存'Context'实例。

807802 次浏览

不,我不认为有。不幸的是,您只能从ActivityContext的其他子类中调用getApplicationContext()。此外,这个问题有些相关。

这样做:

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

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

然后编写类:

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()静态获取您的应用程序上下文。

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

如果您尝试用AlertDialog.Builder创建AlertDialogApplication上下文将不起作用。我相信您需要当前Activity的上下文…

我认为你需要一个getAppContext()方法的主体:

public static Context getAppContext()return MyApplication.context;

如果你愿意使用RoboGuice,你可以将上下文注入到你想要的任何类中。这是一个如何使用RoboGuice 2.0(撰写本文时为beta 4)执行此操作的小示例

import android.content.Context;import android.os.Build;import roboguice.inject.ContextSingleton;
import javax.inject.Inject;
@ContextSingletonpublic class DataManager {@Injectpublic DataManager(Context context) {Properties properties = new Properties();properties.load(context.getResources().getAssets().open("data.properties"));} catch (IOException e) {}}}

这是从UI线程中的任何位置获取申请方式(这是一个上下文)的无证方法。它依赖于隐藏的静态方法ActivityThread.currentApplication()。它至少应该在Android 4. x上工作。

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}

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

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

我刚刚发布了一个受jQuery启发的Android框架,名为蒸汽API,旨在使应用程序开发更简单。

中央#0外观类维护了一个#1(链接到Ethan NicholasJava博客文章)到当前Activity上下文,您可以通过调用来检索:

$.act()

WeakReference维护引用而不阻止垃圾回收机制回收原始对象,因此您不应该遇到内存泄漏的问题。

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

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

$.act(Activity);

此外,蒸汽API框架的大部分固有地使用此存储上下文,这可能意味着如果您决定使用该框架,您根本不需要自己存储它。查看网站以获取更多信息和示例。

希望能有所帮助:)

您可以使用以下内容:

MainActivity.this.getApplicationContext();

MainActivity.java:

...public class MainActivity ... {static MainActivity ma;...public void onCreate(Bundle b) {super...ma=this;...

任何其他类:

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

我在某个时候用过这个:

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

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

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

您必须知道的警告:使用此上下文注册广播接收器时,它将无法工作,您将获得:

java.lang.SecurityException:给定调用者包android不是运行在进程ProcessRecords

我使用Singleton设计模式的变体来帮助我做到这一点。

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

然后我在我的activity.on创建()中调用ApplicationContextSingleton.setContext( this );,在onDestroy()中调用ApplicationContextSingleton.setContext( null );

大多数需要方便方法来获取应用程序上下文的应用程序都会创建自己的扩展#0的类。

指南

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

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();}
@Overridepublic void onCreate() {super.onCreate();sApplication = this;}}

然后,在你的Android清单中,你应该在AndroidManifest.xml的标签中指定你的类的名称:

<application...android:name="com.example.App" >...</application>

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

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

警告

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

通常不需要对Application进行子类化。在大多数情况下,静态单例可以以更模块化的方式提供相同的功能如果您的单例需要全局上下文(例如注册)广播接收器),检索它的功能可以给出一个在内部使用Context.getApplication ationContext()的上下文首先构建单例。


反思

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

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

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

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

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

编码愉快!

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

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中初始化它

GlobalAppContextSingleton.getInstance().initialize(this);

使用它在任何地方通过调用

GlobalAppContextSingleton.getInstance().getApplicationContext()

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

假设我们谈论的是获取Application Context,我按照@Roshot Ghatol扩展Application的建议实现了它。然后发生了什么,就是不能保证以这种方式检索到的Context总是非空。在你需要的时候,通常是因为你要初始化一个helper,或者获取一个资源,不能及时延迟;处理null情况对你没有帮助。所以我明白我基本上是在与Android架构作斗争,正如文档中所述。

注意:通常不需要子类Application。在大多数情况下,静态单例可以以更模块化的方式提供相同的功能。如果您的单例需要全局上下文(例如注册广播接收器),请在调用单例的getInstance()方法时Context.getApplication ationContext()作为Context参数。

黛安·哈克伯恩解释

应用程序作为你可以派生的东西存在的唯一原因是,在1.0之前的开发过程中,我们的一个应用程序开发人员不断地打扰我,说需要有一个他们可以派生的顶级应用程序对象,这样他们就可以有一个更“正常”的应用程序模型,我最终屈服了。我将永远后悔放弃这一点:)

她还提出了解决这个问题的方法:

如果你想要的是一些可以在应用程序的不同部分之间共享的全局状态,请使用单例。[…]这更自然地导致了你应该如何管理这些东西——根据需要初始化它们。

所以我所做的是摆脱扩展Application,并将上下文直接传递给单例助手的getInstance(),同时在私有构造函数中保存对应用程序上下文的引用:

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

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

Helper.getInstance(myCtx).doSomething();

因此,要正确回答这个问题:有静态访问Application Context的方法,但都应该被阻止,您应该更喜欢将本地上下文传递给单例的getInstance()。


对于任何感兴趣的人,您可以在fwd博客中阅读更详细的版本

根据这个来源,您可以通过扩展ContextWrapper获得自己的Context

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 ServicestartActivity(this, SomeRealActivity.class);
//would require context tooFile cacheDir = getCacheDir();}}

用于ContextWrapper的JavaDoc

Context的代理实现,简单地将其所有调用委托给另一个Context。可以子类化以修改行为而不更改原始Context。

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

public class App {private static Context context;
public static void setContext(Context cntxt) {context = cntxt;}
public static Context getContext() {return context;}}

并在您的活动(或活动)开始时设置上下文:

// MainActivity
@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
// Set ContextApp.setContext(getApplicationContext());
// Other stuff}

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

静态编程语言

清单:

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

MyApplication.kt

class MyApplication: Application() {
override fun onCreate() {super.onCreate()instance = this}
companion object {lateinit var instance: MyApplicationprivate set}}

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

罗希特的答案似乎是正确的。然而,请注意,据我所知,Android Studio的“即时运行”取决于您的代码中没有static Context属性。

静态编程语言

open class MyApp : Application() {override fun onCreate() {super.onCreate()mInstance = this}
companion object {lateinit var mInstance: MyAppfun getContext(): Context? {return mInstance.applicationContext}}}

并获取Context如

MyApp.mInstance

MyApp.getContext()

在静态编程语言中,将Context/App Context放在同伴对象中仍然会产生警告Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)

或者如果你使用这样的东西:

    companion object {lateinit var instance: MyApp}

它只是欺骗lint不发现内存泄漏,App实例仍然可以产生内存泄漏,因为Application类及其后代是Context。

或者,您可以使用函数式接口或函数式属性来帮助您获取应用程序上下文。

简单地创建一个对象类:

object CoreHelper {lateinit var contextGetter: () -> Context}

或者您可以使用可为空的类型更安全地使用它:

object CoreHelper {var contextGetter: (() -> Context)? = null}

并在您的App类中添加以下行:

class MyApp: Application() {
override fun onCreate() {super.onCreate()CoreHelper.contextGetter = {this}}}

并在您的清单中声明应用程序名称为. MyApp

<applicationandroid:name=".MyApp"

当您想要获取上下文时,只需调用:

CoreHelper.contextGetter()
// or if you use the nullable versionCoreHelper.contextGetter?.invoke()

希望能有所帮助。

今天,拥有context的正确方法是使用依赖注入。例如,可以使用Hilt在任何需要的地方注入上下文。假设一个人在某个数据库管理器中需要context,那么这可以通过以下方式解决:

在Gradle中添加Hilt:

implementation "com.google.dagger:hilt-android:2.35"kapt "com.google.dagger:hilt-android-compiler:2.35"

使用@HiltAndroidApp注释定义Application类(例如让它注入数据库管理器):

@HiltAndroidAppclass MyApplication : Application() {
@Injectlateinit var dbManager: DBManager
override fun onCreate() {super.onCreate()dbManager.initDB()}}

定义数据库管理器(例如也让它为@Singleton):

@Singletonclass DBManager @Inject constructor(@ApplicationContext private val context: Context) {
fun initDB() {// context is avaiabledatabaseInit(context)}}

就是这样。DBManager可以以正确的方式访问上下文,而不会出现内存泄漏。

在不对Application对象进行子类化和不使用隐藏类的情况下获得context的另一种选择是使用ContentProvider。一旦调用onCreate方法,上下文应该是可用的。您可以在静态编程语言中做这样的事情

class ContextContentProvider : ContentProvider() {override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?) = 0
override fun getType(uri: Uri): String? = null
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
override fun onCreate(): Boolean {applicationContext = contextreturn true}
override fun query(uri: Uri, projection: Array<String>?, selection: String?,selectionArgs: Array<String>?, sortOrder: String?): Cursor? = null
override fun update(uri: Uri, values: ContentValues?, selection: String?,selectionArgs: Array<String>?) = 0
companion object {private var applicationContext: Context? = null
@JvmStaticfun applicationContext() = applicationContext}}

任何需要上下文的地方,都可以调用ContextContentProvider.applicationContext()方法

如果您已经有另一个内容提供程序并且未导出内容提供程序,请确保在AndroidManifest.xml中使用不同的权限。

<application><providerandroid:name=".ContextContentProvider"android:authorities="${applicationId}.ContextContentProvider"android:enabled="true"android:exported="false" /></application>