Handler的创建Message的处理与Looper的作用——源码分析

Posted Greyson_Guo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Handler的创建Message的处理与Looper的作用——源码分析相关的知识,希望对你有一定的参考价值。

分析源码,总结小过程(源码只打印出重要部分):
一般Hanlder的创建:
//Handler的部分构造方法
public Handler() 
  this(null, false);


public Handler(Callback callback, boolean async) 
  mLooper = Looper.myLooper();
  if (mLooper == null) 
   throw new RuntimeException(
    "Can't create handler inside thread that has not called Looper.prepare()");
  
  mQueue = mLooper.mQueue;
  mCallback = callback;
  mAsynchronous = async;

Handler中有几个重要的全局变量:mLooper、mQueue、mCallback,分别为此Handler所在的线程的Looper对象、消息队列、还有Handler的回调接口(此接口有handleMessage()方法,实现此接口可以替代,必须匿名内部类重写Handler的handleMessage()方法的情况)。
在以上源码中可看出创建Handler的时候会调用Looper的静态方法myLooper():
public static @Nullable Looper myLooper() 
  return sThreadLocal.get();
此方法会去访问Looper对象中的属性ThreadLocal对象的get()方法:
public T get() 
  // Optimized for the fast path.
  Thread currentThread = Thread.currentThread();//获取当前线程
  Values values = values(currentThread);//获取线程对应的ThreadLocal.Values对象

  return (T) values.getAfterMiss(this);//获取Values中的Looper对象
即它会去访问当前线程中的属性ThreadLocal.Values对象,对象中维护了一个一维Object数组,数组顺序交替保存了ThreadLocal和Looper对象,所以可以从整体上看待ThreadLocal,把它当作一个Map对象(Java的ThreadLocal的确维护着一个Map对象),里面存着以ThreadLocal为key,以Looper对象为value的键-值对。最后通过Values的getAfterMiss()方法返回当前线程对应的Looper对象。
总结, Handler在哪个线程中创建,它都会与当前线程的Looper绑定在一起,操作的消息队列也是当前线程与其Looper所维护的消息队列
Looper的创建: 1)首先我们来看一下官方API对Looper的介绍: Looper用来在线程中执行一个消息队列(原文是“message loop”在此翻译为消息队列会比较好译)。线程默认情况都不会有与之关联的消息队列。如果在线程调用prepare(),那么会创建一个Looper与该线程关联,线程就拥有了自己的消息队列。并且必须调用loop()方法到执行消息队列直到队列被停止。 一般用户与消息队列的交互都是通过Handler类,以下是一个典型的例子:
class LooperThread extends Thread 
 public Handler mHandler;
 public void run() 
  Looper.prepare();
  mHandler = new Handler() 
   public void handleMessage(Message msg) 
    // process incoming messages here
   
  ;
  Looper.loop();
 

源码前的分析:当线程有了自己的Looper(也拥有了消息队列),在它的代码就可以创建Handler来与用户交互,即Handler在哪个线程创建,它就属于哪个线程,它发送的消息Message也会加入它所属的线程的消息队列中。如果线程没有Looper就创建Handler类对象会产生"Can't create handler inside thread that has not called Looper.prepare()"异常。自己在线程中维护一个Looper要注意可能必须调用quit()方法来关闭Looper。

2)再来看源码。Looper的构造方法和一些重要的全局变量:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class

final MessageQueue mQueue;
final Thread mThread;

private Looper(boolean quitAllowed) 
  mQueue = new MessageQueue(quitAllowed);
  mThread = Thread.currentThread();

新建消息队列并保存当前线程到全局变量mThread中。因为此方法是私有,所以只能在内部实例化,这就用到了我们常见的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));

由源码可知,创建Looper时会先尝试从ThreadLocal对象中获取,如果ThreadLocal中还没保存Looper,就会新建一个Looper对象并保存到ThreadLocal(其中的Thread.Values的数组中)中, 这就保证了一个线程中只有一个Looper当线程调用了prepare()方法,Looper会将当前线程的对象保存到mThread变量中,即绑定了当前创建它的线程。线程有了自己的Looper和MessageQueue后,必须调用Looper.loop()方法(源码请看“Handler的消息处理”),让消息队列开始运转
Looper还有个重要的方法(对系统而言):
public static void prepareMainLooper() 
  prepare(false);
  synchronized (Looper.class) 
  if (sMainLooper != null) 
  throw new IllegalStateException("The main Looper has already been prepared.");
  
  sMainLooper = myLooper();
  

它将当前线程的Looper设置为整个应用的主要Looper(他属于UI线程)。应用的主要Looper是由安卓环境创建的,所以开发者不需要自己去调用他。
Handler的消息处理
public final boolean sendMessage(Message msg)

  return sendMessageDelayed(msg, 0);


public final boolean sendMessageDelayed(Message msg, long delayMillis)

  if (delayMillis < 0) 
  delayMillis = 0;
  
  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);


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 
  msg.target = this;//将消息执行完成要回调的handleMessage()方法所属的对象设置为当前对象
  if (mAsynchronous) 
  msg.setAsynchronous(true);
  
  return queue.enqueueMessage(msg, uptimeMillis);

平时我们调用的sendMessage(Message msg)方法经过一些参数的判断和处理后,最终到了enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 方法中来实现功能;在这里,将我们想发送的Message对象的target设为当前Handler对象,这就是为什么MessageQueue中有很多Message,但执行时可以回调到正确的Handler对象的原因。最后用了MessageQueue的enqueueMessage(msg, uptimeMillis)方法将消息推入队列中。
Looper.prepare()调用后,都必须调用Looper.loop()方法以运转消息队列,这样Handler发的消息才可以被处理:
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);//派遣任务,让Handler处理消息内容

  msg.recycleUnchecked();//回收消息对象,标为“非在用”状态
  

从上面的源码可以看出,消息队列一直无止境地循环获取消息,而消息的处理则是Message对象中的target,处理任务又回到了Handler中:
public void dispatchMessage(Message msg) 
  if (msg.callback != null) //如果有线程任务,则执行线程
  handleCallback(msg);
   else 
  if (mCallback != null) 
  if (mCallback.handleMessage(msg)) 
  return;
  
  
  handleMessage(msg);
  


private static void handleCallback(Message message) 
  message.callback.run();

Handler会优先判断是否有实现了Handler.CallBack接口的类对象mCallback,如果没有直接回调Handler的handleMessage()方法;有则回调该对象的handleMessage()方法,并且判断消息是否继续传递到Handler的handleMessage()方法。

以上是关于Handler的创建Message的处理与Looper的作用——源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Handler的创建Message的处理与Looper的作用——源码分析

android消息处理机制之2handler与looper,MessageQueue:的关系

Android Handler消息机制03-Message源码学习

Handler类和Handler,Loop,MessageQueue的工作原理

Handler类和Handler,Loop,MessageQueue的工作原理

Handler类和Handler,Loop,MessageQueue的工作原理