转自 https://www.cnblogs.com/cheneasternsun/p/5467115.html
android的消息处理机制——Looper,Handler,Message (原理图、源码)
转自:http://my.oschina.net/u/1391648/blog/282892
在开始讨论android的消息处理机制前,先来谈谈一些基本相关的术语。
通信的同步 (Synchronous):指向客户端发送请求后,必须要在服务端有回应后客户端才继续发送其它的请求 ,所以这时所有请求将会在服务端得到同步,直到服务端返回请求。
通信的异步 (Asynchronous):指客户端在发送请求后,不必等待服务端的回应就可以发送下一个请求 。
所谓同步调用 ,就是在一个函数或方法调用时,没有得到结果之前,该调用就不返回,直到返回结果。异步调用 和同步是相对的,在一个异步调用发起后,被调用者立即返回给调用者 ,但是调用者不能立刻得到结果,被调用都在实际处理这个调用的请求完成后,通过状态、通知或回调等方式来通知调用者请求处理的结果。
android的消息处理有三个核心类:Looper,Handler和Message。其实还有一Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,所以它不算是个核心类。
1. 消息类:Message类
android.os.Message的主要功能是进行消息的封装,同时可以指定消息的操作形式,Message类定义的变量和常用方法如下:
(1)public int what:变量,用于定义此Message属于何种操作
(2)public Object obj:变量,用于定义此Message传递的信息数据,通过它传递信息
(3)public int arg1:变量,传递一些整型数据时使用
(4)public int arg2:变量,传递一些整型数据时使用
(5)public Handler getTarget():普通方法,取得操作此消息的Handler对象。
在整个消息处理机制中,message 又叫task,封装了 任务携带的信息 和处理该任务的handler 。message的用法比较简单,但是有这么几点需要注意:
(1)尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。
(2)如果你的message只需要携带简单的int 信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
(3)擅用message.what 来标识信息 ,以便用不同方式处理message。
(4)使用setData()存放Bundle 对象。???
2. 消息通道:Looper
在使用Handler处理Message时,需要Looper (通道)来完成。在一个Activity中,系统会自动帮用户启动Looper对象 ,而在一个用户自定义的类中,则需要用户手工调用Looper类中的方法,然后才可以正常启动Looper对象 。Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程 。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。使用Looper类创建Looper线程很简单:
1
2
3
4
5
6
7
8
9
10
11
12
public
class
LooperThread
extends
Thread {
@Override
public
void
run() {
Looper.prepare();
Looper.loop();
}
}
通过上面两行核心代码,你的线程就升级为Looper线程了!那么这两行代码都做了些什么呢?
1)Looper.prepare():创建Loop而对象。
通过上图可以看到,现在你的线程中有一个Looper对象,它的内部维护了一个消息队列MQ。注意,一个Thread只能有一个Looper对象 ,为什么呢?来看一下源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public
class
Looper {
private
static
final
ThreadLocal sThreadLocal =
new
ThreadLocal();
final
MessageQueue mQueue;
Thread mThread;
private
Looper() {
mQueue =
new
MessageQueue();
mRun =
true
;
mThread = Thread.currentThread();
}
public
static
final
void
prepare() {
if
(sThreadLocal.get() !=
null
) {
throw
new
RuntimeException(
"Only one Looper may be created per thread"
);
}
sThreadLocal.set(
new
Looper());
}
}
prepare()背后的工作方式一目了然,其核心就是将looper对象定义为ThreadLocal。
2)Looper.loop():循环获取MQ中的消息,并发送给相应Handler对象。
调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。其源码分析如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public
static
final
void
loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final
long
ident = Binder.clearCallingIdentity();
while
(
true
) {
Message msg = queue.next();
if
(msg !=
null
) {
if
(msg.target ==
null
) {
return
;
}
if
(me.mLogging!=
null
) me.mLogging.println(
">>>>> Dispatching to "
+ msg.target +
" "
+ msg.callback +
": "
+ msg.what
);
msg.target.dispatchMessage(msg);
if
(me.mLogging!=
null
) me.mLogging.println(
"<<<<< Finished to "
+ msg.target +
" "
+ msg.callback);
final
long
newIdent = Binder.clearCallingIdentity();
if
(ident != newIdent) {
Log.wtf(
"Looper"
,
"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();
}
}
}
除了prepare()和loop()方法,Looper类还提供了一些有用的方法,比如Looper.myLooper() 得到当前线程looper对象 :
1
2
3
4
public
static
final
Looper myLooper() {
return
(Looper)sThreadLocal.get();
}
getThread()得到looper对象所属线程:
1
2
3
public
Thread getThread() {
return
mThread;
}
quit()方法结束looper循环:
1
2
3
4
5
6
public
void
quit() {
Message msg = Message.obtain();
mQueue.enqueueMessage(msg,
0
);
}
综上,Looper有以下几个要点:
1)每个线程有且只能有一个Looper对象 ,它是一个ThreadLocal
2)Looper内部有一个消息队列 ,loop()方法调用后线程开始不断从队列中取出消息执行
3)Looper使一个线程变成Looper线程 。
那么,我们如何操作Message Queue上的消息呢?这就是Handler的用处了
3. 消息操作类:Handler类
Message对象封装了所有的消息,而这些消息的操作需要android.os.Handler类完成。什么是handler?handler 起到了处理MQ上的消息 的作用(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的 。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的 。默认的构造方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public
class
handler {
final
MessageQueue mQueue;
final
Looper mLooper;
final
Callback mCallback;
public
Handler() {
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 =
null
;
}
}
下面我们就可以为之前的LooperThread类加入Handler:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public
class
LooperThread
extends
Thread {
private
Handler handler1;
private
Handler handler2;
@Override
public
void
run() {
Looper.prepare();
handler1 =
new
Handler();
handler2 =
new
Handler();
Looper.loop();
}
}
加入handler后的效果如下图:
可以看到,一个线程可以有多个Handler,但是只能有一个Looper!
Handler发送消息
有了handler之后,我们就可以使用
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
这些方法向MQ上发送消息了。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了 ,见源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public
final
boolean
post(Runnable r)
{
return
sendMessageDelayed(getPostMessage(r),
0
);
}
private
final
Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return
m;
}
public
boolean
sendMessageAtTime(Message msg,
long
uptimeMillis)
{
boolean
sent =
false
;
MessageQueue queue = mQueue;
if
(queue !=
null
) {
msg.target =
this
;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else
{
RuntimeException e =
new
RuntimeException(
this
+
" sendMessageAtTime() called with no mQueue"
);
Log.w(
"Looper"
, e.getMessage(), e);
}
return
sent;
}
通过handler发出的message有如下特点:
1.message.target为该handler对象 ,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码
msg.target.dispatchMessage(msg);
2.post发出的message,其callback为Runnable对象
Handler处理消息
说完了消息的发送,再来看下handler如何处理消息。消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)
完成的,见源码
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
可以看到,除了handleMessage(Message msg)和Runnable对象的run方法由开发者实现外(实现具体逻辑),handler的内部工作机制对开发者是透明的。Handler拥有下面两个重要的特点:
1)handler可以在任意线程发送消息 ,这些消息会被添加到关联的MQ上
2)消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的 ,handler是在它关联的looper线程中处理消息 的。
这就解决了android最经典的不能在其他非主线程中更新UI的问题。android的主线程也是一个looper线程 (looper在android中运用很广),我们在其中创建的handler默认将关联主线程MQ。因此,利用handler的一个solution就是在activity中创建handler并将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知activity更新UI 。(过程如图)
下面给出sample代码,仅供参考:
TestDriverActivity Activity {
TextView textview;
@Override
onCreate(Bundle savedInstanceState) {
.onCreate(savedInstanceState);
setContentView(R.layout.main);
textview = (TextView) findViewById(R.id.textview);
Thread workerThread = Thread( SampleTask( MyHandler()));
workerThread.start();
}
appendText(String msg) {
textview.setText(textview.getText() + "\n" + msg);
}
MyHandler Handler {
@Override
handleMessage(Message msg) {
String result = msg.getData().getString("message");
appendText(result);
}
}
}
SampleTask Runnable {
String TAG = SampleTask..getSimpleName();
Handler handler;
SampleTask(Handler handler) {
();
.handler = handler;
}
@Override run() { { Thread.sleep(5000); Message msg = prepareMessage(“task completed!”); handler.sendMessage(msg); } (InterruptedException e) { Log.d(TAG, “interrupted!”); }
}
Message prepareMessage(String str) { Message result = handler.obtainMessage(); Bundle data = Bundle(); data.putString(“message”, str); result.setData(data); result; }
}
转自:http://www.cnblogs.com/youxilua/archive/2011/11/25/2263825.html
前言: 很早以前,学习android的时候就接触过Handler ,知道Handler是一个用于线程间通信的类,最常用于做下载条,最近,看了Pro android 3 这本书,里面描述的Handler 说得非常的细致,与此,写下Handler的学习笔记
Android 运行的进程
为了,更好的了解Handler的机制,我们应该首先,将Android系统整个运行进程都要烂熟于心,下面是android 进程运行图 :
从图中我们可以看到,当我们从外部调用组件的时候,Service 和 ContentProvider 是从线程池那里获取线程,而Activity 和BroadcastReceiver是直接在主线程运行 ,为了,追踪线程,我们可以用debug 方法,或者使用一个工具类,这里,我们创建一个用于监视线程的工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* @author Tom_achai
* @
date
2011-11-20
*
*/
public class Utils {
public static long getThreadId(){
Thread t = Thread.currentThread();
return
t.getId();
}
/**
* 获取单独线程信息
* @
return
*/
public static String getThreadSignature(){
Thread t = Thread.currentThread();
long l = t.getId();
String name = t.getName();
long p = t.getPriority();
String gname = t.getThreadGroup().getName();
return
(
"(Thread):"
+name+
":(id)"
+ l +
"(:priority)"
+ p +
":(group)"
+ gname );
}
/**
*获取当前线程 信息
*/
public static void logThreadSignature(){
Log.d(
"ThreadUtils"
, getThreadSignature());
}
public static void logThreadSignature(String name ){
Log.d(
"ThreadUtils"
, name +
":"
+getThreadSignature());
}
public static void sleepForInSecs(int secs){
try{
Thread.
sleep
(secs * 1000);
}catch (Exception e) {
//
TODO: handle exception
e.printStackTrace();
}
}
/**
* 讲String放进Bundle 中
* @param message
* @
return
*/
public static Bundle getStringAsBundle(String message){
Bundle b = new Bundle();
b.putString(
"message"
, message);
return
b;
}
/**
*
* 获取Bundle的String
* @param b
* @
return
*/
public static String getStringFromABundle(Bundle b){
return
b.getString(
"message"
);
}
}
有了这样一个类就可以方便我们观察线程的运行
好了,现在准备好以后就进入正题Handler
Handlers
为什么要使用Handlers?
因为,我们当我们的主线程队列,如果处理一个消息超过5秒,android 就会抛出一个 ANP(无响应)的消息,所以,我们需要把一些要处理比较长的消息,放在一个单独线程里面处理,把处理以后的结果,返回给主线程运行,就需要用的Handler来进行线程建的通信,关系如下图 ;
下面是Handler,Message,Message Queue 之间的关系图
这个图有4个地方关系到handlers
1, 主线程(Main thread)
2, 主线程队列(Main thread queue)
3,Hanlder
4,Message
上面的四个地方,主线程,和主线程的队列我们无需处理,所以,我们主要是处理Handler 和 Message 之间的关系.
我们每发出一个Message,Message就会落在主线程的队列当中,然后,Handler就可以调用Message绑定的数据,对主线程的组件进行操作.
Message
作为handler接受的对象,我们有必要知道Message这个数据类型是个怎样的数据类型
从官方文档中我们可以知道message 关于数据的字段
public int what public int arg1 public int arg2 public Object obj
从上面的表格可以看出,message 提供了一个对象来存储对象,而且,还提供了三个int字段用来存储少量int类型
当然,除了以上三个Message 自有的字段外,我们还可以通过setData(Bundle b) ,来存储一个Bundle对象,来存储更丰富的数据类型,例如,图片等等.
在初始化我们的message的时候就可以为我们的Message默认字段赋值,注意赋值顺序!!!
1
2
3
4
5
6
7
8
9
Message msg = obtainMessage();
//
设置我们what 字段的初值,注意顺序!!!
Message msg = mHandler.obtainMessage(int what);
//
下面同理
Message msg = mHandler.obtainMessage(int what,Object object);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2, Object obj
);
handler机制的原理 (原理图)
转自:http://blog.youkuaiyun.com/itachi85/article/details/8035333
andriod提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。 1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。 2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。 3) Message Queue(消息队列):用来存放线程放入的消息。
4)线程:UIthread 通常就是main thread ,而Android 启动程序时会替它建立一个MessageQueue。
1.Handler创建消息
每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。消息的创建流程 如图所示。
2.Handler发送消息
UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应 。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1 。
Handler、Looper、MessageQueue的初始化流程 如图所示:
Hander持有对UI主线程消息队列MessageQueue和消息循环Looper的引用,子线程可以通过Handler将消息发送到UI线程的消息队列MessageQueue中。
3.Handler处理消息
UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。
子线程通过Handler、Looper与UI主线程通信的流程 如图 所示。
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系 (源码!!!)
转载请标明出处:http://blog.youkuaiyun.com/lmj623565791/article/details/38377229 ,本文出自【张鸿洋的博客】
很多人面试肯定都被问到过,请问Android 中的Looper , Handler , Message有什么关系?本篇博客目的首先为大家从源码角度介绍3者关系,然后给出一个容易记忆的结论。
1、 概述
Handler 、 Looper 、Message 这三者都与Android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢? 异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。
说了这一堆,那么和Handler 、 Looper 、Message有啥关系?其实Looper负责的就是创建一个MessageQueue,然后进入一个无限循环体不断从该MessageQueue中读取消息,而消息的创建者就是一个或多个Handler 。
2、 源码解析
1、Looper
对于Looper主要是prepare()和loop()两个方法。 首先看prepare()方法
public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(true)); }
sThreadLocal是一个ThreadLocal对象 ,可以在一个线程中存储变量。可以看到,在第5行,将一个Looper的实例放入了ThreadLocal ,并且2-4行判断了sThreadLocal是否为null,否则抛出异常 。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例 ~相信有些哥们一定遇到这个错误。 下面看Looper的构造方法:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
在构造方法中,创建了一个MessageQueue(消息队列)。 然后我们看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; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); if (msg == null) { return; } 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); } 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(); } }
第2行: public static Looper myLooper() { return sThreadLocal.get(); } 方法直接返回了sThreadLocal存储的Looper实例 ,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。 第6行:拿到该looper实例中的mQueue(消息队列) 13到45行:就进入了我们所说的无限循环。 14行:取出一条消息,如果没有消息则阻塞。 27行:使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。Msg的target是什么呢?其实就是handler对象 ,下面会进行分析。 44行:释放消息占据的资源。 Looper主要作用: 1、 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。 2、 loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。 好了,我们的异步消息处理线程已经有了消息队列(MessageQueue),也有了在无限循环体中取出消息的哥们,现在缺的就是发送消息的对象了,于是乎:Handler登场了。
2、Handler
使用Handler之前,我们都是初始化一个实例,比如用于更新UI线程,我们会在声明的时候直接初始化,或者在onCreate中初始化Handler实例。所以我们首先看Handler的构造方法,看其如何与MessageQueue联系上的,它在子线程中发送的消息(一般发送消息都在非UI线程)怎么发送到MessageQueue中的。
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; }
14行:通过Looper.myLooper()获取了当前线程保存的Looper实例,然后在19行又获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。
然后看我们最常用的sendMessage方法
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); }
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); }
辗转反则最后调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue然后调用了enqueueMessage方法 ,我们再来看看此方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
enqueueMessage中首先为meg.target赋值为this,【如果大家还记得Looper的loop方法会取出每个msg然后交给msg,target.dispatchMessage(msg)去处理消息】,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。
现在已经很清楚了Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispathMessage方法,下面我们赶快去看一看这个方法:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
可以看到,第10行,调用了handleMessage方法,下面我们去看这个方法:
public void handleMessage(Message msg) { }
可以看到这是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。
例如:
private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case value: break; default: break; } }; };
到此,这个流程已经解释完毕,让我们首先总结一下
1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
好了,总结完成,大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。
3、Handler post
今天有人问我,你说Handler的post方法创建的线程和UI线程有什么关系?
其实这个问题也是出现这篇博客的原因之一;这里需要说明,有时候为了方便,我们会直接写如下代码:
mHandler.post(new Runnable() { @Override public void run() { Log.e("TAG", Thread.currentThread().getName()); mTxt.setText("yoxi"); } });
然后run方法中可以写更新UI的代码,其实这个Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:
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; }
可以看到,在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message.
注:产生一个Message对象,可以new ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。
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); }
最终和handler.sendMessage一样,调用了sendMessageAtTime,然后调用了enqueueMessage方法,给msg.target赋值为handler,最终加入MessagQueue.
可以看到,这里msg的callback和target都有值,那么会执行哪个呢?
其实上面已经贴过代码,就是dispatchMessage方法:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
第2行,如果不为null,则执行callback回调,也就是我们的Runnable对象。
好了,关于Looper , Handler , Message 这三者关系上面已经叙述的非常清楚了。
最后来张图解:
希望图片可以更好的帮助大家的记忆~~
4、后话
其实Handler不仅可以更新UI,你完全可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。
new Thread() { private Handler handler; public void run() { Looper.prepare(); handler = new Handler() { public void handleMessage(android.os.Message msg) { Log.e("TAG",Thread.currentThread().getName()); }; };<pre code_snippet_id="445431" snippet_file_name="blog_20140808_19_1943618" name="code" class="java"> Looper.loop(); } </pre>
Android不仅给我们提供了异步消息处理机制让我们更好的完成UI的更新,其实也为我们提供了异步消息处理机制代码的参考~~不仅能够知道原理,最好还可以将此设计用到其他的非Android项目中去~~
最新补充:
关于后记,有兄弟联系我说,到底可以在哪使用,见博客:Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
Android之Handler用法总结 (Handler与Thread\TimerTask的结合使用思路,不用细看代码。)
转自:http://www.cnblogs.com/devinzhang/archive/2011/12/30/2306980.html
方法一:(java习惯,在android平台开发时这样是不行的,因为它违背了单线程模型)
刚刚开始接触android线程编程的时候,习惯好像java一样,试图用下面的代码解决问题
new Thread( new Runnable() { public void run() { myView.invalidate(); } }).start();
可以实现功能,刷新UI界面。但是这样是不行的,因为它违背了单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
方法二:(Thread+Handler)
查阅了文档和apidemo后,发觉常用的方法是利用Handler来实现UI线程的更新的。
Handler来根据接收的消息,处理UI更新。Thread线程发出Handler消息,通知更新UI。
Handler myHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case TestHandler.GUIUPDATEIDENTIFIER: myBounceView.invalidate(); break; } super.handleMessage(msg); } };
class myThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { Message message = new Message(); message.what = TestHandler.GUIUPDATEIDENTIFIER; TestHandler.this.myHandler.sendMessage(message); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
以上方法demo看:http://rayleung.javaeye.com/blog/411860
方法三:(java习惯。Android平台中,这样做是不行的,这跟Android的线程安全有关)
在Android平台中需要反复按周期执行方法可以使用Java上自带的TimerTask类,TimerTask相对于Thread来说对于资源消耗的更低,除了使用Android自带的AlarmManager使用Timer定时器是一种更好的解决方法。 我们需要引入import java.util.Timer; 和 import java.util.TimerTask;
public class JavaTimer extends Activity { Timer timer = new Timer(); TimerTask task = new TimerTask(){ public void run() { setTitle("hear me?"); } }; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); timer.schedule(task, 10000); } }
方法四:(TimerTask + Handler)
通过配合Handler来实现timer功能的!
public class TestTimer extends Activity { Timer timer = new Timer(); Handler handler = new Handler(){ public void handleMessage(Message msg) { switch (msg.what) { case 1: setTitle("hear me?"); break; } super.handleMessage(msg); } }; TimerTask task = new TimerTask(){ public void run() { Message message = new Message(); message.what = 1; handler.sendMessage(message); } }; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); timer.schedule(task, 10000); } }
方法五:( Runnable + Handler.postDelayed(runnable,time) )
在Android里定时更新 UI,通常使用的是 java.util.Timer , java.util.TimerTask , android.os. Handler组合。实际上Handler 自身已经提供了定时的功能。
private Handler handler = new Handler(); private Runnable myRunnable= new Runnable() { public void run() { if (run) { handler.postDelayed(this, 1000); count++; } tvCounter.setText("Count: " + count); } };
然后在其他地方调用
handler.post(myRunnable);
handler.post(myRunnable,time);
案例看:http://shaobin0604.javaeye.com/blog/515820
====================================================================
知识点总结补充:
很多初入Android或Java开发的新手对Thread、Looper、Handler和Message仍然比较迷惑,衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask由于目前市面上的书籍等资料都没有谈到这些问题,今天就这一问题做更系统性的总结。我们创建的Service、Activity以及Broadcast均是一个主线程处理,这里我们可以理解为UI线程 。但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以考虑使用Thread线程来解决。
对于从事过J2ME开发的程序员来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。或者从Runnable接口继承,但对于Android平台来说UI控件都没有设计成为线程安全类型 ,所以需要引入一些同步的机制来使其刷新,这点Google在设计Android时倒是参考了下Win32的消息处理机制。
1. 对于线程中的刷新一个View为基类的界面,可以使用postInvalidate()方法 在线程中来处理,其中还提供了一些重写方法比如postInvalidate(int left,int top,int right,int bottom) 来刷新一个矩形区域,以及延时执行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一个参数为毫秒
2. 当然推荐的方法是通过一个Handler来处理 这些,可以在一个线程的run方法中调用handler对象的 postMessage或sendMessage方法来实现,Android程序内部维护着一个消息队列,会轮训处理这些,如果你是Win32程序员可以很好理解这些消息处理,不过相对于Android来说没有提供 PreTranslateMessage这些干涉内部的方法。
3. Looper又是什么呢? ,其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制,Looper对象的执行需要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要释放资源,使用Looper.release方法。
4.Message 在Android是什么呢? 对于Android中Handler可以传递一些内容,通过Bundle对象可以封装String、Integer以及Blob二进制对象,我们通过在线程中使用Handler对象的sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处理器 。对于Handler类提供了重写方法handleMessage(Message msg) 来判断,通过msg.what来区分每条信息 。将Bundle解包来实现Handler类更新UI线程中的内容实现控件的刷新操作。相关的Handler对象有关消息发送sendXXXX相关方法如下,同时还有postXXXX相关方法,这些和Win32中的道理基本一致,一个为发送后直接返回,一个为处理后才返回 .
5. java.util.concurrent对象分析,对于过去从事Java开发的程序员不会对Concurrent对象感到陌生吧,他是JDK 1.5以后新增的重要特性作为掌上设备,我们不提倡使用该类,考虑到Android为我们已经设计好的Task机制,这里不做过多的赘述,相关原因参考下面的介绍:
6. 在Android中还提供了一种有别于线程的处理方式,就是Task以及AsyncTask ,从开源代码中可以看到是针对Concurrent的封装,开发人员可以方便的处理这些异步任务。
摘录自:http://www.cnblogs.com/playing/archive/2011/03/24/1993583.html
Android应用程序消息处理机制(Looper、Handler)分析 (!!!消息的循环、发送、处理原理!深入!分析!)
转自:http://blog.youkuaiyun.com/luoshengyang/article/details/6817933/
Android 应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序的执行,本文将详细分析Android应用程序的消息处理机制。
前面我们学习Android应用程序中的Activity启动(Android应用程序启动过程源代码分析 和Android应用程序内部启动Activity过程(startActivity)的源代码分析 )、Service启动(Android系统在新进程中启动自定义服务过程(startService)的原理分析 和Android应用程序绑定服务(bindService)的过程源代码分析 )以及广播发送(Android应用程序发送广播(sendBroadcast)的过程分析 )时,它们都有一个共同的特点,当ActivityManagerService需要与应用程序进行并互时,如加载Activity和Service、处理广播待,会通过Binder进程间通信机制 来知会应用程序,应用程序接收到这个请求时,它不是马上就处理这个请求,而是将这个请求封装成一个消息,然后把这个消息放在应用程序的消息队列中去,然后再通过消息循环来处理这个消息。这样做的好处就是消息的发送方只要把消息发送到应用程序的消息队列中去就行了,它可以马上返回去处理别的事情,而不需要等待消息的接收方去处理完这个消息才返回,这样就可以提高系统的并发性。实质上,这就是一种异步处理机制。
这样说可能还是比较笼统,我们以Android应用程序启动过程源代码分析 一文中所介绍的应用程序启动过程的一个片断来具体看看是如何这种消息处理机制的。在这篇文章中,要启动的应用程序称为Activity,它的默认Activity是MainActivity,它是由Launcher 来负责启动的,而Launcher又是通过ActivityManagerService来启动的,当ActivityManagerService为这个即将要启的应用程序准备好新的进程后,便通过一个Binder进程间通信过程 来通知这个新的进程来加载MainActivity,如下图所示:
它对应Android应用程序启动过程中的Step 30到Step 35,有兴趣的读者可以回过头去参考Android应用程序启动过程源代码分析 一文。这里的Step 30中的scheduleLaunchActivity是ActivityManagerService通过Binder进程间通信机制 发送过来的请求,它请求应用程序中的ActivityThread执行Step 34中的performLaunchActivity操作,即启动MainActivity的操作。这里我们就可以看到,Step 30的这个请求并没有等待Step 34这个操作完成就返回了,它只是把这个请求封装成一个消息,然后通过Step 31中的queueOrSendMessage操作把这个消息放到应用程序的消息队列中,然后就返回了。应用程序发现消息队列中有消息时,就会通过Step 32中的handleMessage操作来处理这个消息,即调用Step 33中的handleLaunchActivity来执行实际的加载MainAcitivy类的操作。
了解Android应用程序的消息处理过程之后,我们就开始分样它的实现原理了。与Windows应用程序的消息处理过程一样,Android应用程序的消息处理机制也是由消息循环、消息发送和消息处理这三个部分组成的,接下来,我们就详细描述这三个过程。
1. 消息循环
在消息处理机制中,消息都是存放在一个消息队列中去,而应用程序的主线程就是围绕这个消息队列进入一个无限循环的,直到应用程序退出。如果队列中有消息,应用程序的主线程就会把它取出来,并分发给相应的Handler进行处理;如果队列中没有消息,应用程序的主线程就会进入空闲等待状态,等待下一个消息的到来。在Android应用程序中,这个消息循环过程是由Looper类来实现的,它定义在frameworks/base/core/Java /android/os/Looper.java文件中,在分析这个类之前,我们先看一下Android应用程序主线程是如何进入到这个消息循环中去的。
在Android应用程序进程启动过程的源代码分析 一文中,我们分析了Android应用程序进程的启动过程,Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的,我们来看看这个函数的实现,它定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread { ...... public static final void main(String[] args) { ...... Looper.prepareMainLooper(); ...... ActivityThread thread = new ActivityThread(); thread.attach(false); ...... Looper.loop(); ...... thread.detach(); ...... } }
这个函数做了两件事情,一是在主线程中创建了一个ActivityThread实例,二是通过Looper类使主线程进入消息循环中,这里我们只关注后者。
首先看Looper.prepareMainLooper函数的实现,这是一个静态成员函数,定义在frameworks/base/core/java/android/os/Looper.java文件中:
public class Looper { ...... private static final ThreadLocal sThreadLocal = new ThreadLocal(); final MessageQueue mQueue; ...... public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); } public static final void prepareMainLooper() { prepare(); setMainLooper(myLooper()); if (Process.supportsProcesses()) { myLooper().mQueue.mQuitAllowed = false; } } private synchronized static void setMainLooper(Looper looper) { mMainLooper = looper; } public static final Looper myLooper() { return (Looper)sThreadLocal.get(); } private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); } ...... }
函数prepareMainLooper做的事情其实就是在线程中创建一个Looper对象,这个Looper对象是存放在sThreadLocal成员变量里面的,成员变量sThreadLocal的类型为ThreadLocal,表示这是一个线程局部变量,即保证每一个调用了prepareMainLooper函数的线程里面都有一个独立的Looper对象。在线程是创建Looper对象的工作是由prepare函数来完成的,而在创建Looper对象的时候,会同时创建一个消息队列MessageQueue,保存在Looper的成员变量mQueue中,后续消息就是存放在这个队列中去。消息队列在Android应用程序消息处理机制中最重要的组件,因此,我们看看它的创建过程,即它的构造函数的实现,实现frameworks/base/core/java/android/os/MessageQueue.java文件中:
public class MessageQueue { ...... private int mPtr; private native void nativeInit(); MessageQueue() { nativeInit(); } ...... }
它的初始化工作都交给JNI方法nativeInit来实现了,这个JNI方法定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (! nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return; } android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue); }
在JNI中,也相应地创建了一个消息队列NativeMessageQueue,NativeMessageQueue类也是定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中,它的创建过程如下所示:
NativeMessageQueue::NativeMessageQueue() { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); Looper::setForThread(mLooper); } }
它主要就是在内部创建了一个Looper对象,注意,这个Looper对象是实现在JNI层的,它与上面Java层中的Looper是不一样的,不过它们是对应的,下面我们进一步分析消息循环的过程的时候,读者就会清楚地了解到它们之间的关系。
这个Looper的创建过程也很重要,不过我们暂时放一放,先分析完android_os_MessageQueue_nativeInit函数的执行,它创建了本地消息队列NativeMessageQueue对象之后,接着调用android_os_MessageQueue_setNativeMessageQueue函数来把这个消息队列对象保存在前面我们在Java层中创建的MessageQueue对象的mPtr成员变量里面:
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj, NativeMessageQueue* nativeMessageQueue) { env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, reinterpret_cast<jint>(nativeMessageQueue)); }
这里传进来的参数messageQueueObj即为我们前面在Java层创建的消息队列对象,而gMessageQueueClassInfo.mPtr即表示在Java类MessageQueue中,其成员变量mPtr的偏移量,通过这个偏移量,就可以把这个本地消息队列对象natvieMessageQueue保存在Java层创建的消息队列对象的mPtr成员变量中,这是为了后续我们调用Java层的消息队列对象的其它成员函数进入到JNI层时,能够方便地找回它在JNI层所对应的消息队列对象。
我们再回到NativeMessageQueue的构造函数中,看看JNI层的Looper对象的创建过程,即看看它的构造函数是如何实现的,这个Looper类实现在frameworks/base/libs/utils/Looper.cpp文件中:
Looper::Looper(bool allowNonCallbacks) : mAllowNonCallbacks(allowNonCallbacks), mResponseIndex(0) { int wakeFds[2]; int result = pipe(wakeFds); ...... mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1]; ...... #ifdef LOOPER_USES_EPOLL mEpollFd = epoll_create(EPOLL_SIZE_HINT); ...... struct epoll_event eventItem; memset(& eventItem, 0, sizeof(epoll_event)); eventItem.events = EPOLLIN; eventItem.data.fd = mWakeReadPipeFd; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); ...... #else ...... #endif ...... }
这个构造函数做的事情非常重要,它跟我们后面要介绍的应用程序主线程在消息队列中没有消息时要进入等待状态以及当消息队列有消息时要把应用程序主线程唤醒的这两个知识点息息相关。它主要就是通过pipe系统调用来创建了一个管道了:
int wakeFds[2]; int result = pipe(wakeFds); ...... mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1];
管道是Linux系统中的一种进程间通信机制,具体可以参考前面一篇文章Android学习启动篇 推荐的一本书《Linux内核源代码情景分析》中的第6章--传统的Uinx进程间通信。简单来说,管道就是一个文件,在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的,一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容,写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。这个等待和唤醒的操作是如何进行的呢,这就要借助Linux系统中的epoll机制了。 Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。但是这里我们其实只需要监控的IO接口只有mWakeReadPipeFd一个,即前面我们所创建的管道的读端,为什么还需要用到epoll呢?有点用牛刀来杀鸡的味道。其实不然,这个Looper类是非常强大的,它除了监控内部所创建的管道接口之外,还提供了addFd接口供外界面调用,外界可以通过这个接口把自己想要监控的IO事件一并加入到这个Looper对象中去,当所有这些被监控的IO接口上面有事件发生时,就会唤醒相应的线程来处理,不过这里我们只关心刚才所创建的管道的IO事件的发生。
要使用Linux系统的epoll机制,首先要通过epoll_create来创建一个epoll专用的文件描述符:
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
传入的参数EPOLL_SIZE_HINT是在这个mEpollFd上能监控的最大文件描述符数。
接着还要通过epoll_ctl函数来告诉epoll要监控相应的文件描述符的什么事件:
struct epoll_event eventItem; memset(& eventItem, 0, sizeof(epoll_event)); eventItem.events = EPOLLIN; eventItem.data.fd = mWakeReadPipeFd; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
这里就是告诉mEpollFd,它要监控mWakeReadPipeFd文件描述符的EPOLLIN事件,即当管道中有内容可读时,就唤醒当前正在等待管道中的内容的线程。 C++层的这个Looper对象创建好了之后,就返回到JNI层的NativeMessageQueue的构造函数,最后就返回到Java层的消息队列MessageQueue的创建过程,这样,Java层的Looper对象就准备好了。有点复杂,我们先小结一下这一步都做了些什么事情:
A. 在Java层,创建了一个Looper对象,这个Looper对象是用来进入消息循环的,它的内部有一个消息队列MessageQueue对象mQueue;
B. 在JNI层,创建了一个NativeMessageQueue对象,这个NativeMessageQueue对象保存在Java层的消息队列对象mQueue的成员变量mPtr中;
C. 在C++层,创建了一个Looper对象,保存在JNI层的NativeMessageQueue对象的成员变量mLooper中,这个对象的作用是,当Java层的消息队列中没有消息时,就使Android应用程序主线程进入等待状态,而当Java层的消息队列中来了新的消息后,就唤醒Android应用程序的主线程来处理这个消息。
回到ActivityThread类的main函数中,在上面这些工作都准备好之后,就调用Looper类的loop函数进入到消息循环中去了:
public class Looper { ...... public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; ...... while (true) { Message msg = queue.next(); ...... if (msg != null) { if (msg.target == null) { return; } ...... msg.target.dispatchMessage(msg); ...... msg.recycle(); } } } ...... }
这里就是进入到消息循环中去了,它不断地从消息队列mQueue中去获取下一个要处理的消息msg,如果消息的target成员变量为null,就表示要退出消息循环了,否则的话就要调用这个target对象的dispatchMessage成员函数来处理这个消息,这个target对象的类型为Handler,下面我们分析消息的发送时会看到这个消息对象msg是如设置的。
这个函数最关键的地方便是从消息队列中获取下一个要处理的消息了,即MessageQueue.next函数,它实现frameworks/base/core/java/android/os/MessageQueue.java文件中:
public class MessageQueue { ...... final Message next() { int pendingIdleHandlerCount = -1; int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(mPtr, nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); final Message msg = mMessages; if (msg != null) { final long when = msg.when; if (now >= when) { mBlocked = false; mMessages = msg.next; msg.next = null; if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg); return msg; } else { nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); } } else { nextPollTimeoutMillis = -1; } if (pendingIdleHandlerCount < 0) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount == 0) { mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } pendingIdleHandlerCount = 0; nextPollTimeoutMillis = 0; } } ...... }
调用这个函数的时候,有可能会让线程进入等待状态。什么情况下,线程会进入等待状态呢?两种情况,一是当消息队列中没有消息时,它会使线程进入等待状态;二是消息队列中有消息,但是消息指定了执行的时间,而现在还没有到这个时间,线程也会进入等待状态。消息队列中的消息是按时间先后来排序的,后面我们在分析消息的发送时会看到。
执行下面语句是看看当前消息队列中有没有消息:
nativePollOnce(mPtr, nextPollTimeoutMillis);
这是一个JNI方法,我们等一下再分析,这里传入的参数mPtr就是指向前面我们在JNI层创建的NativeMessageQueue对象了,而参数nextPollTimeoutMillis则表示如果当前消息队列中没有消息,它要等待的时候,for循环开始时,传入的值为0,表示不等待。
当前nativePollOnce返回后,就去看看消息队列中有没有消息:
final Message msg = mMessages; if (msg != null) { final long when = msg.when; if (now >= when) { mBlocked = false; mMessages = msg.next; msg.next = null; if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg); return msg; } else { nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); } } else { nextPollTimeoutMillis = -1; }
如果消息队列中有消息,并且当前时候大于等于消息中的执行时间,那么就直接返回这个消息给Looper.loop消息处理,否则的话就要等待到消息的执行时间:
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
如果消息队列中没有消息,那就要进入无穷等待状态直到有新消息了:
nextPollTimeoutMillis = -1;
-1表示下次调用nativePollOnce时,如果消息中没有消息,就进入无限等待状态中去。
这里计算出来的等待时间都是在下次调用nativePollOnce时使用的。
这里说的等待,是空闲等待,而不是忙等待,因此,在进入空闲等待状态前,如果应用程序注册了IdleHandler接口来处理一些事情,那么就会先执行这里IdleHandler,然后再进入等待状态。IdlerHandler是定义在MessageQueue的一个内部类:
public class MessageQueue { ...... public static interface IdleHandler { boolean queueIdle(); } ...... }
它只有一个成员函数queueIdle,执行这个函数时,如果返回值为false,那么就会从应用程序中移除这个IdleHandler,否则的话就会在应用程序中继续维护着这个IdleHandler,下次空闲时仍会再执会这个IdleHandler。MessageQueue提供了addIdleHandler和removeIdleHandler两注册和删除IdleHandler。
回到MessageQueue函数中,它接下来就是在进入等待状态前,看看有没有IdleHandler是需要执行的:
if (pendingIdleHandlerCount < 0) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount == 0) { mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
如果没有,即pendingIdleHandlerCount等于0,那下面的逻辑就不执行了,通过continue语句直接进入下一次循环,否则就要把注册在mIdleHandlers中的IdleHandler取出来,放在mPendingIdleHandlers数组中去。
接下来就是执行这些注册了的IdleHanlder了:
for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } }
执行完这些IdleHandler之后,线程下次调用nativePollOnce函数时,就不设置超时时间了,因为,很有可能在执行IdleHandler的时候,已经有新的消息加入到消息队列中去了,因此,要重置nextPollTimeoutMillis的值:
nextPollTimeoutMillis = 0;
分析完MessageQueue的这个next函数之后,我们就要深入分析一下JNI方法nativePollOnce了,看看它是如何进入等待状态的,这个函数定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jint ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(timeoutMillis); }
这个函数首先是通过传进入的参数ptr取回前面在Java层创建MessageQueue对象时在JNI层创建的NatvieMessageQueue对象,然后调用它的pollOnce函数:
void NativeMessageQueue::pollOnce(int timeoutMillis) { mLooper->pollOnce(timeoutMillis); }
这里将操作转发给mLooper对象的pollOnce函数处理,这里的mLooper对象是在C++层的对象,它也是在前面在JNI层创建的NatvieMessageQueue对象时创建的,它的pollOnce函数定义在frameworks/base/libs/utils/Looper.cpp文件中:
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { int result = 0; for (;;) { ...... if (result != 0) { ...... return result; } result = pollInner(timeoutMillis); } }
为了方便讨论,我们把这个函数的无关部分都去掉,它主要就是调用pollInner函数来进一步操作,如果pollInner返回值不等于0,这个函数就可以返回了。
函数pollInner的定义如下:
int Looper::pollInner(int timeoutMillis) { ...... int result = ALOOPER_POLL_WAKE; ...... #ifdef LOOPER_USES_EPOLL struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); bool acquiredLock = false; #else ...... #endif if (eventCount < 0) { if (errno == EINTR) { goto Done; } LOGW("Poll failed with an unexpected error, errno=%d", errno); result = ALOOPER_POLL_ERROR; goto Done; } if (eventCount == 0) { ...... result = ALOOPER_POLL_TIMEOUT; goto Done; } ...... #ifdef LOOPER_USES_EPOLL for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeReadPipeFd) { if (epollEvents & EPOLLIN) { awoken(); } else { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { ...... } } if (acquiredLock) { mLock.unlock(); } Done: ; #else ...... #endif ...... return result; }
这里,首先是调用epoll_wait函数来看看epoll专用文件描述符mEpollFd所监控的文件描述符是否有IO事件发生,它设置监控的超时时间为timeoutMillis:
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
回忆一下前面的Looper的构造函数,我们在里面设置了要监控mWakeReadPipeFd文件描述符的EPOLLIN事件。
当mEpollFd所监控的文件描述符发生了要监控的IO事件后或者监控时间超时后,线程就从epoll_wait返回了,否则线程就会在epoll_wait函数中进入睡眠状态了。返回后如果eventCount等于0,就说明是超时了:
if (eventCount == 0) { ...... result = ALOOPER_POLL_TIMEOUT; goto Done; }
如果eventCount不等于0,就说明发生要监控的事件:
for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeReadPipeFd) { if (epollEvents & EPOLLIN) { awoken(); } else { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { ...... } }
这里我们只关注mWakeReadPipeFd文件描述符上的事件,如果在mWakeReadPipeFd文件描述符上发生了EPOLLIN就说明应用程序中的消息队列里面有新的消息需要处理了,接下来它就会先调用awoken函数清空管道中的内容,以便下次再调用pollInner函数时,知道自从上次处理完消息队列中的消息后,有没有新的消息加进来。
函数awoken的实现很简单,它只是把管道中的内容都读取出来:
void Looper::awoken() { ...... char buffer[16]; ssize_t nRead; do { nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); }
因为当其它的线程向应用程序的消息队列加入新的消息时,会向这个管道写入新的内容来通知应用程序主线程有新的消息需要处理了,下面我们分析消息的发送的时候将会看到。
这样,消息的循环过程就分析完了,这部分逻辑还是比较复杂的,它利用Linux系统中的管道(pipe)进程间通信机制来实现消息的等待和处理,不过,了解了这部分内容之后,下面我们分析消息的发送和处理就简单多了。
2. 消息的发送 应用程序的主线程准备就好消息队列并且进入到消息循环后,其它地方就可以往这个消息队列中发送消息了。我们继续以文章开始介绍的Android应用程序启动过程源代码分析 一文中的应用程序启动过为例,说明应用程序是如何把消息加入到应用程序的消息队列中去的。
在Android应用程序启动过程源代码分析 这篇文章的Step 30中,ActivityManagerService通过调用ApplicationThread类的scheduleLaunchActivity函数通知应用程序,它可以加载应用程序的默认Activity了,这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread { ...... private final class ApplicationThread extends ApplicationThreadNative { ......