1064

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

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

4

21 回答 21

1381

做这个:

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

于 2011-02-25T06:37:00.513 回答
112

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

指导

您可以通过首先在项目中创建一个类来完成此操作,如下所示:

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 的标签中指定您的类的名称:

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

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

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

警告

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

通常不需要子类化 Application。在大多数情况下,静态单例可以以更模块化的方式提供相同的功能。如果您的单例需要全局上下文(例如注册广播接收器),则可以为检索它的函数提供一个 Context,该上下文在首次构造单例时在内部使用 Context.getApplicationContext()。


反射

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

要检索应用程序上下文,我们必须调用从 API 1 开始可用的隐藏类 ( ActivityThread ) 上的方法:

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

快乐编码!

于 2015-01-19T09:09:34.113 回答
66

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

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

并由Dianne Hackborn解释

应用程序作为您可以派生的东西而存在的唯一原因是,在 1.0 之前的开发过程中,我们的一位应用程序开发人员一直在困扰我,他们需要一个可以派生的顶级应用程序对象,以便他们可以拥有一个更“正常”的应用程序对象。 “对他们的应用模型,我最终屈服了。我将永远后悔放弃那个。:)

她还建议解决这个问题:

如果您想要的是可以在应用程序的不同部分共享的全局状态,请使用单例。[...] 这更自然地导致您应该如何管理这些事情——按需初始化它们。

所以我所做的是摆脱扩展应用程序,并将上下文直接传递给单例助手的 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();

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


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

于 2016-08-16T05:36:06.690 回答
51

不,我认为没有。不幸的是,您被困在getApplicationContext()从. 另外,这个问题有点相关。ActivityContext

于 2010-01-05T00:46:00.533 回答
40

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

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 线程之外调用该方法时,或者应用程序未绑定到该线程时。

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

于 2012-09-19T13:34:04.167 回答
33

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

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

于 2011-08-12T01:07:38.100 回答
32

科特林方式

显现:

<application android:name="MyApplication">

</application>

我的应用程序.kt

class MyApplication: Application() {

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

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

然后,您可以通过以下方式访问该物业MyApplication.instance

于 2018-05-23T12:42:19.317 回答
12

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

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) {
        }
    }
}
于 2012-02-29T14:46:28.267 回答
12

科特林

open class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        mInstance = this
    }

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

并获得 Context 之类的

MyApp.mInstance

或者

MyApp.getContext()
于 2018-09-21T12:59:22.080 回答
9

我在某些时候使用过这个:

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

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

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

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

java.lang.SecurityException:给定调用程序包android未在进程ProcessRecord中运行

于 2014-05-08T10:22:50.943 回答
7

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

public class App {
    private static Context context;

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

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

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

// MainActivity

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

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

    // Other stuff
}

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

于 2018-03-14T21:02:05.340 回答
7

在 Kotlin 中,将 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


    <application
            android:name=".MyApp"

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

CoreHelper.contextGetter()

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

希望它会有所帮助。

于 2019-08-03T18:01:00.060 回答
5

您可以使用以下内容:

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();
于 2013-04-06T17:25:59.803 回答
5

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

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

Context 的代理实现,它只是将其所有调用委托给另一个 Context。可以子类化以修改行为而不更改原始上下文。

于 2016-12-13T08:29:11.067 回答
4

我认为您需要该getAppContext()方法的主体:

public static Context getAppContext()
   return MyApplication.context; 
于 2011-08-18T14:15:08.993 回答
3

我刚刚发布了一个基于 jQuery 的 Android 框架,名为Vapor API,旨在简化应用程序开发。

中央$外观类维护一个WeakReference(链接到由 Ethan Nicholas 撰写的关于此的很棒的 Java 博客文章)到当前Activity上下文,您可以通过调用来检索它:

$.act()

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

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

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

$.act(Activity);

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

我希望这会有所帮助:)

于 2013-02-26T10:50:21.870 回答
3

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

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()

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

于 2016-02-23T14:20:08.163 回答
2

我使用 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;
    }
}

然后我调用ApplicationContextSingleton.setContext( this );我的activity.onCreate ()onDestroy()ApplicationContextSingleton.setContext( null );

于 2014-12-30T19:20:54.503 回答
2

Rohit 的回答似乎是正确的。但是,请注意,据我所知,AndroidStudio 的“即时运行”取决于static Context您的代码中没有属性。

于 2018-07-12T13:04:46.080 回答
1

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

在 Gradle 中添加 Hilt:

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

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

@HiltAndroidApp
class MyApplication : Application() {

    @Inject
    lateinit var dbManager: DBManager

    override fun onCreate() {
        super.onCreate()
        dbManager.initDB()
    }
}

定义数据库管理器(@Singleton例如也可以):

@Singleton
class DBManager @Inject constructor(
    @ApplicationContext private val context: Context
) {

    fun initDB() {
        // context is avaiable
        databaseInit(context)
    }
}

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

于 2021-05-26T01:42:52.813 回答
0

context在不继承Application对象且不使用隐藏类的情况下获得的另一种替代方法是使用 ContentProvider。调用该onCreate方法后,上下文应该可用。你可以在 Kotlin 中做这样的事情

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 = context
        return 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

        @JvmStatic
        fun applicationContext() = applicationContext
    }
}

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

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

<application>
    <provider
        android:name=".ContextContentProvider"
        android:authorities="${applicationId}.ContextContentProvider"
        android:enabled="true"
        android:exported="false" />
</application>
于 2022-02-28T21:27:21.297 回答