安卓消息处理机制(Looper,Handler,Message)
1.前言
android消息处理机制很基础,也很重要。一般耗时操作需要放到子线程中去执行,执行完后,需要把结果反馈给主线程,在主线程中更新ui。比如:在子线程中加载一张网络图片,加载成功后,在主线程中显示图片。那么,子线程怎样把结果反馈给主线程?这就用到了我们今天讲到的消息处理。
2.简要说明消息处理过程
消息处理机制主要涉及四个类:Looper
,Message
,MessageQueue
,Handler
。过程如下:
一个线程有且只有一个Looper
对象,Looper
是循环的意思,Looper
对象创建自己的消息队列MessageQueue
,当有消息message产生时,message会被压入队列message queue。Looper
一直循环做如下工作:从message queue中取出一个 msg,handler来处理。借用一张图:
具体怎么处理下面会详细说明。
3. Looper
先来说说Looper。
Looper主要作用:
-
与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue(创建消息队列)。
-
loop()方法不断从MessageQueue中去取消息,交给消息
Message
的target属性的dispatchMessage()
去处理。
Looper中的变量:
// 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;//looper中的消息队列
final Thread mThread;//绑定的线程
Looper通过prepare()方法来创建looper对象:
//quitAllowed:消息循环是否可以退出(主线程传进去的是false)
private static void prepare(boolean quitAllowed) {
//如果一个线程中已经有looper对象,再创建就会抛出异常,也就是说,一个线程只能有一个looper对象
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
调用这个方法之后,需调用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();
//looper开始无限循环
for (;;) {
Message msg = queue.next(); // 取出对列头的message(也可以叫task)
if (msg == null) {
//如果消息队列中没有task,就退出循环
return;
}
//日志
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。
// Msg的target是什么呢?其实就是handler对象,下面会进行分析。
msg.target.dispatchMessage(msg);
//日志
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
/*
clearCallingIdentity这个可以看成是安全性代码,也可以看成是调试代码
作用是确定当前这个looper所在的“线程”是否一直在同一个“进程”里,如果进程变多半是说明这个线程运行在某种跨进程代码里。
比如说你通过AIDL调用stub,远程那边接到之后启动一个线程,就有可能触发ident != newIdent了
*/
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();
}
}
public static MessageQueue myQueue() {
return myLooper().mQueue;
}
/*
* 得到当前线程的Looper实例
* */
public static Looper myLooper() {
return sThreadLocal.get();
}
如果要结束loop()循环,可以调用quit()退出循环。
public void quit() {
mQueue.quit(false);
}
注意:
-
looper方法必须在prepare方法之后运行
-
每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal;一个消息队列(如果一个线程已经有looper对象,再创建会抛异常)
-
Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行
-
Looper使一个线程变成Looper线程。
另外,Looper中还有一些方法看一下:
//获取message queue
public static MessageQueue myQueue() {
return myLooper().mQueue;
}
/*
* 得到当前线程的Looper实例
* */
public static Looper myLooper() {
return sThreadLocal.get();
}
/*
* 给主线程初始化一个MainLooper,MainLooper一般是系统create,所以自己永远不用调用,知道有这么个东西就好了。
* */
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实例
* */
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
4.Message
上面讲到,从looper中取出一个msg后,通过这句代码交给msg的target来处理消息:msg.target.dispatchMessage(msg)
。
target是什么?
我们来看看Message类有什么东西。先看看有哪些变量:
Handler target;//target是处理消息的Handler对象
Runnable callback;//回调callback
private static Message sPool;
private static int sPoolSize = 0; /
//message内容
public int what;
Bundle data;
public Object obj;
public int arg1;
public int arg2;
获取Message实例可以直接new 一个Message,也可以通过调用Message的obtain()方法,Message中obtain()方法有很多,这里只看一个。
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
类中有一个Handler
类型的变量target,来处理消息。真正处理消息的,是Handler
类。下面我们来看Handler
类。
注意:
产生一个Message对象,可以new,也可以使用Message.obtain()
方法;
两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。
5.Handler
依旧先看看变量:
final MessageQueue mQueue;//handler所在的线程的消息队列
final Looper mLooper;//handler所在线程的looper对象
//这个callback是一个接口,create handler对象的时候需要实现它。里面有一个handlerMessage()方法,
//handlerMessage()方法就是我们需要自己实现的处理消息的方法。
final Callback mCallback;
CallBack接口:
public interface Callback {
public boolean handleMessage(Message msg);
}
我们通常这样用:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
break;
}
}
};
熟悉吧?这个handlerMessage()
就是Handler
类中的interface mCallback
里的handlerMessage()
方法。
前面说过,looper从消息队列中取出一个message,通过message的target.dispatchMessage(msg)(Handler)
来处理该消息。那么,Handler是如何与MessageQueue联系上的,它在子线程中发送的消息(一般发送消息都在非UI线程)又是怎么发送到MessageQueue中的?
我们来看看Handler的构造方法:
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());
}
}
//得到当前线程中的looper对象
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;
}
ok,上面疑问搞清楚了。我们接着来看之前一直说的dispatchMessage(msg)
方法。
/*
* 这个方法处理消息
* 如果msg的callback和target(handler)都有值,会执行哪个?
* 通过getPostMessage(Runnerable r)方法可以知道,r即指定msg的callback。
* */
public void dispatchMessage(Message msg) {
//先执行msg的callback
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果msg的callback为空,则执行msg的handler对象target的回调mCallback。
// 这个mCallback就是我们平时创建handler时实现的接口handleressage()。
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
好了,到这里,消息处理就说完了。handler类里有一些发送消息的方法,post(Runnable r)
,sendMessageDelayed(Message msg, long delayMillis)
,sendMessageAtTime(Message msg, long uptimeMillis)
,postAtTime(Runnable r, long uptimeMillis)
等等,它们的作用都是把一个msg压入message queue中等待处理。如果想知道 post和sendMessage的区别,怎么将msg压入message queue,这些方法的具体实现等,可以看我的博客 postDelayed, sendMessageAtTime等handler发送消息方法总结。
参考资料:
转载于:https://my.oschina.net/u/2421076/blog/701591
上一篇: linux获取字符串长度
推荐阅读
-
android的消息处理机制(图文+源码分析)—Looper/Handler/Message
-
android的消息处理机制(图文+源码分析)—Looper/Handler/Message
-
安卓开发笔记(十九):异步消息处理机制实现更新软件UI
-
Android消息处理机制Looper和Handler详解
-
异步消息处理(Message, Handler, MessageQueue, Looper)
-
Android消息处理机制(Handler、Looper、MessageQueue与Message)
-
Android异步消息处理机制 深入理解Looper、Handler、Message的关系
-
简述android线程间消息处理机制(Looper、Handler和Message)
-
Android消息机制三剑客之Handler、Looper、Message源码分析(一)
-
Android多线程(二)消息处理机制---Handler、Message、Looper源码原理解析