01、Handler的那些事

HandlerPic.png

版权声明:本文为博主原创文章,未经博主允许不得转载。

PS:转载请注明出处
作者: TigerChain
地址: http://www.jianshu.com/p/73e5fd7eb7da
本文出自 TigerChain 简书 Android 系列

教程简介

  • 1、阅读对象

本篇教程适合新手阅读,老手直接略过

  • 2、教程难度

    初级

正文

摘要:

Handler 在 Android 的作用主要是线程间通讯的,现在也有各种文章在讲解 Handler 的作用以及源码分析,但是必定这些都是别人自己的总结和整理,和自己总结还是有区别的,为了加深自己的记忆所以自己也来分析一下 Handler 以及它的小伙伴们。

什么是 Handler

什么是 Handler?

再华丽的解释也不过是官方的解解释吧:先看官网上的一段话

A Handler allows you to send and process Message and Runnable objects associated with
a thread's MessageQueue. Each Handler instance is associated with a single thread 
and that thread's message queue. When you create a new Handler, it is bound to the 
thread / message queue of the thread that is creating it -- from that point on, 
it will deliver messages and runnables to that message queue and execute them 
as they come out of the message queue.

大概意思就是 Handler 可以发送 Message 和 Runnable 对象发送到 Handler 所关联的线程队列中去,每个 Handler 的实例都会绑定到创建它的线程中。

Handler 的作用

再来看官网杂说的

There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed as some point in the future
(2) to enqueue an action to be performed on a different thread than your own.

大概意思就是说,Handler 有两个用途:

(1)、在某一时刻执行某些事情
(2)、在不同的线程中执行操作 (也就是线程间通信)

Handler 常用的方法

方法名 描述
boolean post (Runnable r) 立即执行操作
postAtTime(Runnable, long) 在指定的时间执行某些操作
postDelayed(Runnable, long) 延时执行某些操作
sendEmptyMessage(int) 发送一个含有what的信息
sendMessage(Message) 发送一个Message
sendMessageAtTime(Message, long) 在指定的时间发送消息
sendMessageDelayed(Message, long) 延时发送消息

Handler 默认是运行在 UI 线程中的,默认默认默认...哦 一定记得,至于为什么,我们后面会说的。

Handler 常用的使用方式

1、解决 ANR,子线程更新UI线程

在Android应用程序中我们最常见的异步莫过于 ANR 了(主线程执行耗时操作了,发起网络请求,或操作 IO 等),通常情况下在 Activity 是5秒钟响应就会报这个错,在 BroadCast 中是 10 秒钟,使用 Handler 可以很容易的解决这个问题。

解决方法:

  • 1、定义 Handler 并重写 handleMessage 方法
 //在UI线程中
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    //取得传递过来的 msg
                    String passedChangeTv = (String) msg.obj;
                    tv.setText(passedChangeTv);
                    break ;
            }
        }
    } ;

  • 2、在子线程中去执行耗时操作
 //比如请求服务器,然后更新 TextView
        new Thread(new Runnable() {
            @Override
            public void run() {
                //执行耗时操作
                /**
                 * 1 请求服务器
                 * 2 解析数据
                 * 3 获取数据
                 */
                //比如从服务器取得的数据是" Handler 应用"
                String changeTv = "Handler应用" ;
                Message message = new Message() ;
                message.what = 1 ;
                message.obj = changeTv ;
                mHandler.sendMessage(message) ;
            }
        }).start();

这样就可以解决 ANR

2、可以用作轮询,或是定时器

  • 1、举个栗子,我们要一秒钟打印一个字符串,我们可以使用 Handler 的 postDelayed 方法
 private Handler mtimeTaskHandler  = new Handler() ;
    private void timerTaskHandler() {
        //调用Handler自带的
        mtimeTaskHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.e("===","TAG:"+ System.currentTimeMillis()/1000) ;
                mtimeTaskHandler.postDelayed(this,1000) ;
            }
        },1000) ;
    }
  • 2、一秒钟请求一次服务器,然后把服务器的最新数据显示在 TextView ,在这里我们使用 Handler 的sendMessageDelayed(Message msg,Long deayTime)方法
 private Handler preSecondHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    //取得传递过来的 msg
                    int passedChangeTv = (int) msg.obj;
                    tv.setText(passedChangeTv+"");
                    //再次调用获取服务器最新数据方法
                    preSecondGetServer() ;
                    break ;
            }
        }
    } ;
    private int i ;
    private void preSecondGetServer() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //假设这里从服务器取回来的 i
                i++ ;
                Message message = new Message() ;
                message.what = 1 ;
                message.obj = i ;
            preSecondHandler.sendMessageDelayed(message,1000) ;
            }
        }).start();

这样就可以一秒请求一次服务器,并且把结果显示在 TextView 上了

3、两个子线程进行通信

Handler的作用就是是进行线程间通信的,上面说的都是UI线程(主线程)和子线程之间的通信,那么两个工作线程(子线程)可以通信吗,答案是肯定 的。

  • 1、先声明 Handler 所在的线程
  //成员变量 子线程中的 Handler 
private Handler threadHandler ;
class MyThread extends Thread{
      {
          //一实例化就开启线程 代码块
          start();
      }
      @Override
      public void run() {
          super.run(); // Handler 所在的线程默认是没有 looper 的(除了 Activity )
          Looper.prepare();
          threadHandler = new Handler(){
              @Override
              public void handleMessage(Message msg) {
                  super.handleMessage(msg);
                  //取得传递过来的 msg
                  String passedChangeTv = (String) msg.obj;
                  //设置显示
                  tv.setText(passedChangeTv);
              }
          } ;
          Looper.loop();
      }
  }

注意:子线程默认是没有 Looper 的所以我们在子线程中一定要声明 Looper.prepare() 和 Looper.loop() 在它们之间去初始化 Handler

  • 2、在另一个子线程中发送消息
private void threadHandlerTask() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                String changeTv = "Handler应用" ;
                Message message = new Message() ;
                message.what = 1 ;
                message.obj = changeTv ;
                threadHandler.sendMessage(message) ;
            }
        }).start() ;
    }
  • 3、最后在合适的地方调用,在这里我们为了演示就在 Activity 的onCreate() 方法中调用
 MyThread t = new MyThread() ;
 threadHandlerTask() ;

以上就完成了两个子线程通过 Handler 来通信。

注意:上面的子线程能信有问题,不知道细心的朋友发现了没有,我故意留了一个 bug,忽略此 bug 不提,我们上面的程序性能是有问题的,我们一一解决。

1、上面代码存在的 bug

细心的朋友会说我靠,擦,FK,竟然在子线程中更新 UI,我们要以清楚的看到 threadHandler 是在子线程中的,那么我们是不能更新 UI的。会 ANR 的好不好呀,没错就是这个问题,不怕,程序员就是发现问题并解决问题的,我们来解决这个问题,修改代码如下。

  • 1、首先在 MainActivity 中声明一个 threadHander( threadHander 一定是在主线程中的)
 private Handler threadHander = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //取得传递过来的 msg
                String passedChangeTv = (String) msg.obj;
                tv.setText(passedChangeTv+"");
            }
        } ;

所以上面代码是可以更新 UI 的没有问题

  • 2、声明子线程
class MyThread extends Thread{
        private Looper mLooper ;
        private Handler mHandler ;

        /**
         * 代码块或是构造方法中开启线程都可以
         */

        public MyThread(){
            Log.e("t所在的线程",this.getName()) ;
            start();
        }
//        {
//            //一实例化就开启线程 代码块
//            start();
//        }
        @Override
        public void run() {
            super.run(); // Handler 所在的线程默认是没有 looper 的(除了Activity)
            Looper.prepare();
            mLooper = Looper.myLooper() ;
            mHandler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    //取得传递过来的 msg
                    String passedChangeTv = (String) msg.obj;
                    //设置显示  这里的 Handler 运行在子线程中去的,所以不能更新UI
                   // tv.setText(passedChangeTv);
                    threadHander.sendMessage(threadHander.obtainMessage(1,passedChangeTv)) ;

                }
            } ;
            Looper.loop();
        }

         public Handler getThreadHandler(){
            return mHandler!=null?mHandler:null ;
        }
    }
  • 3、在另一个线程中去发送消息
 private void threadHandlerTask() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                String changeTv = "Handler应用" ;
                Message message = new Message() ;
                message.what = 1 ;
                message.obj = changeTv ;
                t.getThreadHandler().sendMessage(message) ;
            }
        }).start() ;
    }

此处的t就是 MyThread 的实例,声明成成员变量,不用纠结

我们来捋一捋流程,首先调用

t.getThreadHandler().sendMessage(message)

将消息发送到 MyThread 的 Handler 中,然后在 MyThread 的 Handler (子线程中)中将消息发送到 MainActivity 的 threadHander(UI线程中),这样就完成了,子线程更新UI线程的过程

注:这里是为了显示更新UI线程所以多次发送,如果只是为了两个线程间通信,那么 threadHander 是可以不需要的

我们把改后的代码运行一下,并且我添加了一些线程的日志,看效果

thread_handler.gif

从图中我们可以看到,子线程间通信是完全没有问题的,但是我们退出应用再进入应用的时候发现,线程数量在不停的增加,我靠如果打开 100 次岂不是又多了 100 个线程,1000... 次呢,想都不敢想。如何解决呢,往下看。

2、手动调用 Looper 需要手动释放

接着上面继续说,问题出现在那里呢,就是手动调用 Looper.perpare() 和 Looper.loop() 的时候一定要记得在任务完成以后,或是合适的地方要释放掉 looper,要调用 looper.quit(),结束消息队列,进而结束线程。如果不这么做,thread 会长时间存在不销毁这。这就是上面所说的性能问题,不断的开辟线程,线程的开辟是需要开销的,所以我们来解决这一问题

  • 1、我们改造MyThread类
class MyThread extends Thread{
        private Looper mLooper ;
        private Handler mHandler ;

        /**
         * 代码块或是构造方法中开启线程都可以
         */

        public MyThread(){
            Log.e("t所在的线程",this.getName()) ;
            start();
        }
//        {
//            //一实例化就开启线程 代码块
//            start();
//        }
        @Override
        public void run() {
            super.run(); //Handler所在的线程默认是没有looper的(除了Activity)
            Looper.prepare();
            mLooper = Looper.myLooper() ;
            mHandler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    //取得传递过来的 msg
                    String passedChangeTv = (String) msg.obj;
                    //设置显示  这里的Handler运行在子线程中去的,所以不能更新UI
                   // tv.setText(passedChangeTv);
                    threadHander.sendMessage(threadHander.obtainMessage(1,passedChangeTv)) ;
                }
            } ;
            Looper.loop();
        }
         //这里是新添加的方法
        public void exit(){
            if(mLooper!=null){
                mLooper.quit();
                mLooper = null ;
            }
        }

        public Handler getThreadHandler(){
            return mHandler!=null?mHandler:null ;
        }
    }

我们只是在 MyThread 类中添加了一个 exit 方法,其它的都没有变.

  • 2、为了方便测试,我们在 onDestory() 方法中调用
 @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.e("onDestroy",t.getName()+"销毁") ;
            t.exit();
            t=null ;
        }

我们重新跑一下应用程序,来观察结果

remove_thread_handler.gif

从效果图中我们可以看出,当我们手动调用 Looper.prepare() 和 Looper.loop() 的时候创建一个线程,但是们退出应用的时候会退出 Looper,线程会销毁,我们需要你的时候让你存在,不需要的时候让你销毁,这就大大提高了性能。我们看始终激活的线程数量都是5,这是我们所期望的

总结:在子线程中,如果手动为其创建了 Looper,那么在所有的事情完成后应该调用 quit 方法来终止消息循环

Handler 和它兄弟们

Handler 不能单独工作,必须和它的兄弟们一起协同才可以工作。而它的兄弟们就是以下三个

1、Looper

Looper 字面意思是一个轮询器,是用来检测 MessageQueue 中是否有消息,如果有消息则把消息分发出去,没有消息就阻塞在那里直到有消息过来为止。
一个线程对应一个 Looper,一个 Looper 对应一个 MessageQueue.

注意:默认情况下,线程是没有 Looper 的,所以要调用 Looper.prepare() 来给线程创建消息队列,然后再通过,Looper.loop() 来不停(死循环)的处理消息消息队列的消息

2、MessageQueue

是用来存放消息的,通过 Looper.prepare() 来初始化消息队列。MessageQueue 从字面意思来看是一个消息队列,但是内部实现并不是基于队列的。是基于一个单链表的数据结构来维护消息列表的,链表插入和删除优势很明显。

3、Message

传递信息的载体,可以携带一些信息,类似于 Intent,Bundle 一样。两个线程通过 Message 联系在一起。

Handler 源码分析

重点来了,下面我们一起来分析一下Handler的源码

1、sendMessage() 发生了什么

当我们调用 Handler 的 sendMessage 方法的时候到底发生了什么,我们从源码去看一下

 public final boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0);
    }

我们可以看到调用sendMessage方法调用sendMessageDelayed(msg, 0)

public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

而 sendMessageDelayed 方法又调用 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) 方法

 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
         //取得MessageQueue
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        //把消息存放到MessageQueue中
        return enqueueMessage(queue, msg, uptimeMillis);
    }

在这里不是不有些小激动呢,我们看到 sendMessage 最终调用的是 enqueueMessage(queue, msg, uptimeMillis) 这个方法,我们来看看这个方法

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

我勒个插,还在调用,这里代码我们基本上也能看懂,就是不知道msg.target 是个毛线,不过他是 Message 的一个属性,我们进去看一下发现 msg.target 是一个Handler,好我们再看queue.enqueueMessage(msg, uptimeMillis) 方法

 boolean enqueueMessage(Message msg, long when) {
 //msg.target 这么熟悉,我去不就是 Handler 吗,要发送一个消息肯定要有 Handler 呀,不然发个毛毛呢
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        //消息是否正在使用
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
  //消息插入链表头,条件是如果MessageQueue为空,或是消息发送的时刻为0或者消息发送的时刻小于链表头的派发时刻,就把消息插入链表头
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
            //否则就找个合适的位置把消息插到链表中
                 Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

总之这个方法就是把消息插入到消息队列中,我们现在可以归纳了,当调用handler.sendMessage方法的时候做了一件重要的事:
把消息插入到MessageQueue中

2、Looper 该上场了

根据前面所述,我们知道 Looper 的作用是创建消息队列,和处理消息。

  • 1、Looper.prepare() 方法就是用来创建消息队列的,我们看源码
 public static void prepare() {
        prepare(true);
    }
    
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
 }

我们看到 prepare 方法调用 prepare(true) 方法,此方法中我们目前也没有看不懂的语句,无非就是 sThreadLocal 是什么东东,不要紧,我们暂且当它是一个 List 一样用来存取 Looper 的,接下来我们看 new Looper(quitAllowed) 方法

 private Looper(boolean quitAllowed) {
     //初始化消息队列
     mQueue = new MessageQueue(quitAllowed);
     //取得当线线程
     mThread = Thread.currentThread();
 }

看到了吧,确实在上面方法中初始化了 MessageQueue。所以我们这里总结 Looper.prepare() 方法就是创建了一个消息队列

根据上面的分析,我们有了消息队列,也知道 sendMessage 是把消息插入到消息队列中去了,那么如何管理 MessageQueue中 的消息呢,Looper.loop() 该上场了。

  • 2、Looper.loop(): 我们直接看源码
 public static void loop() {
       //取得Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //拿到 MessageQueue
        final MessageQueue queue = me.mQueue;
          ...省略部分代码
        for (;;) { //这里是一个死循环,取出每个消息,然后分发
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
             ... 省略部分代码
             //msg.target(Handler)这里调用 Handler 的dispatchMessage方法
            msg.target.dispatchMessage(msg);
        }
    }

综上代码,我们可以得出结论 Lopper.loop() 方法主要作用是取出 MessageQueue 中消息然后调用 Handler 的 dispatchMessage 方法把消息分发出去(前提是 Message 中要有消息)

接下来我们看 Handler 的 dispatchMessage 方法

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            //如果msg.callback不为空,则调用handleCallback
            handleCallback(msg);
        } else { 否则,mCallback不为空
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //调用handleMessage
            handleMessage(msg);
        }
    }

似乎看到熟悉的方法了,对就是 handleMessage(msg),就是 Handler 的回调方法,就是处理消息的方法。

这样我们大体了解到,Handler的处理流程了,大致如下:

Looper.parpare() ;  //先准备好消息队列

Handler handler = new Handler(){
  @Override
  handleMessage(Message msg){
        //如理发送过来的消息
        ...
        ...
  }
}


Looper.loop() ; //是一个死循环,一直监听着 MessageQueue ,从消息队列中取消息,然后调用 handler. dispatchMessage 方法,最后调用的就是 handler 的 handleMessage(Message msg) 方法来处理消息

那么如何把消息插入到消息队列呢,就靠 Handler 的 sendMessage() 方法了,所以当调用了 Handler 的 sendMessage(),Looper.loop() 监听到 MessageQueue 中有消息,就调用 dispatchMessage 方法分发消息,最终调用 handler 的 handleMessage(Message msg) 方法来处理消息。

Handler的原理如下图所示

handler.png

原创图:转载注明出处

Activity 默认有 Looper

Activity 中的 Looper 从何来

我们知道使用 Handler 的时候,Handler 一定要存在于有Looper.parpare() 和 Looper.loop() 的线程中可是我们都知道 Activity 中使用 Handler 的时候不用写 Looper.parpare() 和 Looper.loop(),但是为什么不用写有没有想过,那么我们来分析一下

Activity 所在线程也叫 UI 线程,为什么叫 UI 线程呢,其实它是通过 ActivityThread 来管理的,我们知道 Android 应用层是用 Java 开发的,那么 Java 肯定是有一个入口方法即 main 方法,main 方法到底在那里呢?
答案就是在 ActivityThread.java 中,我们来看一下源码

public final class ActivityThread {
   
   ... 省略一大坨代码
   final Looper mLooper = Looper.myLooper();
   //H就是一个Handler
   final H mH = new H();
    ... 省略若干代码
   private class H extends Handler {
        省略一大堆
        String codeToString(int code) {
            if (DEBUG_MESSAGES) {
                switch (code) {
                    case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
                    case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
                    case PAUSE_ACTIVITY_FINISHING: return "PAUSE_ACTIVITY_FINISHING";
                    case STOP_ACTIVITY_SHOW: return "STOP_ACTIVITY_SHOW";
                    ...省略大量代码
                 }
            }
            return Integer.toString(code);
        }
        public void handleMessage(Message msg) {
         
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                case RELAUNCH_ACTIVITY: 
                    ...            
                case PAUSE_ACTIVITY:
                    ...
                    break;
                    
                ... 省略部分代码
                
                case RESUME_ACTIVITY:
                    ...
                    break;
                case SEND_RESULT:
                    ...
                    break;
                case DESTROY_ACTIVITY:
                    ...
                    break;
                    
                ... 省略部分代码

                case CREATE_SERVICE:
                    ...
                    break;
                case BIND_SERVICE:
                    ...
                    break;
                case UNBIND_SERVICE:
                    ...
                    break;
                    
                  ... 省略又臭又长的代码   
               }
            if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
        }

       }
       
       public static void main(String[] args) {

         ...省略部分代码

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ...省略部分代码
        
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}  
  

从上面代码中可以隐约的看到 Activity 的 oncreate ,onresume... Service 的 oncreate bindService 等等方法都在这里处理了,再从上面看到了 main 方法并且调用了 Looper.prepareMainLooper(); 和 Looper.loop(); 由此就知道了,为什么我们的 Activity 默认是有 Looper 的。

结论

Activity 叫 UI 线程,其实指的就是 ActivityThread,Activity 是由ActivityThread 管理启动,暂停等,并且 ActivityThread 的入口方法中开启了 Looper.prepareMainLooper() 和 Looper.loop(),所以我们的 Activity 默认就会有一个 Looper,Service 同理

Handler引起的内存泄露

引子

我们先来看一个例子

public class MainActivity extends AppCompatActivity {

    private TextView tv ;

    //在UI线程中
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            ...
            ...
        }
    } ;
  }

乍一看,好像没有什么问题,但是我告诉你,这段代码可能会存在内存泄露的,这是为什么呢?答案是非静态内存部会持有外部类的引用(Java的特性),在这里Handler是一个非静态的内存部类,会隐式的持有 MainActivity 的引用的。

解决办法

解决这个问题的办法就是避免使用非静态内部类。

  static class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
          
      }
  }

如果要操作Activity的一些对象,这里使用弱引用

static class MyHandler extends Handler {
        // WeakReference to the outer class's instance.
        private WeakReference<MyActivity> mOuter;

        public MyHandler(MyActivity activity) {
            mOuter = new WeakReference<MyActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MyActivity outer = mOuter.get();
            if (outer != null) {
                // Do something with outer as your wish.
            }
        }
  }

完整的代码

public class MyActivity extends AppCompatActivity {
    private MyHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new MyHandler(this);
    }

    @Override
    protected void onDestroy() {
        // Remove all Runnable and Message.
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }

    static class MyHandler extends Handler {
        // WeakReference to the outer class's instance.
        private WeakReference<MyActivity> mOuter;

        public MyHandler(MyActivity activity) {
            mOuter = new WeakReference<MyActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MyActivity outer = mOuter.get();
            if (outer != null) {
                // Do something with outer as your wish.
            }
        }
    }
}

当然我们可以把Handler单独写到一个类中,配置 RxBus 或 EventBus来更新UI或执行某些操作,这里就介绍完了 Handler 的相关知识点了

想成为一个牛 B 的人都会点喜欢或分享的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容