Static way to get 'Context' in Android?

Viewed 800359

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.

21 Answers

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

Kotlin

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>
Related