93

安卓工作室:

不要将 Android 上下文类放在静态字段中;这是内存泄漏(也破坏了即时运行)

所以 2 个问题:

#1 如何在startService没有上下文的静态变量的情况下从静态方法调用 a?
#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

注意:我认为我的主要问题可能是如何将上下文传递给调用方法所在的类。

4

8 回答 8

61

如果您决定将其存储在任何成员字段中,请确保您通过 context.getApplicationContext() 或在通过方法/构造函数传递给您的单例的任何上下文上调用 getApplicationContext() 。

白痴证明示例(即使有人传入一个活动,它也会获取应用程序上下文并使用它来实例化单例):

public static synchronized RestClient getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new RestClient(context.getApplicationContext());
    }
    return mInstance;
}

getApplicationContext() 根据文档:“返回当前进程的单个全局应用程序对象的上下文。”

这意味着“getApplicationContext()”返回的上下文将贯穿整个过程,因此无论您是否在任何地方存储对它的静态引用都没有关系,因为它在您的应用程序运行期间始终存在(并且比任何对象都存在) /singleton 由它实例化)。

将其与保存大量数据的视图/活动内部的上下文进行比较,如果您泄漏活动保存的上下文,系统将无法释放该资源,这显然是不好的。

通过其上下文对活动的引用应该与活动本身存在相同的生命周期,否则它将持有上下文人质导致内存泄漏(这是 lint 警告背后的原因)。

编辑:对于从上面的文档中抨击示例的人,代码中甚至还有一个关于我刚刚写的内容的注释部分:

    // getApplicationContext() is key, it keeps you from leaking the
    // Activity or BroadcastReceiver if someone passes one in.
于 2016-10-25T09:08:25.407 回答
57

只需将其作为参数传递给您的方法。仅出于Context启动Intent.

这是您的方法的外观:

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

从对问题的评论中更新:将上下文从启动活动(通过构造函数参数或方法参数)级联到您需要的点。

于 2016-06-08T18:15:34.783 回答
8

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

现在您可以访问 Context 之类的

  if (context.get() instanceof MainActivity) {
            ((MainActivity) context.get()).startActivityForResult(pickIntent, CODE_REQUEST_PICK_APPWIDGET);
        }
于 2020-06-23T23:43:17.947 回答
6

这只是一个警告。不用担心。如果您想使用应用程序上下文,可以将其保存在“单例”类中,该类用于保存项目中的所有单例类。

于 2017-03-06T03:18:38.100 回答
2

在您的情况下,将其作为静态字段没有多大意义,但我认为这在所有情况下都不是坏事。如果您现在在做什么,您可以拥有具有上下文的静态字段,然后将其设为空。我正在为我的主模型类创建静态实例,其中包含上下文,它的应用程序上下文不是活动上下文,而且我有包含 Activity 的类的静态实例字段,我在销毁时为空。我没有看到我有内存泄漏。因此,如果一些聪明的人认为我错了,请随时发表评论...

Instant Run 也可以在这里正常工作...

于 2016-10-01T18:33:29.750 回答
2

通常,避免将上下文字段定义为静态。警告本身解释了原因:这是内存泄漏。不过,打破即时运行可能不是这个星球上最大的问题。

现在,您会在两种情况下收到此警告。例如(最明显的一个):

public static Context ctx;

然后还有一个更棘手的问题,其中上下文被包装在一个类中:

public class Example{
    public Context ctx;
    //Constructor omitted for brievety 
}

该类在某处被定义为静态的:

public static Example example;

你会收到警告。

解决方案本身相当简单:不要将上下文字段放在静态实例中,无论是包装类还是直接将其声明为静态。

警告的解决方案很简单:不要静态放置字段。在您的情况下,将上下文作为实例传递给方法。对于进行多个 Context 调用的类,请使用构造函数将上下文(或就此而言的 Activity)传递给该类。

请注意,这是一个警告,而不是错误。如果您出于某种原因需要静态上下文,您可以这样做。尽管这样做会造成内存泄漏。

于 2017-12-22T12:36:40.210 回答
1

如果你确定它是一个应用程序上下文。没关系。添加这个

@SuppressLint("StaticFieldLeak")
于 2019-07-29T09:49:15.560 回答
0

您可以将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;
    }
}

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

于 2021-11-21T12:18:56.833 回答