Is there a way to get the current Context instance inside a static method?
I'm looking for that way because I hate saving the 'Context' instance each time it changes.
Is there a way to get the current Context instance inside a static method?
I'm looking for that way because I hate saving the 'Context' instance each time it changes.
No, I don't think there is. Unfortunately, you're stuck calling getApplicationContext() from Activity or one of the other subclasses of Context. Also, this question is somewhat related.
Kotlin way:
Manifest:
<application android:name="MyApplication">
</application>
MyApplication.kt
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: MyApplication
private set
}
}
You can then access the property via MyApplication.instance
open class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: MyApp
fun getContext(): Context? {
return mInstance.applicationContext
}
}
}
and get Context like
MyApp.mInstance
or
MyApp.getContext()
in Kotlin, putting Context/App Context in companion object still produce warning Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
or if you use something like this:
companion object {
lateinit var instance: MyApp
}
It's simply fooling the lint to not discover the memory leak, the App instance still can produce memory leak, since Application class and its descendant is a Context.
Alternatively, you can use functional interface or Functional properties to help you get your app context.
Simply create an object class:
object CoreHelper {
lateinit var contextGetter: () -> Context
}
or you could use it more safely using nullable type:
object CoreHelper {
var contextGetter: (() -> Context)? = null
}
and in your App class add this line:
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
CoreHelper.contextGetter = {
this
}
}
}
and in your manifest declare the app name to . MyApp
<application
android:name=".MyApp"
When you wanna get the context simply call:
CoreHelper.contextGetter()
// or if you use the nullable version
CoreHelper.contextGetter?.invoke()
Hope it will help.
Rohit's answer seems correct. However, be aware that AndroidStudio's "Instant Run" depends on not having static Context attributes in your code, as far as I know.
Today the right way to have context is to use dependency injection.
For instance, one can use Hilt to inject context at any place it is needed. Let's say one needs context in some database manager, then this can be resolved in the following way:
Add Hilt in Gradle:
implementation "com.google.dagger:hilt-android:2.35"
kapt "com.google.dagger:hilt-android-compiler:2.35"
Define Application class with @HiltAndroidApp annotation (let it inject the database manager for example):
@HiltAndroidApp
class MyApplication : Application() {
@Inject
lateinit var dbManager: DBManager
override fun onCreate() {
super.onCreate()
dbManager.initDB()
}
}
Define Database manager (let it be @Singleton for example as well):
@Singleton
class DBManager @Inject constructor(
@ApplicationContext private val context: Context
) {
fun initDB() {
// context is avaiable
databaseInit(context)
}
}
And that's it. The DBManager can access context in the right way without memory leaks.
Another alternative to get context without subclassing the Application object and without using hidden classes would be to use a ContentProvider. Once the onCreate method is called, the context should be available. You can do something like this in 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
}
}
Anywhere you need the context, you can call ContextContentProvider.applicationContext() method
Make sure to use a different authority in the AndroidManifest.xml if you already have another content provider and the content provider is not exported.
<application>
<provider
android:name=".ContextContentProvider"
android:authorities="${applicationId}.ContextContentProvider"
android:enabled="true"
android:exported="false" />
</application>