警告:不要将 Android 上下文 类 放在静态字段中;这是一个内存泄漏(并且还会破坏 Instant 运行)
Warning: Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
Android工作室:
Do not place Android context classes in static fields; this is a
memory leak (and also breaks Instant Run)
所以 2 个问题:
#1 如何从没有上下文静态变量的静态方法调用 startService
?
#2 如何从静态方法发送 localBroadcast(相同)?
示例:
public static void log(int iLogLevel, String sRequest, String sData) {
if(iLogLevel > 0) {
Intent intent = new Intent(mContext, LogService.class);
intent.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
mContext.startService(intent);
}
}
或
Intent intent = new Intent(MAIN_ACTIVITY_RECEIVER_INTENT);
intent.putExtra(MAIN_ACTIVITY_REQUEST_FOR_UPDATE, sRequest));
intent.putExtra(MAIN_ACTIVITY_DATA_FOR_VIEW, sData);
intent.putExtra(MAIN_ACTIVITY_LOG_LEVEL, iLogLevel);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
不使用 mContext
的正确方法是什么?
注意:我认为我的主要问题可能是如何将上下文传递给调用方法所在的 class。
只需将其作为参数传递给您的方法即可。仅仅为了启动 Intent
.
而创建 Context
的静态实例是没有意义的
你的方法应该是这样的:
public static void log(int iLogLevel, String sRequest, String sData, Context ctx) {
if(iLogLevel > 0) {
Intent intent = new Intent(ctx, LogService.class);
intent1.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
ctx.startService(intent);
}
}
根据问题评论更新:将上下文从启动 activity(通过构造函数参数或方法参数)级联到您需要的位置。
在你的情况下,将它作为静态字段没有多大意义,但我认为它在所有情况下都不是坏事。如果您现在在做什么,您可以拥有具有上下文的静态字段,稍后再将其清空。我正在为我的主模型 class 创建静态实例,它内部有上下文,它的应用程序上下文不是 activity 上下文,而且我有 class 的静态实例字段,其中包含 Activity 我在销毁时为空。我没有看到我有内存泄漏。所以如果有聪明的人认为我错了,请随时发表评论...
Instant 运行 在这里也可以正常工作...
如果您决定将 context.getApplicationContext() 存储在任何成员字段中,请确保在通过 methods/constructor 传递给您的单例的任何上下文上传递 context.getApplicationContext() 或调用 getApplicationContext() 。
白痴证明示例(即使有人传入 activity 它也会获取应用程序上下文并使用它来实例化单例):
public static synchronized RestClient getInstance(Context context) {
if (mInstance == null) {
mInstance = new RestClient(context.getApplicationContext());
}
return mInstance;
}
getApplicationContext() 根据文档:"Return the context of the single, global Application object of the current process."
这意味着 "getApplicationContext()" 返回的上下文将在整个过程中存在,因此如果您将对它的静态引用存储在任何地方都没有关系,因为它在您的应用程序运行期间始终存在(并且比它实例化的任何 objects/singletons 都长寿)。
将其与 views/activities 内部保存大量数据的上下文进行比较,如果您泄漏 activity 保存的上下文,系统将无法释放该资源,这显然不好。
通过其上下文对 activity 的引用应该与 activity 本身具有相同的生命周期,否则它将持有上下文人质导致内存泄漏(这是背后的原因棉绒警告)。
编辑: 对于抨击上面文档中示例的人,代码中甚至还有一个关于我刚刚写的内容的评论部分:
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
这只是一个警告。不用担心。如果你想使用应用程序上下文,你可以将它保存在一个 "singleton" class 中,它用于保存你项目中的所有单例 class。
通常,避免将上下文字段定义为静态。警告本身解释了原因:这是内存泄漏。瞬间崩溃运行可能不是这个星球上最大的问题。
现在,您会在两种情况下收到此警告。举个例子(最明显的一个):
public static Context ctx;
然后是更棘手的一个,上下文被包裹在 class:
public class Example{
public Context ctx;
//Constructor omitted for brievety
}
并且 class 在某处被定义为静态的:
public static Example example;
你会收到警告。
解决方案本身相当简单:不要将上下文字段放在静态实例中,无论是包装 class 还是直接将其声明为静态。
警告的解决方法很简单:不要静态放置字段。在您的情况下,将上下文作为实例传递给方法。对于进行多个 Context 调用的 classes,使用构造函数将上下文(或 Activity 传递给 class.
注意是警告,不是错误。如果您出于某种原因 需要 静态上下文,您可以这样做。尽管这样做会造成内存泄漏。
如果您确定它是一个应用程序上下文。没关系。添加这个
@SuppressLint("StaticFieldLeak")
使用 WeakReference
将上下文存储在单例中 类 & 警告将消失
private WeakReference<Context> context;
//Private contructor
private WidgetManager(Context context) {
this.context = new WeakReference<>(context);
}
//Singleton
public static WidgetManager getInstance(Context context) {
if (null == widgetManager) {
widgetManager = new WidgetManager(context);
}
return widgetManager;
}
现在您可以像
一样访问上下文
if (context.get() instanceof MainActivity) {
((MainActivity) context.get()).startActivityForResult(pickIntent, CODE_REQUEST_PICK_APPWIDGET);
}
您可以将 App
实例设置为静态 属性。例如:
public class App extends MultiDexApplication {
private static App context;
public void onCreate() {
super.onCreate();
App.context = this;
}
public static Context getAppContext() {
return App.context;
}
}
警告将消失。但我不知道这是最好的方法。
Android工作室:
Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
所以 2 个问题:
#1 如何从没有上下文静态变量的静态方法调用 startService
?
#2 如何从静态方法发送 localBroadcast(相同)?
示例:
public static void log(int iLogLevel, String sRequest, String sData) {
if(iLogLevel > 0) {
Intent intent = new Intent(mContext, LogService.class);
intent.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
mContext.startService(intent);
}
}
或
Intent intent = new Intent(MAIN_ACTIVITY_RECEIVER_INTENT);
intent.putExtra(MAIN_ACTIVITY_REQUEST_FOR_UPDATE, sRequest));
intent.putExtra(MAIN_ACTIVITY_DATA_FOR_VIEW, sData);
intent.putExtra(MAIN_ACTIVITY_LOG_LEVEL, iLogLevel);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
不使用 mContext
的正确方法是什么?
注意:我认为我的主要问题可能是如何将上下文传递给调用方法所在的 class。
只需将其作为参数传递给您的方法即可。仅仅为了启动 Intent
.
Context
的静态实例是没有意义的
你的方法应该是这样的:
public static void log(int iLogLevel, String sRequest, String sData, Context ctx) {
if(iLogLevel > 0) {
Intent intent = new Intent(ctx, LogService.class);
intent1.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
ctx.startService(intent);
}
}
根据问题评论更新:将上下文从启动 activity(通过构造函数参数或方法参数)级联到您需要的位置。
在你的情况下,将它作为静态字段没有多大意义,但我认为它在所有情况下都不是坏事。如果您现在在做什么,您可以拥有具有上下文的静态字段,稍后再将其清空。我正在为我的主模型 class 创建静态实例,它内部有上下文,它的应用程序上下文不是 activity 上下文,而且我有 class 的静态实例字段,其中包含 Activity 我在销毁时为空。我没有看到我有内存泄漏。所以如果有聪明的人认为我错了,请随时发表评论...
Instant 运行 在这里也可以正常工作...
如果您决定将 context.getApplicationContext() 存储在任何成员字段中,请确保在通过 methods/constructor 传递给您的单例的任何上下文上传递 context.getApplicationContext() 或调用 getApplicationContext() 。
白痴证明示例(即使有人传入 activity 它也会获取应用程序上下文并使用它来实例化单例):
public static synchronized RestClient getInstance(Context context) {
if (mInstance == null) {
mInstance = new RestClient(context.getApplicationContext());
}
return mInstance;
}
getApplicationContext() 根据文档:"Return the context of the single, global Application object of the current process."
这意味着 "getApplicationContext()" 返回的上下文将在整个过程中存在,因此如果您将对它的静态引用存储在任何地方都没有关系,因为它在您的应用程序运行期间始终存在(并且比它实例化的任何 objects/singletons 都长寿)。
将其与 views/activities 内部保存大量数据的上下文进行比较,如果您泄漏 activity 保存的上下文,系统将无法释放该资源,这显然不好。
通过其上下文对 activity 的引用应该与 activity 本身具有相同的生命周期,否则它将持有上下文人质导致内存泄漏(这是背后的原因棉绒警告)。
编辑: 对于抨击上面文档中示例的人,代码中甚至还有一个关于我刚刚写的内容的评论部分:
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
这只是一个警告。不用担心。如果你想使用应用程序上下文,你可以将它保存在一个 "singleton" class 中,它用于保存你项目中的所有单例 class。
通常,避免将上下文字段定义为静态。警告本身解释了原因:这是内存泄漏。瞬间崩溃运行可能不是这个星球上最大的问题。
现在,您会在两种情况下收到此警告。举个例子(最明显的一个):
public static Context ctx;
然后是更棘手的一个,上下文被包裹在 class:
public class Example{
public Context ctx;
//Constructor omitted for brievety
}
并且 class 在某处被定义为静态的:
public static Example example;
你会收到警告。
解决方案本身相当简单:不要将上下文字段放在静态实例中,无论是包装 class 还是直接将其声明为静态。
警告的解决方法很简单:不要静态放置字段。在您的情况下,将上下文作为实例传递给方法。对于进行多个 Context 调用的 classes,使用构造函数将上下文(或 Activity 传递给 class.
注意是警告,不是错误。如果您出于某种原因 需要 静态上下文,您可以这样做。尽管这样做会造成内存泄漏。
如果您确定它是一个应用程序上下文。没关系。添加这个
@SuppressLint("StaticFieldLeak")
使用 WeakReference
将上下文存储在单例中 类 & 警告将消失
private WeakReference<Context> context;
//Private contructor
private WidgetManager(Context context) {
this.context = new WeakReference<>(context);
}
//Singleton
public static WidgetManager getInstance(Context context) {
if (null == widgetManager) {
widgetManager = new WidgetManager(context);
}
return widgetManager;
}
现在您可以像
一样访问上下文 if (context.get() instanceof MainActivity) {
((MainActivity) context.get()).startActivityForResult(pickIntent, CODE_REQUEST_PICK_APPWIDGET);
}
您可以将 App
实例设置为静态 属性。例如:
public class App extends MultiDexApplication {
private static App context;
public void onCreate() {
super.onCreate();
App.context = this;
}
public static Context getAppContext() {
return App.context;
}
}
警告将消失。但我不知道这是最好的方法。