警告:不要将Android上下文类放在静态字段中;这是内存泄漏(也打破了Instant Run)

时间:2016-06-08 18:12:45

标签: android android-studio-2.2

Android Studio:

  

不要将Android上下文类放在静态字段中;这是一个   内存泄漏(也打破了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的情况下执行此操作的正确方法是什么?

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

7 个答案:

答案 0 :(得分:47)

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

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

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

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

这意味着“getApplicationContext()”返回的上下文将贯穿整个过程,因此如果您在任何地方存储静态引用并不重要,因为它会在您的应用程序运行期间始终存在(并且比任何实例化的对象/单身人士都活得更长。

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

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

编辑:对于从上述文档中抨击示例的人来说,代码中甚至还有关于我刚才所写内容的评论部分:

    // getApplicationContext() is key, it keeps you from leaking the
    // Activity or BroadcastReceiver if someone passes one in.

答案 1 :(得分:45)

只需将其作为参数传递给您的方法即可。创建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);
    }
}

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

答案 2 :(得分:4)

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

答案 3 :(得分:1)

在你的情况下,将它作为静态字段没有多大意义,但我不认为它在所有情况下都不好。如果您现在正在做什么,您可以使用具有上下文的静态字段,稍后将其置零。我正在为我的主模型类创建静态实例,其中包含上下文,其应用程序上下文不是活动上下文,并且我还有包含Activity的类的静态实例字段,我在destroy上为null。我没有看到我有内存泄漏。所以,如果一些聪明的家伙认为我错了,请随意评论......

Instant Run在这里工作得很好......

答案 4 :(得分:1)

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

现在,您有两种情况可以获得此警告。对于一个实例(最明显的一个):

Things

然后有一点比较棘手,其中上下文包含在一个类中:

public static Context ctx;

该类在某处被定义为static:

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

你会得到警告。

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

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

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

答案 5 :(得分:1)

使用WeakReference将上下文存储在Singleton类中,警告将消失

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

答案 6 :(得分:0)

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

@SuppressLint("StaticFieldLeak")