本人原创,转载请注明出处哈:http://blog.csdn.net/tyhj_sf/article/details/51105453
1 引言 个人认为,不亲自阅读和熟悉Android的源码,不了解Android源码背后的实现机理,就不能真正成为Android开发的高手。前两天为了了解android的消息处理机制,我阅读和分析了Looper,Handler,MessageQueue,Message这几个类的源码,不得不对Google大牛们膜拜下,现在把学习的笔记拿出来特与大家分享。
2 消息机制的基本原理 从大的方面讲,不光是Android平台,各种平台的消息机制的原理基本上都是相近的,其中用到的主要概念大概有:
1)消息发送者;
2)消息队列;
3)消息处理循环。
示意图如下:
文章图片
图中表达的基本意思是,消息发送者通过某种方式,将消息发送到某个消息队列里,同时还有一个消息处理循环,不断从消息队列里摘取消息,并进一步解析处理。
在Android平台上,把上图的右边部分包装成了一个Looper类,这个类的内部具有对应的消息队列(MessageQueue mQueue)和loop函数。
但是Looper只是个简单的类而已,它虽然提供了循环处理方面的成员函数loop(),却不能自己凭空地运行起来,而只能寄身于某个真实的线程。而且,每个线程最多只能运作一个Looper对象,这一点应该很容易理解。
Android平台上另一个关键类是Handler。当消息循环在其寄身的线程里正式运作后,外界就是通过Handler向消息循环发出事件的。我们再画一张示意图如下:
文章图片
在Android应用中为线程创建消息处理程序的常规用法如下示例代码:
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();
}
}
3 Android消息机制相关的核心类 android的消息处理有四个核心类:Looper,Handler和MessageQueue(消息队列),Message。其中MessageQueue被封装到Looper里面了,我们不会直接与MessageQueue打交道。下面一一介绍:
3.1 Looper类
Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。使用Looper类创建Looper线程很简单:
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// ...其他处理,如实例化handler// 开始循环处理消息队列
Looper.loop();
}
}
【android|源码分析-Android中的消息机制详解】让我们看看与Looper相关的这两行代码各自做了什么。
1)Looper.prepare()的源码:
public class Looper {
// 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper内的消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
// 。。。其他属性// 每个Looper对象中有它的消息队列,和它所属的线程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}// 我们调用该方法会在调用线程的TLS中创建Looper对象
public static final void prepare() {
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper将抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
// 其他方法
}
通过源码,prepare()背后的工作方式一目了然,其核心就是将looper对象保存到ThreadLocal中去。如果你还不清楚什么是ThreadLocal,可以去看看它源码,这里不对它进行细说,我们只需要知道是每一个线程对象都有一个ThreadLocal类型的成员变量,而这段源码里的sThreadLocal就是当前线程的ThreadLocal类型的成员变量的引用。
2)Looper.loop()方法的源码:
当调用loop方法后,Looper线程就开始真正工作了,loop()方法内部的For(;;)死循环不断从自己的MessageQueue中取出队头的消息(也叫任务)执行。其源码分析如下:
/**
* 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.recycle();
}
}
除了prepare()和loop()方法,Looper类还提供了一些有用的方法,比如:
prepareMainLooper(),如果是主线程,调用此方法为主线程准备一个looper,主线程的looper有应用程序初始化;
Looper.myLooper()得到当前线程looper对象;
getThread()得到looper对象所属线程;
quit()方法结束looper循环。
到此为止,你应该对Looper有了基本的了解,总结几点:
1.每个线程有且最多只能有一个Looper对象,它被添加到ThreadLocal中去。每个线程都有一个ThreadLocal类型成员变量。
2.Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行
3.Looper使一个线程变成Looper线程。
3.2 Message类
Message类中含有一个Message类型的成员变量next。学过数据结构就可立刻知道,这是链表节点的结构,因此MessageQueue实际上维护的是一个Message链表。Message部分源码:
public final class Message implements Parcelable {public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
/*package*/ static final int FLAG_IN_USE = 1 << 0;
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
/** Flags to clear in the copyFrom method */
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
··················
··················
在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,这里不做总结了。但是有这么几点做开发时需要注意(有待补充):
1.尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。
2.如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
3.擅用message.what来标识信息,以便用不同方式处理message。
3.3 Handler类
3.3.1 什么是handler? handler扮演了往MessageQueue上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以通过它的几个有参构造方法来指定looper的。
下面我们就可以为之前的LooperThread类加入Handler:
public class LooperThread extends Thread {
private Handler handler1;
private Handler handler2;
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// 实例化两个handler
handler1 = new Handler();
handler2 = new Handler();
// 开始循环处理消息队列
Looper.loop();
}
}
可以看到,一个线程可以有多个Handler,但是只能有一个Looper!
3.3.2 Handler发送消息 有了handler之后,我们就可以使用
post(Runnable),
postAtTime(Runnable, long),
postDelayed(Runnable, long),
sendEmptyMessage(int),
sendMessage(Message),
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
这些方法向消息队列上发送消息了。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,见源码:
// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行
public final boolean post(Runnable r)
{
// 注意getPostMessage(r)将runnable封装成message
returnsendMessageDelayed(getPostMessage(r), 0);
}private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
//得到空的message
m.callback = r;
//将runnable设为message的callback,
return m;
}public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
// message的target必须设为该handler!
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对象
3.3.3 Handler处理消息 消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,见源码:
// 处理消息,该方法由looper调用
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 如果message设置了callback,即runnable消息,处理callback!
handleCallback(msg);
} else {
// 如果handler本身设置了callback,则执行callback
if (mCallback != null) {
/* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */
if (mCallback.handleMessage(msg)) {
return;
}
}
// 如果message没有callback,则调用handler的钩子方法handleMessage
handleMessage(msg);
}
}// 处理runnable消息
private final void handleCallback(Message message) {
message.callback.run();
//直接调用run方法!
}
// 由子类实现的钩子方法
public void handleMessage(Message msg) {
}
可以看到,除了handleMessage(Message msg)和Runnable对象的run( )方法由开发者实现外(实现具体逻辑),handler的内部工作机制对开发者是透明的。这正是handler API设计的精妙之处!
3.3.4 Handler的作用 1.只要线程A的handler传进另一个线程B中,就可以在线程B中向线程B发送消息,这些消息会被添加到关联的MessageQueue上。
文章图片
2.handler是在它关联的线程中处理消息的。
handler创建时,成员变量包含了一个looper,而looper关联到一个线程的。因此可以判断handler关联到哪个线程。
这就解决了android不能在其他非主线程中更新UI的问题。
Activity是在主线程运行的,在Activity中创建handler,那么这个handler就属于主线程,我们可以将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知Activity更新UI。
过程如下图所示:
文章图片
下面给出一个在worker thread通过发消息更新UI的例子(代码仅供参考):
public class TestDriverActivity extends Activity {
private TextView textview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textview = (TextView) findViewById(R.id.textview);
// 创建并启动工作线程
Thread workerThread = new Thread(new SampleTask(new MyHandler()));
workerThread.start();
}public void appendText(String msg) {
textview.setText(textview.getText() + "\n" + msg);
}class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
String result = msg.getData().getString("message");
// 更新UI
appendText(result);
}
}
}
public class SampleTask implements Runnable {
private static final String TAG = SampleTask.class.getSimpleName();
Handler handler;
public SampleTask(Handler handler) {
super();
this.handler = handler;
}@Override
public void run() {
try {// 模拟执行某项任务,下载等
Thread.sleep(5000);
// 任务完成后通知activity更新UI
Message msg = prepareMessage("task completed!");
// message将被添加到主线程的MQ中
handler.sendMessage(msg);
} catch (InterruptedException e) {
Log.d(TAG, "interrupted!");
}}private Message prepareMessage(String str) {
Message result = handler.obtainMessage();
Bundle data = https://www.it610.com/article/new Bundle();
data.putString("message", str);
result.setData(data);
return result;
}}
4 分析源码后对几个疑惑的回答
- 问:消息机制相关的类几个类?
答:Thread、 Handler、Looper、MessageQueue、Message - 问:Thread如何获得Looper?
答:对于Main Thread,调用Looper.getMainLooper( ),因为Looper已由应用创建。对于worker Thread,调用Looper.prepare( )创建一个关联的looper。 - 问:handler如何从消息队列中取消息并处理?
答:(1)handler持有Looper类型变量mLooper,handler在初始化时,将相关联的线程的Looper赋值给了mLooper。(2)handler持有MessageQueue类型变量mQueue,而mQueue通过赋值语句(mQueue=Looper.mQueue;)实例化。 - 问:Message怎样被传递给Handler.handleMessage(msg)方法的?
答:在Looper.loop()方法中,for死循环中调用了Handler.dispatchMessage(msg)方法(该方法中调用了handleMessage(msg)钩子方法)直到消息队列为空退出循环。 - 问:一个线程的handler怎么知道另外的线程发送了msg消息并从消息队列取消息处理呢?
答:在调用handler.sendMessage(msg)发送了msg消息时,sendMessage(msg)方法内部调用了enqueueMessage()方法,enqueueMessage()内部则调用了C/C++代码接口方法nativeWake( ),在这里我们不在向下研究C++层的代码。通过查资料得知:我们在构造Looper对象时,其内部创建了一个监听机制来监听消息入队,一旦有消息入消息队列进进行消息处理。
推荐阅读
- Android|Android_Handler机制原理解析和源码分析
- Android的XML常用标签整理
- 安卓源码探究|android源码学习-Handler机制及其六个核心点
- android|android studio新闻界面,课内资源 - 基于Android Studio实现的新闻APP
- 嵌入式软件|【迅为iMX6Q】iTOP-iMX6_android4.4.2 提取开发板的 uboot 源码