ChatGPT解决这个技术问题 Extra ChatGPT

Static way to get 'Context' in Android?

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.

Not saving Context is a good idea not just because it is inconvenient, but more because it can lead to huge memory leaks!
@VikramBodicherla Yes, but the answers below assume that we are talking about the application context. So, memory leaks are not an issue, but the user should only use these solutions where that is the correct context to use.
If you have to use a static way of getting Context, then there might be a better way to design the code.
Android documentation recommends passing the context to getters of singletons. developer.android.com/reference/android/app/Application.html
For preferring singletons and context passed with getInstance() over static context, please have a look, I tried to explain my reasoning here supported with working code: stackoverflow.com/a/38967293/4469112

P
Peter Mortensen

Do this:

In the Android Manifest file, declare the following.

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

</application>

Then write the class:

public class MyApplication extends Application {

    private static Context context;

    public void onCreate() {
        super.onCreate();
        MyApplication.context = getApplicationContext();
    }

    public static Context getAppContext() {
        return MyApplication.context;
    }
}

Now everywhere call MyApplication.getAppContext() to get your application context statically.


Is there any downside to this method? This seems like cheating. (A hack?)
The downside is that there is no guarantee that the non-static onCreate() will have been called before some static initialization code tries to fetch your Context object. That means your calling code will need to be ready to deal with null values which sort of defeats the whole point of this question.
Also maybe.. should we declare this static context variable as volatile?
@Tom This is not a case of a static data member being initially statically. In the given code, the static member is being initialized non-statically in onCreate(). Even statically initialized data is not good enough in this case because nothing insures that the static initialization of the given class will happen before it will be accessed during the static initialization of some other class.
@MelindaGreen According to the documentation for Application, onCreate() is called before any activity, service or receiver (excluding content providers) have been created. So wouldn't this solution be safe as long as you're not trying to access getAppContext() from a content provider?
J
Jared Rummler

The majority of apps that want a convenient method to get the application context create their own class which extends android.app.Application.

GUIDE

You can accomplish this by first creating a class in your project like the following:

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

Then, in your AndroidManifest you should specify the name of your class in the AndroidManifest.xml’s tag:

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

You can then retrieve the application context in any static method using the following:

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

WARNING

Before adding something like the above to your project you should consider what the documentation says:

There is normally no need to subclass Application. In most situation, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a Context which internally uses Context.getApplicationContext() when first constructing the singleton.

REFLECTION

There is also another way to get the application context using reflection. Reflection is often looked down upon in Android and I personally think this should not be used in production.

To retrieve the application context we must invoke a method on a hidden class (ActivityThread) which has been available since API 1:

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

There is one more hidden class (AppGlobals) which provides a way to get the application context in a static way. It gets the context using ActivityThread so there really is no difference between the following method and the one posted above:

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

Happy coding!


Yep! Love the last approach! Especially because I've internal/hidden APIs showing on Android Studio, so I don't even need to use Reflection, which seems safer (if the methods disappear, Android Studio will warn about it). Wonder why this is not on the SDK. Makes life easier, I think.
I just found a problem with the last approach... Doesn't seem to always return the context. getApplicationContext() and getBaseContext() work, but when I call ActivityThread.currentApplication(), it returns null. I'm calling all 3 inside a Thread which is declared inside a Service as a constant. Might not be a reliable way of getting a Context instance. Though I don't think it happened many times since my other comment. I think this is the only time. It's happening on Android 4.0.3 on the emulator, but doesn't happen with OnePlus X on Lollipop 5.1 nor on BV9500 with Oreo 8.1.
A
Alessio

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

Note: There is normally no need to subclass Application. In most situations, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), include Context.getApplicationContext() as a Context argument when invoking your singleton's getInstance() method.

and explained by Dianne Hackborn

The only reason Application exists as something you can derive from is because during the pre-1.0 development one of our application developers was continually bugging me about needing to have a top-level application object they can derive from so they could have a more "normal" to them application model, and I eventually gave in. I will forever regret giving in on that one. :)

She is also suggesting the solution to this problem:

If what you want is some global state that can be shared across different parts of your app, use a singleton. [...] And this leads more naturally to how you should be managing these things -- initializing them on demand.

so what I did was getting rid of extending Application, and pass the context directly to the singleton helper's getInstance(), while saving a reference to the application context in the private constructor:

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

the caller will then pass a local context to the helper:

Helper.getInstance(myCtx).doSomething();

So, to answer this question properly: there are ways to access the Application Context statically, but they all should be discouraged, and you should prefer passing a local context to the singleton's getInstance().

For anyone interested, you can read a more detailed version at fwd blog


@Alessio Doesn't this method lead to memory leaks
@codephillip I don't understand what you're talking about. The singleton references the application context retrieved from the passed activity, not the host activity. That's legit, and it will not cause any memory leak. That's the main point of the blog I wrote. If you really think you're right, please send me a sample code where I can reproduce the memory leak you're talking about, because that's not the case.
I think @KigenyiPhillip is correct, and this does still represent a resource leak. Picture the reference chart after your first call to getInstance(ctx). You have a GC root instance of type MyHelper, which has a private field mContext of type Context, which references the application context collected via the context passed to getInstance(). instance is never set a second time, nor cleared, so GC will never catch the appcontext referenced by instance. You don't leak any activities so it's low cost IMO.
@MarkMcKenna as you state "which has a private field mContext of type Context, which references the application context", so that's clear to you that mContext is a reference to the application context, not to any context. In getApplicationContext() docs you read: "a Context whose lifecycle is separate from the current context, that is tied to the lifetime of the process rather than the current component". How can this create a memory leak? The application context is GC'd only when the process exits.
@Alessio if you accept that a reference to the application context doesn't qualify as a resource leak, then you can simplify this by posting a static reference to this in Application.onCreate(), which makes the accepted answer better.
C
Community

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.


The right link to the article: android-developers.blogspot.co.il/2009/01/…
C
Community

Here is an undocumented way to get an Application (which is a Context) from anywhere in the UI thread. It relies on the hidden static method ActivityThread.currentApplication(). It should work at least on 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
}

Note that it is possible for this method to return null, e.g. when you call the method outside of the UI thread, or the application is not bound to the thread.

It is still better to use @RohitGhatol's solution if you can change the Application code.


I used the above method KennyTM, but sometimes the method returns null. Is there some other alternative to this ? Like if we get a null here, we can retrieve the context from elsewhere. In my case, onCreate() of Application is not called. But the above method gets called before it. Plzzz help
This will not always work in the case where GC cleaned out all activity related stuff.
Then how do getApplicationContext() or getBaseContext() return a Context instance? Static variable internally and they just return it instead of relying on currentApplication()? Would be cool to go get the Context from where the 2 functions get it - but statically. I thought currentApplication() was where the other 2 would go, but it seems not to be. Wonder what is it then.
P
Peter Mortensen

It depends on what you are using the context for. I can think of at least one disadvantage to that method:

If you are trying to create an AlertDialog with AlertDialog.Builder, the Application context won't work. I believe you need the context for the current Activity...


That's right. If you use the application context for that, you may see your dialog hidden under foreground activities.
+1 first of all. And the possible error that comes is Unable to start activity ComponentInfo{com.samples/com.MyActivity}: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
p
phnmnn

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


C
Community

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

u
user605331

If you're open to using RoboGuice, you can have the context injected into any class you want. Here's a small sample of how to do it with RoboGuice 2.0 (beta 4 at time of this writing)

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

P
Peter Mortensen

I've used this at some point:

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

This is a valid context I used at getting system services and worked.

But, I used it only in framework/base modifications and did not try it in Android applications.

A warning that you must know: When registering for broadcast receivers with this context, it will not work and you will get:

java.lang.SecurityException: Given caller package android is not running in process ProcessRecord


S
Sheharyar

If you don't want to modify the manifest file, you can manually store the context in a static variable in your initial activity:

public class App {
    private static Context context;

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

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

And just set the context when your activity (or activities) start:

// MainActivity

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

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

    // Other stuff
}

Note: Like all other answers, this is a potential memory leak.


What exactly will it be leaking since the context in this case is bound to the application? If the application dies, so does everything else.
Is there anyway of preventing this leak on static context references?
No, since you are setting the context with getApplicationContext() it is not going to leak activity contexts. However, it may return null in a non-UI thread running beyond the activity.
H
Hayi Nukman

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.


This corehelper's object class will be initialised and can be used through out activities at later stage? Sorry I am new to kotlin
P
Peter Mortensen

You can use the following:

MainActivity.this.getApplicationContext();

MainActivity.java:

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

Any other class:

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

This only works if you are inside an inner class, which is hardly the case in the OP.
This would work as long as the ANY_METHOD is called after MainActivity is created, but keeping static references to activities almost inevitably introduces memory leaks (as other responses to OP's question already mention), so if you really must keep a static reference, use the application context only.
Inner classes are evil. Worst part is that a lot of people do that for AsyncTasks and things like that, because many tutorials do it that way...
B
BlueWizard

According to this source you can obtain your own Context by extending 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();
    }
}

JavaDoc for ContextWrapper

Proxying implementation of Context that simply delegates all of its calls to another Context. Can be subclassed to modify behavior without changing the original Context.


This is interesting. Good to learn about ContextWrapper. However, if you need to pass in the application context to this constructor, you still need to get it from somewhere.
L
Lucifer

I think you need a body for the getAppContext() method:

public static Context getAppContext()
   return MyApplication.context; 

V
Versa

If you for some reason want Application context in any class, not just those extending application/activity, maybe for some factory or helper classes. You can add the following singleton to your app.

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

then initialize it in your application class's onCreate with

GlobalAppContextSingleton.getInstance().initialize(this);

use it anywhere by calling

GlobalAppContextSingleton.getInstance().getApplicationContext()

I don't recommend this approach to anything but application context however. As it can cause memory leaks.


It's not like the class/method names are set in stone, kept it long and (hopefully) descriptive for a Q&A, shortened it for my own use.
I
IntelliJ Amiya

I use a variation of the Singleton design pattern to help me with this.

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

I then call ApplicationContextSingleton.setContext( this ); in my activity.onCreate() and ApplicationContextSingleton.setContext( null ); in onDestroy();


If all you need is context you can call activity.getApplicationContext(); That can be held onto statically without having to worry about leaks.
this will produce memory leaks
P
Peter Mortensen

I just released a jQuery-inspired framework for Android called Vapor API that aims to make app development simpler.

The central $ facade class maintains a WeakReference (link to awesome Java blog post about this by Ethan Nicholas) to the current Activity context which you can retrieve by calling:

$.act()

A WeakReference maintains a reference without preventing the garbage collection reclaiming the original object, so you shouldn't have a problem with memory leaks.

The downside of course is that you run the risk that $.act() could return null. I have not come across this scenario yet though, so it's perhaps just a minimal risk, worth mentioning.

You can also set the context manually if you are not using VaporActivity as your Activity class:

$.act(Activity);

Also, much of the Vapor API framework uses this stored context inherently which might mean you needn't store it yourself at all if you decide to use the framework. Check out the site for more information and samples.

I hope that helps :)


Apparently this just got downvoted.. an explanation would be nice!?
I didn't downvote this, but Javascript has nothing to do with the question at hand, that would explain any downvotes you may have had! Cheers.
So you're downvoting it because it was inspired by the API semantics of a framework that isn't on the same platform?! I think you guys miss the point of applying platform-agnostic principles.....................................
this answer is totally unrelated to JavaScript. Read the answer before you downvote :/
In fact, this answer inspired me to use the convenient JQuery class naming. But it has nothing to do with JQuery. People should read better! Idea of using a WeakReference is beautiful!
p
payne

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.


You are right. And it will also result to memory leaks!
S
Sergey Trukhachev

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.


J
Joseph

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>