在Android中获取“上下文”的静态方法?

时间:2010-01-04 21:15:01

标签: android android-context

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

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

21 个答案:

答案 0 :(得分:1218)

这样做:

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

答案 1 :(得分:72)

大多数需要方便的方法来获取应用程序上下文的应用程序都会创建自己的类android.app.Application

<强> GUIDE

您可以先在项目中创建一个类,如下所示:

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.getApplicationContext()的Context   首先构建单身人士。


<强>反射

还有另一种使用反射获取应用程序上下文的方法。在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);
} 

快乐的编码!

答案 2 :(得分:49)

不,我认为没有。不幸的是,您无法从getApplicationContext()Activity的其他子类之一调用Context。此外,this问题有些相关。

答案 3 :(得分:46)

假设我们正在讨论获取应用程序上下文,我按照@Rohit Ghatol扩展Application的建议实现了它。那时发生的事情是,不能保证以这种方式检索的上下文总是非空的。当你需要它时,通常是因为你想要初始化一个帮助器,或者获得一个你不能及时延迟的资源;处理null case对你没有帮助。 所以我理解我基本上是在反对Android架构,正如docs

中所述
  

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

并由Dianne Hackborn

解释
  

Application存在的唯一原因是因为在1.0之前的开发过程中,我们的一个应用程序开发人员一直在烦扰我需要拥有一个他们可以派生出来的顶级应用程序对象,这样他们才能拥有对他们应用模型更“正常”,我最终放弃了。   我会永远后悔屈服于那一个。 :)

她也建议解决这个问题:

  

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

所以我做的是摆脱扩展Application,并将上下文直接传递给singleton帮助器的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 blog

阅读更详细的版本

答案 4 :(得分:36)

这是从UI线程中的任何位置获取Application(这是一个上下文)的未记录的方法。它依赖于隐藏的静态方法ActivityThread.currentApplication()。它至少应该在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
}

请注意,此方法可能会返回null,例如当你在UI线程之外调用方法,或者应用程序没有绑定到线程时。

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

答案 5 :(得分:32)

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

如果您尝试使用AlertDialog创建AlertDialog.Builder,则Application上下文将无效。我相信您需要当前Activity ...

的上下文

答案 6 :(得分:11)

如果您愿意使用RoboGuice,则可以将上下文注入到您想要的任何类中。以下是RoboGuice 2.0(在撰写本文时为测试版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) {
        }
    }
}

答案 7 :(得分:8)

我在某个时候使用过它:

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

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

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

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

  

java.lang.SecurityException:给定调用者包android没有在进程ProcessRecord中运行

答案 8 :(得分:4)

Kotlin方式

清单:

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

然后,您可以通过MyApplication.instance

访问该属性

答案 9 :(得分:3)

我认为您需要getAppContext()方法的正文:

public static Context getAppContext()
   return MyApplication.context; 

答案 10 :(得分:3)

科特林

MyApp.mInstance

并获得Context

MyApp.getContext()

{{1}}

答案 11 :(得分:3)

您可以使用以下内容:

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

答案 12 :(得分:3)

根据this source,您可以通过扩展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。可以子类化以修改行为而无需更改原始上下文。

答案 13 :(得分: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;
    }
}

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

答案 14 :(得分:2)

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

private void startCountAnimation() {
          ValueAnimator animator = ValueAnimator.ofInt(0, percA);
    animator.setDuration(1500);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            percA.setText(animation.getAnimatedValue().toString());
        }
    });
    animator.start();

}

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

public class App {
    private static Context context;

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

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

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

答案 15 :(得分:1)

如果由于某种原因,您希望在任何类中使用Application上下文,而不仅仅是扩展应用程序/活动,可能是某些工厂或帮助程序类。您可以将以下单身人士添加到您的应用中。

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

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

答案 16 :(得分:1)

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

答案 17 :(得分:1)

所以我修改了接受的答案,因为它导致内存泄漏,这就是我提出来的......

  

的AndroidManifest.xml

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

    </application>
  

MyApplication.java

public class MyBakingAppContext extends Application {
    private static Object mContext;

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

    public static Context getAppContext() {
        return (Context)mContext;
    }
}

我实际做的是,为对象分配上下文并将对象作为上下文返回(将其转换为上下文)。希望它有所帮助。

答案 18 :(得分:1)

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

中央$ facade class向当前的Activity上下文维护一个WeakReference(链接到关于这个由Ethan Nicholas提供的精彩Java博客文章的链接),您可以通过调用来检索:

$.act()

WeakReference维护引用而不会阻止垃圾回收回收原始对象,因此您不应该遇到内存泄漏问题。

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

如果您未将VaporActivity用作Activity课程,也可以手动设置上下文:

$.act(Activity);

此外,大多数Vapor API框架本身都使用了这个存储的上下文,这可能意味着如果您决定使用框架,则根本不需要自己存储它。查看site了解更多信息和样本。

我希望有帮助:)

答案 19 :(得分:0)

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

这只是愚弄皮棉而没有发现内存泄漏,因为Application类及其后代是Context,所以App实例仍然会产生内存泄漏。

或者,您可以使用功能接口或功能属性来帮助您获取应用上下文。

只需创建一个对象类:

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

希望这会有所帮助。

答案 20 :(得分:0)

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

在 Gradle 中添加刀柄:

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

使用 @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 可以以正确的方式访问上下文而不会出现内存泄漏。