Static way to get 'Context' in Android?
有没有办法在静态方法中获取当前的
我正在寻找这种方法,因为我讨厌每次"上下文"实例更改时都保存它。
这样做:
在Android清单文件中,声明以下内容。
1 | </application> |
然后写下课程:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class MyApplication extends Application { private static Context context; public void onCreate() { super.onCreate(); MyApplication.context = getApplicationContext(); } public static Context getAppContext() { return MyApplication.context; } } |
现在,所有地方都调用
大多数应用程序需要一种方便的方法来获取应用程序上下文,它们创建自己的类来扩展
指南
您可以通过在项目中创建一个类来实现这一点,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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的标记中指定类的名称:
1 2 3 4 5 | <application ... android:name="com.example.App"> ... </application> |
然后,可以使用以下方法检索任何静态方法中的应用程序上下文:
1 2 3 | public static void someMethod() { Context context = App.getContext(); } |
警告
在将上述内容添加到项目中之前,您应该考虑文档中的内容:
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.
反射
还有另一种使用反射获取应用程序上下文的方法。在Android中,反射经常被忽视,我个人认为这不应该用于生产。
要检索应用程序上下文,我们必须对自API 1以来可用的隐藏类(ActivityThread)调用一个方法:
1 2 3 4 | public static Application getApplicationUsingReflection() throws Exception { return (Application) Class.forName("android.app.ActivityThread") .getMethod("currentApplication").invoke(null, (Object[]) null); } |
还有一个隐藏类(AppGlobals),它提供了一种以静态方式获取应用程序上下文的方法。它使用
1 2 3 4 | public static Application getApplicationUsingReflection() throws Exception { return (Application) Class.forName("android.app.AppGlobals") .getMethod("getInitialApplication").invoke(null, (Object[]) null); } |
快乐编码!
不,我想没有。不幸的是,您无法从
假设我们在讨论获取应用程序上下文,我按照@rohitghatol扩展应用程序的建议实现了它。然后发生的事情是,无法保证以这种方式检索的上下文总是非空的。在您需要它的时候,通常是因为您想要初始化一个助手或获取一个资源,所以您不能延迟;处理空的情况对您没有帮助。所以我明白我基本上是在和android体系结构作斗争,如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.
由黛安·哈克伯恩解释
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. :)
她还建议解决这个问题:
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.
因此,我所做的就是去掉扩展应用程序,并将上下文直接传递给singleton helper的getInstance(),同时在私有构造函数中保存对应用程序上下文的引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } } |
然后调用方将本地上下文传递给帮助程序:
1 | Helper.getInstance(myCtx).doSomething(); |
因此,要正确回答这个问题:有一些方法可以静态地访问应用程序上下文,但它们都不应该被鼓励,您应该更喜欢将本地上下文传递给singleton的getInstance()。
对于任何感兴趣的人,您可以在fwd blog上阅读更详细的版本。
下面是一种从UI线程中的任何位置获取应用程序(上下文)的未经记录的方法。它依赖于隐藏的静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 } |
请注意,此方法可能返回空值,例如,当您在UI线程外部调用方法时,或者应用程序未绑定到线程时。
如果可以更改应用程序代码,最好使用@rohitghatol的解决方案。
这取决于您使用上下文的目的。我至少可以想到这种方法的一个缺点:
如果您试图用
如果您愿意使用roboguice,那么您可以将上下文注入您想要的任何类中。下面是一个如何使用roboguice 2.0(本文撰写时是beta 4)的小示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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) { } } } |
我曾经用过这个:
1 2 | ActivityThread at = ActivityThread.systemMain(); Context context = at.getSystemContext(); |
这是我在获取系统服务和工作时使用的有效上下文。
但是,我只在框架/基础修改中使用它,并没有在Android应用程序中尝试它。
您必须知道的警告:在此上下文中注册广播接收器时,它将不起作用,您将得到:
java.lang.SecurityException: Given caller package android is not running in process ProcessRecord
科特林
1 2 3 4 5 6 7 8 9 10 11 12 13 | open class MyApp : Application() { override fun onCreate() { super.onCreate() mInstance = this } companion object { lateinit var mInstance: MyApp fun getContext(): Context? { return mInstance.applicationContext } } } |
让上下文像
1 | MyApp.mInstance |
或
1 | MyApp.getContext() |
根据这个源代码,您可以通过扩展ContextWrapper来获取自己的上下文。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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(); } } |
用于ContextWrapper的JavaDoc
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.
您可以使用以下内容:
1 | MainActivity.this.getApplicationContext(); |
mainactivity.java:主要活动.java
1 2 3 4 5 6 7 8 | ... public class MainActivity ... { static MainActivity ma; ... public void onCreate(Bundle b) { super... ma=this; ... |
任何其他类别:
1 2 3 | public ... public ANY_METHOD... { Context c = MainActivity.ma.getApplicationContext(); |
我想你需要一具尸体来做
1 2 | public static Context getAppContext() return MyApplication.context; |
Kotlin之道:
显示:
1 | </application> |
我的应用.kt
1 2 3 4 5 6 7 8 9 10 11 12 | class MyApplication: Application() { override fun onCreate() { super.onCreate() instance = this } companion object { lateinit var instance: MyApplication private set } } |
然后您可以通过myapplication.instance访问该属性。
如果不想修改清单文件,可以在初始活动的静态变量中手动存储上下文:
1 2 3 4 5 6 7 8 9 10 11 | public class App { private static Context context; public static void setContext(Context cntxt) { context = cntxt; } public static Context getContext() { return context; } } |
并在活动开始时设置上下文:
1 2 3 4 5 6 7 8 9 10 11 12 | // MainActivity @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Set Context App.setContext(getApplicationContext()); // Other stuff } |
注意:和所有其他答案一样,这是一个潜在的内存泄漏。
我使用了一个单件设计模式的变体来帮助我完成这个任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } } |
然后我在活动中调用
如果出于某种原因,您希望在任何类中包含应用程序上下文,而不仅仅是那些扩展应用程序/活动的类,可能是对于某些工厂类或助手类。您可以将以下单例添加到您的应用程序中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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中初始化它
1 | GlobalAppContextSingleton.getInstance().initialize(this); |
打电话到任何地方都可以使用
1 | GlobalAppContextSingleton.getInstance().getApplicationContext() |
但是,除了应用程序上下文之外,我不推荐这种方法。因为它会导致内存泄漏。
我刚刚发布了一个基于jquery的Android框架,叫做vapor api,旨在使应用程序开发更简单。
中央EDCOX1×10×Futd类维护一个EDCOX1×11(链接到Ethan Nicholas的关于这个惊人的Java博客文章)到当前EDCOX1 1上下文,您可以通过调用来检索:
1 | $.act() |
当然,不利的一面是,您有可能面临
如果不使用
1 | $.act(Activity); |
而且,许多vapor api框架都固有地使用这个存储上下文,这可能意味着如果您决定使用这个框架,就不需要自己存储它。有关更多信息和示例,请访问网站。
希望有帮助:)
所以我修改了接受的答案,因为它导致了内存泄漏,这就是我想到的……
AndroidManifest.xml
1 2 3 | ... </application> |
MyApplication.java
1 2 3 4 5 6 7 8 9 10 11 12 | public class MyBakingAppContext extends Application { private static Object mContext; public void onCreate() { super.onCreate(); mContext = getApplicationContext(); } public static Context getAppContext() { return (Context)mContext; } } |
我实际做的是,为一个对象分配一个上下文,并将该对象作为上下文返回(将其转换为上下文)。希望能有所帮助。
罗希特的回答似乎是正确的。但是,请注意,据我所知,AndroidStudio的"即时运行"依赖于代码中没有