标签:
做过windows GUI的同学应该清楚,一般的GUI操作都是基于消息机制的,应用程序维护一个消息队列,开发人员编写对应事件的回调函数就能实现我们想要的操作
其实android系统也和windows GUI一样,也是基于消息机制,今天让我们通过源码来揭开android消息机制的神秘面纱
谈起异步消息,就不能不提及Handler,在安卓中,由于主线程中不能做耗时操作,所以耗时操作必须让子线程执行,而且只能在主线程(即UI线程)中执行UI更新操作,通过Handler发送异步消息,我们就能更新UI,一般的handler的用法如下:
public class TestActivity extends Activity {
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
final TextView tv = (TextView) findViewById(R.id.textView);
//创建一个handler对象,重写handleMessage回调函数,在回调函数里面做UI更新操作
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0x1:
//在这里更新UI
Bundle b = msg.getData();
tv.setText(b.getString("textview"));
break;
default:
break;
}
}
};
new Thread(new InnerRunnable()).start();
}
private class InnerRunnable implements Runnable {
@Override
public void run() {
//做耗时操作放在这里
//创建一个消息对象
Message msg = Message.obtain();
msg.what = 0x1;
//可以用Bundle传递数据
Bundle b = new Bundle();
b.putString("textview", "异步加载");
mHandler.sendMessage(msg);
}
}
}
我们简要归纳一下Handler的常规使用方法:
1、在UI线程创建Handler对象,并重写handler的handlerMessage回调函数,可以在回调函数里面做UI更新操作
2、子线程完成耗时操作后,创建Message对象,用Message对象保存结果
3、在子线程通过handler对象调用sendMessage函数发送消息,这样UI线程收到消息后,获取数据,就可以更新UI
是不是很简单,那现在让我们一步一步来拨开迷雾,看清android异步消息本质
先简要介绍会涉及的组件:
Handler:消息发送者
Looper:消息循环,负责从MessageQueue获取消息,并处理
Message: 消息,可以存放数据
MessageQueue : 消息队列,负责存/取消息
从上面的例子我们得知,要使用Handler必须要先在UI线程创建一个Handler对象,那么我们通过源码查看Handler构造函数,代码位于: com.google.android / android/os/Handler.java,(因为源码较多,所以以下代码截图只包含关键代码)
public class Handler {
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
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使用了无参构造函数版本,但内部最终调用了Handler(Callback callback, boolean async)构造函数
在Handler(Callback callback, boolean async)代码开始,先检查FIND_POTENTIAL_LEAKS开关(该开关默认关闭),若该开关打开,则检查Handler所在类是否为匿名类/成员类或局部类时,并且该类是否是static的,如果不是static的,则输出Log提示使用者Handler类不为static可能会导致内存泄露,至于为什么会造成内存泄露,这里先卖个关子
调用Looper.myLooper()获取Looper对象,然后通过mQueue = mLooper.mQueue和Looper对象中的消息队列关联
那么跟进Looper.myLooper看看,代码位于: com.google.android / android/os/Looper.java
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
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));
}
public static Looper myLooper() {
return sThreadLocal.get();
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
}
Looper.myLooper()仅仅是返回一个线程副本,从代码得知,实际创建Looper对象的函数为Looper.paepare(),该函数创建Looper对象后存放到线程本地存储
Looper的构造函数为private,我们只能通过Looper.myLooper获取Looper对象,而创建Looper对象时,会创建一个消息队列
这样我们现在明白了handler创建相关的工作,现在让我们来看看子线程发送消息的处理机制
当子线程发送传递消息时,调用了handler对象的sendMessage函数
public class Handler {
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
}
根据上面的调用关系链:sendMessage->sendMessageDelayed->sendMessageAtTime->enqueueMessage;可以看到最终sendMessage最终只做了两件事情:
1、通过msg.target = this把Message对象和当前Handler对象关联
2、把消息放到handler中Looper对象的MessageQueue中
消费消息位于Looper.loop函数中
public final class Looper {
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();
}
}
}
Looper.loop函数:
1、通过 final Looper me = myLooper()获取当前线程的Looper对象
2、消息循环中, 通过Message msg = queue.next()获取队列中的消息,若没有消息则阻塞
3、调用msg.target.dispatchMessage(msg)处理消息
4、处理完毕后调用msg.recycle()回收消息
根据上面代码可得知实际处理消息的是msg.target.dispatchMessage(msg),那我们来看看msg.target到底是什么东西
public final class Message implements Parcelable {
/*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;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
原来msg.target是一个handler对象,而最终调用的是handler的dispatchMessage函数来处理消息
public class Handler {
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
public interface Callback {
public boolean 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);
}
}
}
根据以下代码可以得知消息处理的步骤如下:
1、先判断Message的callback对象是否为空,不为空则回调Message对象的callback对象的函数(可以再创建Message时指定Callback)
2、如果创建Handler的Callback对象不为空,那么则调用Callback对象的handlerMessage函数
3、若没指定Handler的Callback或者Handler中Callback对象的handlerMessage函数调用失败,则回调handlerMessage,而这个handlerMessage函数正是我们创建handler时重写的函数
至此我们明白安卓异步消息的内部机制:
1、子线程通过handler向UI线程Looper中MessageQueue发送message
2、Looper处理消息时再回调handler中定义的回调
至此用一张图总结android异步消息机制(网上找的,凑合看):
所以要让一个线程要想成为消息处理线程,那么必须得有Looper才行,可能此时你会疑惑,为什么UI线程没有创建Looper却也能接收Handler发送来的消息?
其实,APP初始化过程中,android会自动帮UI线程创建Looper对象,代码位于:com.google.android / android/app/ActivityThread.java
public final class ActivityThread {
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
可以看到调用了Looper.prepareMainLooper()为主线程创建Looper,最后调用Looper.loop进行消息循环
通过分析源码,可以得知:
1、如果主线程和子线程,或者子线程同子线程通信,可以调用Looper.prepare()为Looper.loop()为子线程创建消息队列,再通过handler就可以非常简单的实现线程间通信
2、创建Message对象,一定要用Message.obtain()函数代替new Message()来创建消息,因为Looper处理完消息后,会调用 msg.recycle()函数把Message归还到消息池中,使用Message.obtain会先从消息池中获取,若没有才会创建新的Message,这样可以避免重复创建Message对象
3、Handler所在类必须是static类,因为非static内部类会隐式的持有外部类的引用,假如延时操作还未执行完成时就关闭Activity,因为handler持有Activity的引用,那么Activity就不会被及时回收而造成内存泄露
标签:
原文地址:http://my.oschina.net/u/1155515/blog/378460