是否有一种方法来获得静态方法内的当前上下文实例?

我正在寻找这种方式,因为我讨厌保存'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>

其他回答

Assuming we're talking about getting the Application Context, I implemented it as suggested by @Rohit Ghatol extending Application. What happened then, it's that there's no guarantee that the context retrieved in such a way will always be non-null. At the time you need it, it's usually because you want to initialize an helper, or get a resource, that you cannot delay in time; handling the null case will not help you. So I understood I was basically fighting against the Android architecture, as stated in the docs

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

并由黛安·哈克伯恩解释

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

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

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

所以我所做的是摆脱扩展Application,并将上下文直接传递给单例helper的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:

Helper.getInstance(myCtx).doSomething();

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


感兴趣的人可以在fwd博客上阅读更详细的版本

不,我想没有。不幸的是,您只能从Activity或Context的其他子类调用getApplicationContext()。而且,这个问题有点相关。

根据这个源代码,您可以通过扩展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 Service
        startActivity(this, SomeRealActivity.class);

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

JavaDoc for ContextWrapper

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

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

在Gradle中添加刀柄:

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

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

@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可以以正确的方式访问上下文,没有内存泄漏。

这样做:

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

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