Android应用开发—如何解决handler的警告:Handler Class Should be Static or Leaks Occur

简介: 转自android handler的警告Handler Class Should be Static or Leaks Occur在使用Handler更新UI的时候,我是这样写的:public class...

转自android handler的警告Handler Class Should be Static or Leaks Occur

在使用Handler更新UI的时候,我是这样写的:

public class SampleActivity extends Activity {
    private final Handler mLeakyHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
          // TODO
        }
    }
}

看起来很正常的,但是 Android Lint 却给出了警告:

This Handler class should be static or leaks might occur

意思是说:这个Handler必须是static的,否则就会引发内存泄露。

其实,对于这个问题,Android Framework 的工程师 Romain Guy 早已经在Google论坛上做出过解释,并且给出了他的建议写法:

I wrote that debugging code because of a couple of memory leaks I
found in the Android codebase. Like you said, a Message has a
reference to the Handler which, when it’s inner and non-static, has a
reference to the outer this (an Activity for instance.) If the Message
lives in the queue for a long time, which happens fairly easily when
posting a delayed message for instance, you keep a reference to the
Activity and “leak” all the views and resources. It gets even worse
when you obtain a Message and don’t post it right away but keep it
somewhere (for instance in a static structure) for later use.

他的建议写法是:

class OuterClass {
    class InnerClass {
        private final WeakReference<OuterClass> mTarget;

        InnerClass(OuterClass target) {
           mTarget = new WeakReference<OuterClass>(target);
    }

    void doSomething() {
           OuterClass target = mTarget.get();
           if (target != null) {
                target.do();    
           }
     }
}

下面,我们进一步解释一下:

  • Android App启动的时候,Android Framework 为主线程创建一个Looper对象,这个Looper对象将贯穿这个App的整个生命周期,它实现了一个消息队列(Message Queue),并且开启一个循环来处理Message对象。而Framework的主要事件都包含着内部Message对象,当这些事件被触发的时候,Message对象会被加到消息队列中执行。
  • 当一个Handler被实例化时(如上面那样),它将和主线程Looper对象的消息队列相关联,被推到消息队列中的Message对象将持有一个Handler的引用以便于当Looper处理到这个Message的时候,Framework执行Handler的handleMessage(Message)方法。
  • 在 Java 语言中,非静态匿名内部类将持有一个对外部类的隐式引用,而静态内部类则不会。

到底内存泄露是在哪里发生的呢?以下面代码为例:

public class SampleActivity extends Activity {              
    private final Handler mLeakyHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
          // ...
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Post a message and delay its execution for 10 minutes.
        mLeakyHandler.postDelayed(new Runnable() {
            @Override
            public void run() { }
        }, 60 * 10 * 1000);

        // Go back to the previous Activity.
        finish();
     }
}

当Activity被finish()掉,Message将存在于消息队列中长达10分钟的时间才会被执行到。这个Message持有一个对Handler的引用,Handler也会持有一个对于外部类(SampleActivity)的隐式引用,这些引用在Message被执行前将一直保持,这样会保证Activity的上下文不被垃圾回收机制回收,同时也会泄露应用程序的资源(views and resources)。

为解决这个问题,下面这段代码中的Handler则是一个静态匿名内部类。静态匿名内部类不会持有一个对外部类的隐式引用,因此Activity将不会被泄露。如果你需要在Handler中调用外部Activity的方法,就让Handler持有一个对Activity的WeakReference,这样就不会泄露Activity的上下文了,如下所示:

public class SampleActivity extends Activity {     
    /**
    * Instances of static inner classes do not hold an implicit
    * reference to their outer class.
    */
    private static class MyHandler extends Handler {
        private final WeakReference<SampleActivity> mActivity;
        public MyHandler(SampleActivity activity) {
        mActivity = new WeakReference<SampleActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        SampleActivity activity = mActivity.get();
        if (activity != null) {
            // ...
        }
    }
}

private final MyHandler mHandler = new MyHandler(this);      
    /**
    * Instances of anonymous classes do not hold an implicit
    * reference to their outer class when they are "static".
    */
    private static final Runnable sRunnable = new Runnable() {
        @Override
        public void run() { }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Post a message and delay its execution for 10 minutes.
        mHandler.postDelayed(sRunnable, 60 * 10 * 1000);

        // Go back to the previous Activity.
        finish();
    }
}

总结:
在实际开发中,如果内部类的生命周期和Activity的生命周期不一致(比如上面那种,Activity finish()之后要等10分钟,内部类的实例才会执行),则在Activity中要避免使用非静态的内部类,这种情况,就使用一个静态内部类,同时持有一个对Activity的WeakReference。

目录
相关文章
|
7天前
|
安全 Shell Android开发
Android系统 init.rc sys/class系统节点写不进解决方案和原理分析
Android系统 init.rc sys/class系统节点写不进解决方案和原理分析
22 0
|
7天前
|
消息中间件 网络协议 Java
Android 开发中实现数据传递:广播和Handler
Android 开发中实现数据传递:广播和Handler
12 1
|
3月前
|
安全 Android开发 开发者
【Android开发小技巧】扔掉这坑人的 Handler
【Android开发小技巧】扔掉这坑人的 Handler
40 0
|
4月前
|
开发工具 Android开发
Android项目 Duplicate class
同时接入基础版短视频SDK跟播放器SDK出现Duplicate class
89 1
|
25天前
|
安全 API Android开发
Android应用开发中实现动态权限请求的实践指南
【4月更文挑战第5天】 在现代安卓应用开发中,用户隐私和安全日益受到重视。自Android 6.0(API级别23)起,系统引入了动态权限模型,要求应用在运行时而非安装时请求特定权限。这一变化迫使开发者调整权限请求策略,以增强用户信任并确保应用的正常运行。本文将深入探讨如何在Android应用中有效实施动态权限请求,提供一套详细的实践流程,并通过代码示例阐释关键步骤。我们将讨论如何判断权限的必要性、向用户发起请求的最佳时机以及如何处理用户的授权结果。
|
27天前
|
消息中间件 安全 数据处理
Android之Handler、Message、MessageQueue、Looper详解2
Android之Handler、Message、MessageQueue、Looper详解
28 0
|
27天前
|
Java Android开发
Android之Handler、Message、MessageQueue、Looper详解1
Android之Handler、Message、MessageQueue、Looper详解
21 0
|
28天前
|
XML Android开发 数据格式
Caused by: android.view.InflateException: Binary XML file line #11: Error inflating class
Caused by: android.view.InflateException: Binary XML file line #11: Error inflating class
22 0
|
8月前
|
Android开发
Android面试常客之Handler全解1
Android面试常客之Handler全解
|
4月前
|
消息中间件 缓存 安全
android开发,使用kotlin学习消息机制Handler
android开发,使用kotlin学习消息机制Handler
84 0