警告:不要将 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;
    }
}

警告将消失。但我不知道这是最好的方法。