Android异步消息之Looper、Handler、Message、HandlerThread的关系
2015-08-04 10:22
686 查看
转载请注明 /article/1462868.html
刚开始接触Android的时候,就碰到Handler了,然后大概知道这个类是用来异步更新UI的。但是事实上,Handler的功能远远不止于此。为了能好好的使用它,我觉得还是要仔细研究下它和与它相关的几个类,并总结一下。
1. Looper
Looper的作用是绑定当前所在的线程thread,并创建一个MessageQueue,不断循环从MessageQueue读取消息,然后消息交给target(Handler)处理。
2. Handler
Handler的作用是获取当前所在线程的looper和其MessageQueue,并可发送消息到queue,然后再因message target指向其本身,辗转反侧回到自己这里进行处理,处理过程在当前线程中执行。
3. Message
Message是个载体,用来存储数据和指定target Handler。然后可以发送给那个Handler。
介绍完这三个,我们再来点刺激的,加深下印象!~~
故事是这样起头的,有个Looper的男人,他苦逼的开通了一个银行卡充钱提醒业务,就是messagequeue,用来干嘛呢?用来接收他女人需要开销的通知,然后类,当然是要打钱进去,最后这笔钱给那个她花。
Handler不用说,你们也猜到了,就是那个女人了。她通过男人的联系方式,发送需要money的提醒,然后从银行卡提现消费,美其名曰交给她处置。
Message呢,就类似是一条短信,一个电话啥的。
这样比喻下,是不是更有印象了~~
咳咳,言归正传。上述三个其实需要一个关键先生,那就是thread。thread是它们赖以生存的环境。没有thread就无法创建looper,没有thread,就无法执行handler的消息处理。
接着说说HandlerThread
HandlerThread相当于是Thread和Looper的合体。HandlerThread本身继承自Thread,并自己内部创建Looper与其关联,创建了looper就创建了MessageQueue。然后我们可以创建Handler与HandlerThread创建的Looper进行关联。
好了,热身完毕~~准备贴码分析。
从这部分代码可以看到,先是创建一个线程,这样就有了运行的环境。
然后开始先是
从这里可以看到Looper.prepare(),先是判断当前的线程是否已经有Looper了,如果没有,那就创一个looper,确保当前线程只有一个looper。ThreadLocal可以给当前的线程保存一个对象,如下代码所示。这里,它保存的是looper对象。
我们再看看Looper的构造方法。
可以看到,looper里创建了一个messagequeue,并绑定了当前的线程。也就是说looper和thread已经是一对一对应了。
然后我们看看new Handler()做了什么。
Handler构造方法里,FIND_POTENTIAL_LEAKS是false,所有那段代码直接忽略。然后可以看到,它执行了
这个做了什么呢,让我们看看。
可以看到,它获取了当前thread保存的looper。
然后继续往下,拿到了looper创建的messagequeue。至此,handler已经成功获取到当前线程的looper。
接下来是执行
在loop()里,先是获取当前线程保存的looper,从而得到messagequeue,然后从这个queue里试图获取下一条message。如果没有,则阻塞。从这里可以得出,new Handler()必须在Looper.loop()调用之前,否则,就执行不到了,因为一直阻塞在那里。
然后如果handler发送一条消息时,就会继续执行下去,并通过
分析到这里,其实关于初始化工作已经差不多结束了,也就这么了了几行,现在就差发送消息过来。那么我们来看看创建消息和发送消息。
怎么创建消息呢?看看Message就知道。先贴个Message里的获取msg的方法。
可以看到一堆obtain,除了第一个obtain()之外,其余obtain(…)里第一句都是
可以看到,如果之前message有创建过,那就继续使用它,就不用new了,如果没有,那就新建一个。sPool就是前一个message。这部分在Looper.loop()里的最后一句可以看到
看到了吧,先是重置所有参数,然后sPool = this;就把这个msg保存在sPool了。
然后我们随便看个参数多点的obtain()
就是这里了,m.target = h,这个h就是目标handler了。这样一来,创建了message,并关联了handler。当然,创建message还可以在handler里。看下Handler.obtainMessage()
这样就直接和这个handler关联了。
至此,创建message说完了。接着讲发送message。
就知道,又是一堆。
这里,我们就找个最简单的
再进去~
这里,创建了一个message,赋了个what参数。继续进去
再进~
这里,获取到messagequeue,并传到下一层。继续往下!
吗呀~终于到了!请看这里,msg.target = this;这句话立马就把这条message的target指定给自己了。所以就像先前总结的,handler发送消息,其实最后还是给自己处理。
忘了?那么我们继续往下说。现在创建好了消息,并发送好了消息,接着又回到原先的Looper.loop()了,这货还在那干等着呢。
然后他欣喜地从
好,接着进去看。
从上面三个handler打头的看来,这里就是分发消息给其中一个处理了。
有两个干货:handlerCallback和handlerMessage。
我们分别来看下。
先handleCallback吧~
可以看到,直接执行message里的callback参数的run();那callback是啥玩样儿?点下一看,原来是
那么怎么用呢?最经典的做法是Handler.post(Runnable r)
好了,再看handleMessage(),这个应该很熟吧。
看到这里是空的,没事,我们的做法是实现它。
到此,初始化、创建message、发送message、处理message都已经讲完了。这样一步步下来是不是很清晰。那么我们再来炒下冷饭,看下之前总结的部分。
1. Looper
Looper的作用是绑定当前所在的thread,并创建一个MessageQueue,不断循环从MessageQueue读取消息,然后消息交给target(Handler)处理。
2. Handler
Handler的作用是获取当前所在线程的looper和其MessageQueue,并可发送消息到queue,然后再因message target指向其本身,辗转反侧回到自己这里进行处理,处理过程在当前线程中执行。
3. Message
Message是个载体,用来存储数据和指定target Handler。然后可以发送给那个Handler。
上述三个需要一个关键先生,那就是thread。thread是它们赖以生存的环境。没有thread就无法创建looper,没有thread,就无法执行handler的消息处理。
有了上面这些基础,再来看handlerThread,实在是so easy!
我们直接上代码,只贴部分代码。
HandlerThread继承Thread,从名字也能猜到啦。然后里面有个looper
再看关键的run();
有没有看到很熟悉的部分。没错,先是Looper.prepare(),接着中间有些处理,然后Looper.loop();
咦~怎么没有handler,别急,不是有个getLooper()嘛。这个getLooper()是获取HandlerThread里的looper。那咋整啊?可以直接赋给handler嘛,具体比方说可以用这个
这样不就有了吗?接下去,就和上面handler、looper、message的流程一样了。
再来炒个饭~~蛋炒饭~#%$
HandlerThread相当于是Thread和Looper的合体。HandlerThread本身继承自Thread,并自己内部创建Looper与其关联,创建了looper就创建了MessageQueue。然后我们可以创建Handler与HandlerThread创建的Looper进行关联。
到此,这四个娘们都已经被我拨了外衣给你们看了一通。过瘾吧,啥?不过瘾?那你自己剥去吧。
下次,搞几个典型的案例玩玩~
案例1:
Android使用Handler实现线程池的效果,实现照片墙应用
刚开始接触Android的时候,就碰到Handler了,然后大概知道这个类是用来异步更新UI的。但是事实上,Handler的功能远远不止于此。为了能好好的使用它,我觉得还是要仔细研究下它和与它相关的几个类,并总结一下。
1.概念
为了给大家瞬间的带入感,它们相互之间的逻辑关系,我先直接在这里总结罗列下,权当给大家热热身。1. Looper
Looper的作用是绑定当前所在的线程thread,并创建一个MessageQueue,不断循环从MessageQueue读取消息,然后消息交给target(Handler)处理。
2. Handler
Handler的作用是获取当前所在线程的looper和其MessageQueue,并可发送消息到queue,然后再因message target指向其本身,辗转反侧回到自己这里进行处理,处理过程在当前线程中执行。
3. Message
Message是个载体,用来存储数据和指定target Handler。然后可以发送给那个Handler。
介绍完这三个,我们再来点刺激的,加深下印象!~~
故事是这样起头的,有个Looper的男人,他苦逼的开通了一个银行卡充钱提醒业务,就是messagequeue,用来干嘛呢?用来接收他女人需要开销的通知,然后类,当然是要打钱进去,最后这笔钱给那个她花。
Handler不用说,你们也猜到了,就是那个女人了。她通过男人的联系方式,发送需要money的提醒,然后从银行卡提现消费,美其名曰交给她处置。
Message呢,就类似是一条短信,一个电话啥的。
这样比喻下,是不是更有印象了~~
咳咳,言归正传。上述三个其实需要一个关键先生,那就是thread。thread是它们赖以生存的环境。没有thread就无法创建looper,没有thread,就无法执行handler的消息处理。
接着说说HandlerThread
HandlerThread相当于是Thread和Looper的合体。HandlerThread本身继承自Thread,并自己内部创建Looper与其关联,创建了looper就创建了MessageQueue。然后我们可以创建Handler与HandlerThread创建的Looper进行关联。
好了,热身完毕~~准备贴码分析。
2.部分源码分析
我们先根据Looper类的注释里的例子的流程进行讲解,因为这例子很普遍,很经典。* 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.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.prepare(),先是判断当前的线程是否已经有Looper了,如果没有,那就创一个looper,确保当前线程只有一个looper。ThreadLocal可以给当前的线程保存一个对象,如下代码所示。这里,它保存的是looper对象。
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }
我们再看看Looper的构造方法。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
可以看到,looper里创建了一个messagequeue,并绑定了当前的线程。也就是说looper和thread已经是一对一对应了。
然后我们看看new Handler()做了什么。
public Handler() { this(null, false); } ...... public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } 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构造方法里,FIND_POTENTIAL_LEAKS是false,所有那段代码直接忽略。然后可以看到,它执行了
mLooper = Looper.myLooper();
这个做了什么呢,让我们看看。
public static Looper myLooper() { return sThreadLocal.get(); }
可以看到,它获取了当前thread保存的looper。
然后继续往下,拿到了looper创建的messagequeue。至此,handler已经成功获取到当前线程的looper。
接下来是执行
Looper.loop();,我们继续看进去。
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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; // 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.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.recycleUnchecked(); } }
在loop()里,先是获取当前线程保存的looper,从而得到messagequeue,然后从这个queue里试图获取下一条message。如果没有,则阻塞。从这里可以得出,new Handler()必须在Looper.loop()调用之前,否则,就执行不到了,因为一直阻塞在那里。
然后如果handler发送一条消息时,就会继续执行下去,并通过
msg.target.dispatchMessage(msg);发送出去。发送给谁?其实是发送给原先的handler。
分析到这里,其实关于初始化工作已经差不多结束了,也就这么了了几行,现在就差发送消息过来。那么我们来看看创建消息和发送消息。
怎么创建消息呢?看看Message就知道。先贴个Message里的获取msg的方法。
可以看到一堆obtain,除了第一个obtain()之外,其余obtain(…)里第一句都是
Message m = obtain();。既然你这么重要,那就进去看看呗。
/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
可以看到,如果之前message有创建过,那就继续使用它,就不用new了,如果没有,那就新建一个。sPool就是前一个message。这部分在Looper.loop()里的最后一句可以看到
msg.recycleUnchecked();看源码~
void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
看到了吧,先是重置所有参数,然后sPool = this;就把这个msg保存在sPool了。
然后我们随便看个参数多点的obtain()
public static Message obtain(Handler h, int what) { Message m = obtain(); m.target = h; m.what = what; return m; }
就是这里了,m.target = h,这个h就是目标handler了。这样一来,创建了message,并关联了handler。当然,创建message还可以在handler里。看下Handler.obtainMessage()
public final Message obtainMessage() { return Message.obtain(this); }
这样就直接和这个handler关联了。
至此,创建message说完了。接着讲发送message。
就知道,又是一堆。
这里,我们就找个最简单的
sendEmptyMessage(int what)看看。
public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); }
再进去~
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); }
这里,创建了一个message,赋了个what参数。继续进去
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); }
这里,获取到messagequeue,并传到下一层。继续往下!
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
吗呀~终于到了!请看这里,msg.target = this;这句话立马就把这条message的target指定给自己了。所以就像先前总结的,handler发送消息,其实最后还是给自己处理。
忘了?那么我们继续往下说。现在创建好了消息,并发送好了消息,接着又回到原先的Looper.loop()了,这货还在那干等着呢。
然后他欣喜地从
Message msg = queue.next();里获得这个msg,并
msg.target.dispatchMessage(msg);
好,接着进去看。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
从上面三个handler打头的看来,这里就是分发消息给其中一个处理了。
有两个干货:handlerCallback和handlerMessage。
我们分别来看下。
先handleCallback吧~
private static void handleCallback(Message message) { message.callback.run(); }
可以看到,直接执行message里的callback参数的run();那callback是啥玩样儿?点下一看,原来是
Runnable callback;也就是说,如果message初始化里包含了callback,那就先执行callback;
那么怎么用呢?最经典的做法是Handler.post(Runnable r)
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
好了,再看handleMessage(),这个应该很熟吧。
public void handleMessage(Message msg) { }
看到这里是空的,没事,我们的做法是实现它。
到此,初始化、创建message、发送message、处理message都已经讲完了。这样一步步下来是不是很清晰。那么我们再来炒下冷饭,看下之前总结的部分。
1. Looper
Looper的作用是绑定当前所在的thread,并创建一个MessageQueue,不断循环从MessageQueue读取消息,然后消息交给target(Handler)处理。
2. Handler
Handler的作用是获取当前所在线程的looper和其MessageQueue,并可发送消息到queue,然后再因message target指向其本身,辗转反侧回到自己这里进行处理,处理过程在当前线程中执行。
3. Message
Message是个载体,用来存储数据和指定target Handler。然后可以发送给那个Handler。
上述三个需要一个关键先生,那就是thread。thread是它们赖以生存的环境。没有thread就无法创建looper,没有thread,就无法执行handler的消息处理。
有了上面这些基础,再来看handlerThread,实在是so easy!
我们直接上代码,只贴部分代码。
public class HandlerThread extends Thread { int mPriority; int mTid = -1; Looper mLooper; ...... @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; } ...... public Looper getLooper() { if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; } ...... public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } ...... }
HandlerThread继承Thread,从名字也能猜到啦。然后里面有个looper
Looper mLooper;
再看关键的run();
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
有没有看到很熟悉的部分。没错,先是Looper.prepare(),接着中间有些处理,然后Looper.loop();
咦~怎么没有handler,别急,不是有个getLooper()嘛。这个getLooper()是获取HandlerThread里的looper。那咋整啊?可以直接赋给handler嘛,具体比方说可以用这个
public Handler(Looper looper, Callback callback) { this(looper, callback, false); }
这样不就有了吗?接下去,就和上面handler、looper、message的流程一样了。
再来炒个饭~~蛋炒饭~#%$
HandlerThread相当于是Thread和Looper的合体。HandlerThread本身继承自Thread,并自己内部创建Looper与其关联,创建了looper就创建了MessageQueue。然后我们可以创建Handler与HandlerThread创建的Looper进行关联。
到此,这四个娘们都已经被我拨了外衣给你们看了一通。过瘾吧,啥?不过瘾?那你自己剥去吧。
下次,搞几个典型的案例玩玩~
案例1:
Android使用Handler实现线程池的效果,实现照片墙应用
相关文章推荐
- android studio编译出错——Failed to resolve: com.android.support:support-v4:22.2.0
- 一个优秀的Android应用从建项目开始
- Android 数据存储
- Android Butterknife框架
- Android FragmentTabHost使用
- GitHub Android 最火开源项目Top20
- android studio处使用
- Android学习心得(10) --- MAC下Android反编译(2)
- Android学习之——图形图像处理(Bitmap、BitmapFactory)(一)
- Android自动化测试:Robotium在Android Studio中的使用
- Android Studio 1.x版 签名key生成,查看key的sha1 和MD5 的方法
- Android 批量打包
- Android分析主线程与子线程,以及子线程之间相互通信
- 关于Android的HAL的一些理解
- 清除android程序中的多余资源
- 4418开发板的使用方法
- Android开发中,有哪些让你觉得相见恨晚的方法、类或接口?
- Android拓展系列(12)--使用Gradle发布aar项目到JCenter仓库
- 查看ANR
- android将LinearLayout中的内容保存为Bitmap。