Handler 的那些事

4,495 阅读10分钟

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

转载请标明出处: githubchen001.github.io/2016/05/23/… 本文来自:【JunJun的博客】

什么是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的实例都会绑定到创建它的线程中。

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常用的使用方式

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

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

解决方法:

  • 1、定义Handler并重写handleMessage方法

   private Handler mHandler = new Handler(){
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           switch (msg.what){
               case 1:
                   String passedChangeTv = (String) msg.obj;
                   tv.setText(passedChangeTv);
                   break ;
           }
       }
   } ;
   

       new Thread(new Runnable() {
           public void run() {
                               * 1 请求服务器                * 2 解析数据                * 3 获取数据                */
               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() {
       mtimeTaskHandler.postDelayed(new Runnable() {
           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(){
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           switch (msg.what){
               case 1:
                   int passedChangeTv = (int) msg.obj;
                   tv.setText(passedChangeTv+"");
                   preSecondGetServer() ;
                   break ;
           }
       }
   } ;
   private int i ;
   private void preSecondGetServer() {
       new Thread(new Runnable() {
           public void run() {
               i++ ;
               Message message = new Message() ;
               message.what = 1 ;
               message.obj = i ;
           preSecondHandler.sendMessageDelayed(message,1000) ;
           }
       }).start();
       

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

3、两个子线程进行通讯

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


private Handler threadHandler ;
class MyThread extends Thread{
      {
          start();
      }
      public void run() {
          super.run(); 
          Looper.prepare();
          threadHandler = new Handler(){
              public void handleMessage(Message msg) {
                  super.handleMessage(msg);
                  String passedChangeTv = (String) msg.obj;
                  tv.setText(passedChangeTv);
              }
          } ;
          Looper.loop();
      }
  }
  

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

private void threadHandlerTask() {
        new Thread(new Runnable() {
            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来通信。

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 queue = mQueue;
       if (queue == null) {
           RuntimeException e = new RuntimeException(
                   this + " sendMessageAtTime() called with no mQueue");
           Log.w("Looper", e.getMessage(), e);
           return false;
       }
       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) {
       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) {
               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; 
               prev.next = msg;
           }
           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()该上场了。

public static void loop() {
       final Looper me = myLooper();
       if (me == null) {
           throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
       }
       final MessageQueue queue = me.mQueue;
	  ...省略部分代码
       for (;;) { 
           Message msg = queue.next(); 
           if (msg == null) {
               return;
           }
	     ... 省略部分代码
           msg.target.dispatchMessage(msg);
       }
   }
   

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

接下来我们看Handler的dispatchMessage方法

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

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

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

Looper.parpare() ;  
Handler handler = new Handler(){
  handleMessage(Message msg){
  		...
  		...
  }
  }
Looper.loop() ; 

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

Handler的原理如下图所示

ShowImage

Activty默认有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中的,我们来看一下源码

public final class ActivityThread {
   ... 省略一大坨代码
   final Looper mLooper = Looper.myLooper();
   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("");
        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 ;
    private Handler mHandler = new Handler(){
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
    		...
    		...
        }
    } ;
  }
  

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

解决办法

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

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

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

static class MyHandler extends Handler {
        private WeakReference mOuter;
        public MyHandler(MyActivity activity) {
            mOuter = new WeakReference(activity);
        }
        public void handleMessage(Message msg) {
            MyActivity outer = mOuter.get();
            if (outer != null) {
            }
        }
  }
  

完整的代码

public class MyActivity extends AppCompatActivity {
    private MyHandler mHandler;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new MyHandler(this);
    }
    protected void onDestroy() {
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
    static class MyHandler extends Handler {
        private WeakReference mOuter;
        public MyHandler(MyActivity activity) {
            mOuter = new WeakReference(activity);
        }
        public void handleMessage(Message msg) {
            MyActivity outer = mOuter.get();
            if (outer != null) {
            }
        }
    }
    }

当然我们可以把Handler单独写到一个类中,配置RxBus或EventBus来更新UI或执行某些操作