全面解析Android之Handler机制
来源:互联网 发布:删除不了windows.old 编辑:程序博客网 时间:2024/06/07 06:12
这篇文章将带着大家全方位去理解Handler机制,我们先将涉及几个重要的类列出来:
Handler: 发送消息到MessageQueue;Looper循环处理消息后,交给Handler的handleMessage(..)进行处理。
MessageQueue: 顾名思义,消息队列,用于存储消息数据。以队列先进先出的特点,对外提供消息的插入和删除操作;但是,MessageQueue数据结构实际是单链表。
Looper: 顾名思义,循环消息。当MessageQueue中有消息时,Looper循环不断的处理消息;当MessageQueue中消息为空时,Looper阻塞在原地。
ThreadLocal: 不是线程,是一个可以存储数据的类。ThreadLocal在取值操作的时候会传入一个线程引用,使ThreadLocal在不同线程中取出的变量引用的值不同。
Message: 顾名思义,消息。用于封装消息的数据,字段what表示消息唯一标示,obj,arg1,arg2表示存储在消息中的数据。
ActivityThread: 主线程,又UI线程。其在创建的时候,就会初始化线程中的Looper。
接下里,先说明下Handler机制为什么要存在?它能解决什么问题?
Android做一个耗时操作的时候,需要在子线程中进行处理,否则会出现ANR;在执行完耗时操作后,需要将数据用于UI的刷新,操作UI是要在UI线程中完成,否则系统会报错。
而,Handler机制就可以实现线程间的通信,将数据封装到消息中,并将消息发送给UI线程,取出消息中数据刷新UI。
后面我们要分析的是:消息是如何通过MessageQueue,Looper,最终实现消息从子线程传递到UI线程的?
第一步,将子线程中获取的数据封装在Message中,第一步是要发送Message到MessageQueue,常调用的方法有:handler.sendMessage(..),message.sendToTarget()。
sendMessage的源码如下:
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }实际上最终调用的是这样一个方法,如下:
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); }
进入方法enqueueMessage(..),如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }调用的是MessageQueue的enqueueMessage(..),将消息插入到消息队列中,继续查看MessageQueue源码如下:
boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } if (msg.target == null) { throw new AndroidRuntimeException("Message must have a target."); } boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false; } msg.when = when; Message p = mMessages; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { //将消息插入到消息队列里,实际是对链表进行插入元素的操作 // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); 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; } } if (needWake) { nativeWake(mPtr); } return true;}
其实在handler.sendMessage(..)方法里,是有如下这样的一段注释的,大致意思是:将消息放到消息队列的末尾,并在handler所在线程被接受,handleMessage进行处理。
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
message.sendToTarget()里面实际调用就是handler.sendMessage(..),感兴趣的同学可以查阅源码去验证,这里不作阐述。
第二步:通过前面我们知道在ActivityThread创建的时候,就已经初始化Looper了。其实,在创建Handler的实例时,如果handler所在线程没有初始化Looper,那么会报一个异常。这里小王君做过一个测试,在子线程里创建Handler实例,没有初始化Looper,报了一个如下异常:
05-28 15:13:50.300: E/AndroidRuntime(2932): FATAL EXCEPTION: Thread-51105-28 15:13:50.300: E/AndroidRuntime(2932): Process: com.example.test, PID: 293205-28 15:13:50.300: E/AndroidRuntime(2932): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()05-28 15:13:50.300: E/AndroidRuntime(2932): at android.os.Handler.<init>(Handler.java:208)05-28 15:13:50.300: E/AndroidRuntime(2932): at android.os.Handler.<init>(Handler.java:122)05-28 15:13:50.300: E/AndroidRuntime(2932): at com.example.test.MainActivity$1$1.<init>(MainActivity.java:25)05-28 15:13:50.300: E/AndroidRuntime(2932): at com.example.test.MainActivity$1.run(MainActivity.java:25)05-28 15:13:50.602: E/(2932): appName=com.example.test, acAppName=/system/bin/surfaceflinger05-28 15:13:50.602: E/(2932): 0Can't create handler inside thread that has not called Looper.prepare(),意思很明显:无法在一个没有调用Looper.prepare()所在的线程里创建handler。我们可以查阅Handler构造函数验证报错原因,留给感兴趣的同学研究,这里不作阐述。
这就间接验证了:为啥我们在主线程里随意创建handler对象,并没有报错的原因,因为主线程在创建的时候就初始化了Looper。那么在子线程中该如何创建Handler呢?查阅Looper源码有这样一段注释,很好告知处理方式,注释如下:
/** * Class used to run a message loop for a thread. Threads by default do * not have a message loop associated with them; to create one, call * {@link #prepare} in the thread that is to run the loop, and then * {@link #loop} to have it process messages until the loop is stopped. * * <p>Most interaction with a message loop is through the * {@link Handler} class. * * <p>This is a typical example of the implementation of a Looper thread, * using the separation of {@link #prepare} and {@link #loop} to create an * initial Handler to communicate with the Looper. * * <pre> * 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(); * } * }</pre> */
接下来,介绍下Looper的两个很关键的方法:Looper.prepare(),Looper.loop();
Looper.prepare(),用于初始化Looper,源码如下:
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不是简单的创建对象,还需要存储Looper对象,操作的类是ThreadLocal。前面有ThreadLocal类的解释,目的就是为了handler可以存入和取出所在线程的Looper。很显然每个线程的Looper是不同的,我们也可以用Hash表结构的类存取Looper,但是系统没有这么做,而是提供一个ThreadLocal解决需求。接下来进入ThreadLocal的源码查看方法,存入set(...),取出get()。ThreadLocal的set(..)源码,如下:
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }
Values values(Thread current) { return current.localValues; }
Values initializeValues(Thread current) { return current.localValues = new Values(); }
void put(ThreadLocal<?> key, Object value) { cleanUp(); // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == key.reference) { // Replace existing entry. table[index + 1] = value; return; } if (k == null) { if (firstTombstone == -1) { // Fill in null slot. table[index] = key.reference; table[index + 1] = value; size++; return; } // Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; return; } // Remember first tombstone. if (firstTombstone == -1 && k == TOMBSTONE) { firstTombstone = index; } } }分析:如果values为空,执行代码current.localValues = new Values(),values = current.localValues,current即Thread。put(...)方法在ThreadLocal的静态内部类-Values类里面,核心代码是调用values.put(ThreadLocal,Looper),变量values对象持有当前Thread的引用(values = current.localValues)。
put方法就是将Looper对象放入一个数组里(代码里变量value就是传入的Looper对象),由于每个线程都对应一个新的Values对象,因此存入Looper是在不同的数组中操作的。
于是,ThreadLocal类就可以正确的存入线程所在Looper的对象,而不会乱存其他线程的Looper。
总结:Looper.prepare()里面不仅创建了Looper对象,还使用ThreadLocal类实现了Looper于所在线程中的存入。
继续分析Looper.loop(),大家主要看下在源码中的一些注释,源码如下:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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."); }//获取消息队列的对象 final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();//通过无限循环不断的抽取消息队列里的消息 for (;;) { //消息队列里取出下一个消息 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } //对消息msg进行转发处理 msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); } }
分析:
1,final Looper me = myLooper(),源码如下:
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }于是查看ThreadLocal的get方法源码,如下:
public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); }该方法取出所在线程的Looper对象。先取出Values对象里的数组table(Object[] table = values.table),执行一个&运算得到角标index,存入的数据在角标index + 1位置,于是looper = table[index + 1]。
2,final MessageQueue queue = me.mQueue;消息队列mQueue的实例,在创建Looper对象时创建,也就是我们调用Looper.prepare()时创建。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }3,Message msg = queue.next();再次进入到MessageQueue类中查看next方法,源码如下:
Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(mPtr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuiting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }取出消息队列中消息return,并在队列中移除该消息,核心代码如下:
// Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); msg.markInUse(); return msg;4,取出消息之后,调用方法msg.target.dispatchMessage(msg)进行转发处理,target就是Handler对象,进入Handler源码查看,如下:
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }由代码可知,处理消息的方式有三种:
4.1, msg.callback != null时,调用方法handleCallback(msg),该方法里就一行代码:message.callback.run();callback就是一个Message里的Runnable接口,这里是回调接口的run方法。
在调用handler.post(runnable)方法发送消息时,callback不为空,处理消息的代码放在run方法里。handler.post(runnable)源码,如下:
/** * Causes the Runnable r to be added to the message queue. * The runnable will be run on the thread to which this handler is * attached. * * @param r The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
注意:方法上有这样一条注释,Causes the Runnable r to be added to the message queue.The runnable will be run on the thread to which this handler is attached。说的意思就是runnable 的执行是在handler所在线程里,我们这里就是UI线程。
getPostMessage(r)就是创建一个callback变量的值为r的消息,查看其源码,如下:private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }sendMessageDelayed就是前面讲到发送消息到消息队列时调用的方法,这里不作阐述。
4.2,mCallback != null时,mCallback 是接口Handler$Callback的引用,执行mCallback.handleMessage(msg)处理消息。Handler$Callback源码,如下:
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { public boolean handleMessage(Message msg); }注释大致意思是:当去实例化一个Handler对象时,如果不想派生其子类,可以用Handler h = new Handler(mCallback)创建对象。
4.3:当以上两个接口都没有实例化时,调用handleMessage(msg)处理消息,源码如下:
/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }我们发现这个方法里面是空的,什么也不做,查看注释发现Android要求在创建Handler子类时,必须重写该方法接受消息并处理。当然,这是开发中最常用的处理消息方式。
总结下handler机制,大体是这样的:
1,首先将子线程中获取数据封装在Message对象中,;
2,通过handler的sendMessage,或post方法将消息发送到消息队列里,最终都走得是sendMessageAtTime这条路,sendMessageAtTime里面调用了MessageQueue的enqueueMessage方法,完成消息插入到消息队列的操作;
3,每当我们创建Handler实例时,就会初始化Looper对象,是调用Looper.prepare()创建Looper对象(同时MessageQueue对象创建在Looper构造函数中完成),并将Looper对象通过ThreadLocal类存入到线程中;
4,Looper从消息队列里不断取出消息,是调用Looper.loop(),里面调用了MessageQueue的next方法,将消息取出来;
5,取出来的消息通过handle的dispatchMessage(..)转发处理,具体处理方式有三种,如上所示。
注:学习到这里,可能都有一个这样的疑问:哇~程序执行是如何从子线程慢慢到主线程的?
对,就是调用Handler所在主线程的Looper.loop()方法,抽取到消息处理的时候,代码就执行到主线程了,是不是很神奇,这也说明Looper对于线程间通信简直太重要了!
下面将粘贴王小君实际开发中,利用handler机制实现线程通信的三个不同example,如下:
4.1:
private Handler h = new Handler();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(){public void run() {SystemClock.sleep(6000);h.post(new Runnable() {@Overridepublic void run() {Toast.makeText(getApplicationContext(), "run方法是在主线程里执行的", 0).show();}});};}.start();}:4.2,:
private static final int SHOW_UI = 0;private Handler h = new Handler(new Handler.Callback(){@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case SHOW_UI:Toast.makeText(getApplicationContext(), "不想派生handler的子类处理消息", 0).show();break;default:break;}return false;}});@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(){public void run() {SystemClock.sleep(6000);Message msg = Message.obtain();msg.what = SHOW_UI;h.sendMessage(msg);};}.start();}
4.3:
private static final int SHOW_UI = 0;Handler h1 = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case SHOW_UI:Toast.makeText(getApplicationContext(), "最常使用处理消息的方式", 0).show();break;default:break;}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(){public void run() {SystemClock.sleep(6000);Message msg = Message.obtain();msg.what = SHOW_UI;h.sendMessage(msg);};}.start();}
最后,辛苦大家能看到这里,文章有不够准确的地方,期待大家能在下面评论中指出,一起学习进步!!!
———小王君 (*^__^*)
- 全面解析Android之Handler机制
- Android机制之Handler解析
- Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue
- Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)
- Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)
- Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)
- Android之handler机制
- Android之Handler机制
- Android之Handler机制
- Android之Handler机制
- Android中Handler机制解析
- Android Handler机制 源码解析
- Android Handler机制完全解析
- Android Handler机制源码解析
- android handler机制源码解析
- android 机制之handler机制
- Handler异步通信机制全面解析(包含Looper、Message Queue)
- android消息机制 之Handler
- Day3
- linux 下C操作SQLite(增删改查)
- Python3 配置文件(configparser)
- Week Training: 413 Arithmetic Slices
- C++递归算法之放苹果
- 全面解析Android之Handler机制
- POJ-3176 Cow Bowling
- java反射机制
- JZOJ1517.2017.05.27【提高组】模拟赛C组 T1背包问题
- 存储类,作用域,生命周期和链接属性
- Linux命令---- mv移动或重命名文件/目录
- CentOS 7 搭建ngrok服务器,从此不再需要花生壳
- js原生放大镜
- java基础技术知识点总结——JDBC