Android的异步消息处理由四个部分组成:Message、Handler 、MessageQueue和Looper。
先抛出自己的几个问题供自己以后进行回顾复习:
1、为什么需要进行异步消息处理?
大量耗时的操作我们一般是另开一个子线程来进行处理,但是在子线程中是不允许对主线程即UI线程进行操作,否则程序将会崩溃,这个时候我们就可以用Handler来实现在子线程安全的切换到UI线程中实现更新UI的操作。
2、为什么叫异步消息处理?
因为当Hander发动消息和处理消息不在同一个时间,所以叫异步消息处理。
3、Handler的实现细节?
4、Looper如何实现轮询操作?
5、Looper和MessageQueue何时new出来的?
6、Handler、Looper、MessageQueue、Message各自的作用?
7.创建Handler实例的时候为什么要用Handler.obtain(),而不是直接new Handler()?
一、Message:在线程之间传递的消息,存放在MessageQueue中。Message类中有三个携带信息的属性 what、arg1、arg2、obj。obj用来携带一个Object对象,其余三个携带整型数据,Handler通过这些字段来判断其接下来的操作。
二、Handler
Handler类似一个处理者,用来发送或者处理Message。在sendMessage(Message msg)发送Message,经过一系列的方法调用处理后,在handleMessage(Message msg)方法中接受子线程传递过来的Message,然后判断Message并在主线程中执行相关操作。
异步消息处理的代码写法:
第一步,是先new一个Handler,并重写handleMessage()方法,用来处理子线程传来的Message
// 先是new一个主线程的Handler,并重写handleMessage()方法
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {// 判断携带的数据
case UPDATE_TEXT:
// 在这里进行UI操作
text.setText("Nice to meet you");
break;
default:
break;
}
}
};
第二步,在子线程中new一个Message,并设置相应的携带数据的字段如what字段,在《第一行代码》中写法是直接new一个Message实例,但是通过查阅网上资料,发现这种写法已经过时
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
}
}).start();
现在的写法是调用Message.obtain()来返回一个Message实例
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
}
}).start();
那为什么要这样写呢?看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) {// 如果sPool不为空说明有现有的Message实例,不必再浪费开销创建一个新实例
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
在官方给的注释中写道“Return a new Message instance from the global pool. Allows us to avoid allocating new objects in many cases.”意思是在global pool中返回Message实例,避免在很多情况下分配新对象,通俗来讲就是如果global pool中有就直接返回现存的Message实例,节约空间开销。
第三步,就是调用Handler.sendMessage()后,经过中间MessageQueue和Looper的操作最后会调用Handler.handleMessage()方法,实现子线程切换到主线程中并在主线程中进行操作。那MessageQueue和Looper是什么时候创建的呢?在Handler的构造期间。
看Handler的构造器源码:
public Handler() {
this(null, false);
}
继续看调用的父类构造器:
public Handler(@Nullable 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();// 在这里返回了一个Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;// 通过Looper又引入了一个MessageQueue
mCallback = callback;
mAsynchronous = async;
}
mLooper = Looper.myLooper(); 在这里返回了一个Looper。
mQueue = mLooper.mQueue; 通过Looper又引入了一个MessageQueue
看Looper.myLooper()细节:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这段代码注释的意思是返回了一个与当前线程相关的Looper。这里猜测一个线程只能有一个Looper,继续看sThreadLocal.get()源码:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
上面代码的注释意思是返回当前线程的本地变量的一个副本,若是没有本地变量就返回first initialized的值。至此MessageQueue和Looper就有了,接下来看怎么把消息从Handler.sendMessage()方法辗转到Handler.handleMessage()的 ,理论是将Message添加到MessageQueue中,然后由主线程的Looper不断在一个死循环方法中轮询MessageQueue中的Message,然后再将Message传递给Handler。
接下来看Handler.sendMessage()方法源码:
①在sendMessage()方法中调用sendMessageDelayed(msg, 0)方法。
sendMessageDelayed(msg, 0)方法的定义:
②继续调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法
sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)定义:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
690 MessageQueue queue = mQueue;// 消息队列queue
691 if (queue == null) {// 如果对列为空报异常
692 RuntimeException e = new RuntimeException(
693 this + " sendMessageAtTime() called with no mQueue");
694 Log.w("Looper", e.getMessage(), e);
695 return false;
696 }
697 return enqueueMessage(queue, msg, uptimeMillis);
698 }
③继续调用enqueueMessage(queue, msg, uptimeMillis)方法
enqueueMessage(queue, msg, uptimeMillis)定义:
这段代码中的msg.target = this;this即Hanler,这段代码将Message和Handler捆绑在一起。
④调用queue.enqueueMessage(msg, uptimeMillis)方法
在queue.enqueueMessage(msg, uptimeMillis)中将Message添加到消息队列中:
至此,Message已经被添加到MessageQueue中。那么Looper如何轮询MessageQueue的呢(Message是怎么从MessageQueue中被拿出来然后被传递到Handler的handleMessage()方法中去的)?
Looper又是什么时候被new的呢?MessageQueue又是什么时候被new的?
三、Looper
两个问题:
1、Looper如何轮询MessageQueue的呢(Message是怎么从MessageQueue中被拿出来然后被传递到Handler的handleMessage()方法中去的)?
2、Looper和MessageQueue是什么时候被new的呢?
看ActivityThread类的源码,在他的main方法中:
public static void main(String[] args) {
//代码1
Looper.prepareMainLooper();
//代码2
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//代码3
Looper.loop();
}
在代码1中,看prepareMainLooper()方法:
108 /**
109 * Initialize the current thread as a looper, marking it as an
110 * application's main looper. The main looper for your application
111 * is created by the Android environment, so you should never need
112 * to call this function yourself. See also: {@link #prepare()}
113 */
114 public static void prepareMainLooper() {
115 prepare(false);// 这段代码
116 synchronized (Looper.class) {
117 if (sMainLooper != null) {
118 throw new IllegalStateException("The main Looper has already been prepared.");
119 }
120 sMainLooper = myLooper();
121 }
122 }
上面这段代码的注释中说初始化当前线程作为一个Looper,并把它标记为app的主要的Looper,并强调这个Looper是由android环境自动创建的,实际上在创建主线程时,会自动的用ActivityThread中的静态main方法,因为会调用main方法中的 Looper.prepareMainLooper() 方法,对Looper进行初始化,但是还是没有看到何时new了,所以继续看Looper.prepareMainLooper()源码的prepare(false);
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));
}
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
这句话也证实了我之前的猜测,每一个线程只能有一个Looper。
new Looper(quitAllowed)跟进去:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
到这里也终于明了,MessageQueue和Looper都是在主线程被创建时就被new了。
问题解决,下一个问题:Looper怎么进行轮询的(Message是怎么从MessageQueue中被拿出来然后被传递到Handler的handleMessage()方法中去的)?
看代码3 :Looper.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;
// 在死循环for中,不断取出MessageQueue中的元素,知道取到null停止,实现了轮询的操作
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// msg.target就是之前说Handler和Message绑定的操作,msg.target就是Hander,在这里又调回到Handler里面的
// dispatchMessage()方法
msg.target.dispatchMessage(msg);
}
先是判断当前线程有没有Looper,没有则抛出"No Looper; Looper.prepare() wasn't called on this thread."的异常,让你调用Looper.prepare()来new一个Looper。
然后在死循环for中,不断取出MessageQueue中的元素,知道取到null停止,实现了轮询的操作。
接着在msg.target.dispatchMessage(msg); 中msg.target就是之前说Handler和Message绑定的操作,msg.target就是Hander,在这里又调回到Handler里面的dispatchMessage()方法:
/**
* 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);
}
}
上面看到又调用了handleMessage()方法。
至此Message经过Handler发出,由MessageQueue接受,然后由主线程的Looper不断轮询MessageQueue,取出Message,然后回调方法 handleMessage(msg)再交由Handler进行处理的细节全部分析完,即Handler机制的实现。
问题总结:
1、为什么需要进行异步消息处理?
大量耗时的操作我们一般是另开一个子线程来进行处理,但是在子线程中是不允许对主线程即UI线程进行操作,否则程序将会崩溃,这个时候我们就可以用Handler来实现在子线程安全的切换到UI线程中实现更新UI的操作。
2、为什么叫异步消息处理?
因为当Hander发动消息和处理消息不在同一个时间,所以叫异步消息处理。
3、Handler机制的实现细节?
大致的步骤:先在主线程即UI线程创建一个Handler实例,同时需要重写handleMessager()方法,在自己创建的子线程中,通过的Message的obtain()方法得到一个Message实例,设置Message用来携带数据的字段,在子线程中调用Handler的sendMessage()来发送Message,在sendMessage()的实现中,经过一系列的方法调用最终会调用Queue.enqueueMessage(msg, uptimeMillis)方法,在这个方法中将Messag添加到消息队列中,在调用Queue.enqueueMessage(msg, uptimeMillis)之前,在enqueueMessage(queue, msg, uptimeMillis)方法中通过msg.traget = this将Message的target属性和Handler绑定,到这里为止消息已经被安全的添加到消息队列中,之后便是Looper对消息队列的轮询操作,在主线程被创建时会调用主线程ActivityThread中静态main方法中的Looper.prepareMainLooper()方法中的prepare()方法来new出一个Looper的实例(每个线程只能有一个Looper,这个会在prepareMainLooper()方法的prepare()方法中进行Looper是否存在的判断)并顺便new出一个MessageQueue实例,然后继续执行主线程中的Looper.loop()方法,在这个方法通过一个死循环for来不断的取出MessageQueue中的元素,直至MessageQueue的元素为空循环停止,最后会调用loop()方法中msg.target.dispatchMessage(Message msg)方法,在dispatchMessage()中调用Handler.handleMessage()方法。
4、Looper如何实现轮询操作?
在主线程创建是,会调用ActivityThread中main方法中的Looper.loop(),通过一个死循环for来不但取出MessageQueue中的元素,直至为空。然后通过调用msg.target.dispatchMessage()方法中的Handler.handleMessage()方法将Message传递给Handler
5、Looper和MessageQueue何时new出来的?
在主线程ActivityThread被创建时,会自动执行ActivityThread的main方法,main方法中Looper.prepareMainLooper()中的prepare()中对Looper和MessageQueue进行new;
6、Handler、Looper、MessageQueue、Message各自的作用?
Handler接受和发送Message; Looper轮询MessageQueue; MessageQueue存储Message; Message携带数据。
7.创建Message实例的时候为什么要用Message.obtain(),而不是直接new Message()?
节省空间开销。
new Message()的形式会在很多情况下浪费空间开销,Message.obtain()方法通过判断gobal pool中是否存在Message实例,如果有直接返回无需再浪费空间花销来创建Message对象。