android 结合源码深入剖析Handler机制原理

android 结合源码深入剖析Handler机制原理

       Handler机制原理已经被许多大佬写透的东西了,这里我们为什么还要说呢?因为对于许多初学者来说,也只是会使用Handler而已,对于其机制,也只是大概知道,今天我们来利用源码深入剖析其原理。

首先我们模拟一下工厂中的情景:

Handler:消息的处理者,工厂中流水线的工人。
Message:
系统传递的消息,工厂中流水线上的产品。
MessageQueue:
消息队列,工厂中流水线上的传送带。

Looper:发动机,工厂中使流水线的传送带运动的发动机。

来看源码分析,Handler主程序入口其实是在ActivityThread的main方法中进行的:

ActivityThread.java:

[html]  view plain  copy
  1. /在android应用程序的入口其实在ActivityThread的main方法  
  2.     //在这里,主线程会创建一个Looper对象。  
  3.     Looper.prepareMainLooper();    
  4.   
  5. //执行消息循环  
  6.     Looper.loop();  

main函数中,Looper调用了prepareMainLooper(),  (准备主要消息泵我们再进去Looper看看。

[html]  view plain  copy
  1. public static void prepareMainLooper() {  
  2.     //在主线程中,其默认初始化一个Looper对象,因此我们在主线程的操作中是不需要自己去调prepare()。  
  3.     prepare(false);  
  4.     synchronized (Looper.class) {  
  5.         //这里先进行判断,在主线程是否已经存在Looper了,  
  6.         // 避免我们手动去调用prepareMainLooper(),因为这个是给程序入口初始化的时候系统会自动调用的  
  7.         if (sMainLooper != null) {  
  8.             throw new IllegalStateException("The main Looper has already been prepared.");  
  9.         }  
  10.         //设置全局变量,主线程的looper  
  11.         sMainLooper = myLooper();  
  12.     }  
  13. }  

注意这个函数的注释,大概意思是:在主线程创建一个looper,是这个主线程的主looper,当这个app在初始化的时候就会自行创建,因此这个函数不是给你们调用的,是给系统自身在程序创建的时候调用的。

继续往下看,有个prepare(boolean)函数,我们去看看这个到底是用来干什么的。

Looper.java:

[html]  view plain  copy
  1. private static void prepare(boolean quitAllowed) {  
  2.     //先判断当前线程是否已经存在Looper了,如果存在,不允许设置新的Looper对象,一个线程只允许存在一个Looper  
  3.     if (sThreadLocal.get() != null) {  
  4.         throw new RuntimeException("Only one Looper may be created per thread");  
  5.     }  
  6.     //在当前线程中,创建新的Looper对象,并绑定当前线程  
  7.     sThreadLocal.set(new Looper(quitAllowed));  
  8. }  

我们看到了sThreadLocal,我们先看看这个sThreadLocalLooper是干什么用的。

[html]  view plain  copy
  1. //sThreadLocal在Looper中作为全局变量,用于保存每个线程中的数据,可以看做是容器  
  2. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  

Looper中,sThreadLocal作为一个全局变量,sThreadLocal其实是保存Looper的一个容器,我们继续往ThreadLocalgetset进行分析。

[html]  view plain  copy
  1. public T get() {  
  2.     //获取当前线程保存的对象--通过get函数来获取Looper对象  
  3.     Thread t = Thread.currentThread();  
  4.     ThreadLocalMap map = getMap(t);  
  5.     if (map != null) {  
  6.         ThreadLocalMap.Entry e = map.getEntry(this);  
  7.         if (e != null)  
  8.             return (T) e.value;  
  9.     }  
  10.     return setInitialValue();  
  11. }  
  12.   
  13. public void set(T value) {  
  14.     //把当前的looper保存到当前线程中  
  15.     Thread t = Thread.currentThread();  
  16.     ThreadLocalMap map = getMap(t);  
  17.     if (map != null)  
  18.         map.set(this, value);  
  19.     else  
  20.         createMap(t, value);  
  21. }  

关键的代码:
Thread t=Thread.currentThread();

也就是说,我们的Looper对象分别保存在相对应的线程中。我们看回来我们的prepare(boolean)函数:

looper.java:

[html]  view plain  copy
  1. private static void prepare(boolean quitAllowed) {  
  2.     //先判断当前线程是否已经存在Looper了,如果存在,不允许设置新的Looper对象,一个线程只允许存在一个Looper  
  3.     if (sThreadLocal.get() != null) {  
  4.         throw new RuntimeException("Only one Looper may be created per thread");  
  5.     }  
  6.     //在当前线程中,创建新的Looper对象,并绑定当前线程  
  7.     sThreadLocal.set(new Looper(quitAllowed));  
  8. }  

Looper.prepare(boolean)的作用就是创建一个Looper对象,并与当前线程绑定在一起。在代码中,首先判断当前线程是否已经存在looper,如果不存在则创建新的looper并且绑定到当前的线程上。

再看回之前的代码:
looper.java:

[html]  view plain  copy
  1. public static void prepareMainLooper() {  
  2.     //在主线程中,其默认初始化一个Looper对象,因此我们在主线程的操作中是不需要自己去调prepare()。  
  3.     prepare(false);  
  4.     synchronized (Looper.class) {  
  5.         //这里先进行判断,在主线程是否已经存在Looper了,  
  6.         // 避免我们手动去调用prepareMainLooper(),因为这个是给程序入口初始化的时候系统会自动调用的  
  7.         if (sMainLooper != null) {  
  8.             throw new IllegalStateException("The main Looper has already been prepared.");  
  9.         }  
  10.         //设置全局变量,主线程的looper  
  11.         sMainLooper = myLooper();  
  12.     }  
  13. }  

分别看一下sMainLooper是什么,myLooper()又是什么?
Looper.java:

 
 
[html]  view plain  copy
  1. //保存一个主线程的looper  
  2. private static Looper sMainLooper;  // guarded by Looper.class  
  3.   
  4. public static Looper myLooper() {  
  5.     //使用当前线程的looper  
  6.     return sThreadLocal.get();  
  7. }  
 
 

sMainLooper在Looper做为一个全局变量,保存主线程绑定的looper,myLooper()则是获取当前线程绑定的Looper。在prepareMainLooper()中,在主线程中创建一个新的Looper,并且绑定主线程中,同时把这个主线程的looper赋值给sMainLooer这个全局变量。

ActivityThread.java:


[html]  view plain  copy
  1.  public static void main(String[] args) {  
  2.         ......  
  3.         //在android应用程序的入口其实在ActivityThread的main方法  
  4.         //在这里,主线程会创建一个Looper对象。  
  5.         Looper.prepareMainLooper();  
  6.   
  7.         ......  
  8.         ......  
  9.         ......          
  10.         //执行消息循环  
  11.         Looper.loop();  
  12.   
  13.         throw new RuntimeException("Main thread loop unexpectedly exited");  
  14.     }  
  15. }  

在应用程序ActivityThread.main入口中,系统除了调用Looper.prepareMainLooper,而且在最后还调用了Looper.loop(),这个函数有什么?大家脑补一下,工厂里的流水线上,除了有传送带外,如果你不让它动起来,那传送带也没什么作用,那么Looper.loop的作用就是让这个传送带动起来,也就是我们的让我们的消息队列动起来。

Looper.java:

[html]  view plain  copy
  1. /**  
  2.  * 调用此函数用于启动消息队列循环起来,作用相当于工厂流水线中的传送带的开关,  
  3.  * 只有把开关打开,传送带才跑起来  
  4.  * Run the message queue in this thread. Be sure to call  
  5.  * {@link #quit()} to end the loop.  
  6.  */  
  7. public static void loop() {  
  8.     //先进行判断当前线程是否有绑定looper  
  9.     final Looper me = myLooper();  
  10.     if (me == null) {  
  11.         throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  12.     }  
  13.     //获取这个looper的消息队列  
  14.     final MessageQueue queue = me.mQueue;  
  15.   
  16.     // Make sure the identity of this thread is that of the local process,  
  17.     // and keep track of what that identity token actually is.  
  18.     Binder.clearCallingIdentity();  
  19.     final long ident = Binder.clearCallingIdentity();  
  20.   
  21.     //循环通过消息队列来获取消息  
  22.     for (; ; ) {  
  23.         Message msg = queue.next(); // might block  
  24.         if (msg == null) {  
  25.             // No message indicates that the message queue is quitting.  
  26.             return;  
  27.         }  
  28.   
  29.         // This must be in a local variable, in case a UI event sets the logger  
  30.         final Printer logging = me.mLogging;  
  31.         if (logging != null) {  
  32.             logging.println(">>>>> Dispatching to " + msg.target + " " +  
  33.                     msg.callback + ": " + msg.what);  
  34.         }  
  35.   
  36.         final long traceTag = me.mTraceTag;  
  37.         if (traceTag != 0) {  
  38.             Trace.traceBegin(traceTag, msg.target.getTraceName(msg));  
  39.         }  
  40.         try {  
  41.             //关键点,这里的msg.target也就是hanlder.看回代码hanlder.enqueueMessage()  
  42.             msg.target.dispatchMessage(msg);  
  43.         } finally {  
  44.             if (traceTag != 0) {  
  45.                 Trace.traceEnd(traceTag);  
  46.             }  
  47.         }  
  48.   
  49.         if (logging != null) {  
  50.             logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  51.         }  
  52.   
  53.         // Make sure that during the course of dispatching the  
  54.         // identity of the thread wasn't corrupted.  
  55.         final long newIdent = Binder.clearCallingIdentity();  
  56.         if (ident != newIdent) {  
  57.             Log.wtf(TAG, "Thread identity changed from 0x"  
  58.                     + Long.toHexString(ident) + " to 0x"  
  59.                     + Long.toHexString(newIdent) + " while dispatching to "  
  60.                     + msg.target.getClass().getName() + " "  
  61.                     + msg.callback + " what=" + msg.what);  
  62.         }  
  63.         //最后回收这个message  
  64.         msg.recycleUnchecked();  
  65.     }  
  66. }  

这一段代码比较长,我们挑有中文注释的来看,先判断当前的线程是否存在looper,如果存在获取保存在Looper的消息队列messagequeue,然后无限循环这个消息队列来获取message,注意我们留到了一段代码:

[html]  view plain  copy
  1. //关键点,这里的msg.target也就是hanlder.看回代码hanlder.enqueueMessage()  
  2. msg.target.dispatchMessage(msg);  

msg.target其实就是我们的handler,无论是handler通过post或者sendEmptyMessage,最终都会调用到调到这个enqueueMessage(),在这里会将handler赋值到msg.target.

[html]  view plain  copy
  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  2.     //在message中放一个标记  
  3.     msg.target = this;  
  4.     if (mAsynchronous) {  
  5.         msg.setAsynchronous(true);  
  6.     }  
  7.     //在这里把消息放到队列里面去  
  8.     return queue.enqueueMessage(msg, uptimeMillis);  
  9. }  

既然Looper中的loop()调用了msg.target.dispatchMessage,我们就看看HandlerdispatchMessage是如何进行处理这个msg的。

Handler.java:

[html]  view plain  copy
  1. public void dispatchMessage(Message msg) {  
  2.     //这里先判断callback是否为空  
  3.     // callback就是我们使用handler.post(Runnable r)的入参runnable  
  4.     if (msg.callback != null) {  
  5.         handleCallback(msg);  
  6.     } else {  
  7.         //如果hanlder的入参callback不为空,优先处理  
  8.         if (mCallback != null) {  
  9.             //如果回调返回true.则拦截了handler.handleMessage的方法  
  10.             if (mCallback.handleMessage(msg)) {  
  11.                 return;  
  12.             }  
  13.         }  
  14.         //这就是为什么我们使用hanlder的时候,需要重写handleMessage的方法  
  15.         handleMessage(msg);  
  16.     }  
  17. }  

在dispatchMessage函数中,意思就是分发这个消息,在代码中先判断msg.callback是否为空,msg.callback是什么?其实就是handler.post中的runnable对象,通俗的来说就是handler如果有post操作的,就处理post的操作,我们在看看handlerCallback这个函数。
Handler.java:

[html]  view plain  copy
  1. private static void handleCallback(Message message) {  
  2.     message.callback.run();  
  3. }  

很简单,就一行代码,我们看到了熟悉的run方法,这个不就是我们使用post的时候传进去的Runnbale对象的run方法吗?;;

[html]  view plain  copy
  1. /**  
  2.  * 模拟开始  
  3.  */  
  4. private void doSth() {  
  5.     //开启个线程,处理复杂的业务业务  
  6.     new Thread(new Runnable() {  
  7.         @Override  
  8.         public void run() {  
  9.             //模拟很复杂的业务,需要1000ms进行操作的业务  
  10.             ......  
  11.             handler.post(new Runnable() {  
  12.                 @Override  
  13.                 public void run() {  
  14.                     //在这里可以更新ui  
  15.                     mTv.setText("在这个点我更新了:" + System.currentTimeMillis());  
  16.                 }  
  17.             });  
  18.         }  
  19.     }).start();  
  20. }  

我们回到handler.dispatchMessage(Message),如果不是通过post那么callback就为空,我们看到了一个mCallback变量,我们看看这个Callback的定义:

[html]  view plain  copy
  1. public interface Callback {  
  2.     public boolean handleMessage(Message msg);  
  3. }  
  4. public Handler(Callback callback) {  
  5.     this(callback, false);  
  6. }  

我们可以通过实现这个接口,并作为一个参数传进去Handler来达到处理这个消息的效果。

Handler.java:

[html]  view plain  copy
  1. public void dispatchMessage(Message msg) {  
  2.     //这里先判断callback是否为空  
  3.     // callback就是我们使用handler.post(Runnable r)的入参runnable  
  4.     if (msg.callback != null) {  
  5.         handleCallback(msg);  
  6.     } else {  
  7.         //如果hanlder的入参callback不为空,优先处理  
  8.         if (mCallback != null) {  
  9.             //如果回调返回true.则拦截了handler.handleMessage的方法  
  10.             if (mCallback.handleMessage(msg)) {  
  11.                 return;  
  12.             }  
  13.         }  
  14.         //这就是为什么我们使用hanlder的时候,需要重写handleMessage的方法  
  15.         handleMessage(msg);  
  16.     }  
  17. }  

最后一行代码中,我们看到了熟悉的handleMessage,这不就是我们经常handler.handlerMessage的方法吗?

但注意之前我们所看到的,如果我们mCallback.handlerMessage(msg)返回为true的话,这样就不交给handler.handleMessage处理了。

我们继续看回来我们的Looper.loop()

Looper.java:

[html]  view plain  copy
  1. /**  
  2.  * 调用此函数用于启动消息队列循环起来,作用相当于工厂流水线中的传送带的开关,  
  3.  * 只有把开关打开,传送带才跑起来  
  4.  * Run the message queue in this thread. Be sure to call  
  5.  * {@link #quit()} to end the loop.  
  6.  */  
  7. public static void loop() {  
  8.     .....  
  9.     .....  
  10.     //循环通过消息队列来获取消息  
  11.     for (; ; ) {  
  12.         ......  
  13.         //最后回收这个message  
  14.         msg.recycleUnchecked();  
  15.     }  
  16. }  

在无限循环每个消息的时候,除了调用handler.dispatchMessage,最后还会调用msg.recycleUnchecked()进行回收这个消息

 

总结:

1.为什么在主线程中创建Handler不需要我们调用Looper.prepare().因为在程序的入口中系统会调用Looper.prepareMainLooper()来创建,并且让其主线程的Looper启动起来。如果我们在子线程创建handler,需要手动创建looper并且启动。

2.每一个线程只能存在一个Looper, Looper有一个全局变量sThreadLocal用来保存每一个线程的looper,通过get、set进行存取looper。

3.Handler可以通过通过post或者sendMessage进行发送消息,因为其最终会调用sendMessageDelayed,我们可以通过runnable方式或者重写handleMessage进行消息的处理,当然如果通过handler.sendMessage(msg)的方式的话,我们可以实现Callback接口达到消息的处理。

4.为什么不能在子线程更新UI?其实更准确的来说应该是UI只能在创建UI的线程中进行更新,也就是主线程,如果子线程创建UI,其可以在子线程进行更新。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值