本文分析基于android-25
Handler是Android系统提供给我们的一个工具,主要用于多线程间的通讯,比如更新UI等等。
一、Handler常见用法
1.
首先定义一个Handler,并重写handlerMessage方法,当收到消息时,根据msg.what判断来源,然后进行相关操作,这里是更新了一个textView的显示文字。
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
mText.setText(msg.obj.toString());
break;
}
}
};
发送消息的代码如下:
mSendButton是一个按钮,点击发送一条消息。
mSendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "from button Click";
mHandler.sendMessage(msg);
}
});
2.我们也可以调用Message的post方法直接发送一条消息
mHandler.post(new Runnable() {
@Override
public void run() {
mText.setText("from post");
}
};
当然还有postDelay,发送之后延时执行,第二个参数为延迟多少ms
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mText.setText("from postDelayed");
}
}, 3 * 1000);
3.Handler也可以直接传入一个CallBack,CallBack是Handler类内定义的一个接口
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
可以看到,这个接口是有返回值的,它的意义是当你通过该接口方法handleMessage(Message msg)处理消息之后,如果后面不需要进行后续处理,那么返回true,如果后面还想再进行其他处理,可以返回false,下文源码分析部分会有详细说明
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// do sth
return false;
}
});
二、Handler机制内部实现源码分析
Handler实现涉及到的几个主要的类为Handler、Looper、MessageQueue、Message等,下面我们来一个个分析一下
Handler
构造方法
Handler有多个构造方法,如下
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我们发现,这些方法最终都是调用的下面的两个方法,那我们来看下这两个。
1. public Handler(Callback callback, boolean async)
这个方法中有个FIND_POTENTIAL_LEAKS变量
/*
* Set this flag to true to detect anonymous, local or member classes
* that extend this Handler class and that are not static. These kind
* of classes can potentially create leaks.
*/
private static final boolean FIND_POTENTIAL_LEAKS = false;
这个变量定义为了static final,并且值为false。也就是if判断里面的内容不会执行,在stackoverflow上查询了一下,基本可以认为if中的语句是Google工程师在调试过程中使用的,正式发布android版本时,该变量则会改为false。
后面的代码我们看到,这是首先会拿到一个Looper对象mLooper,如果该变量为null,则会直接抛出异常,从这里我们就可以看出来,使用Hander时,必须要存在一个Looper对象,否则会报错,Looper对象可以通过Looper.prepare()获取,后面Looper源码部分会有更详细的介绍。
可能有同学会有疑问,平时我们在使用Handler的时候并没有去调用这个方法啊,Looper对象哪里来的?平时我们在主线程没有调用Looper.prepare()是因为主线程在启动的时候已经为我们调用过了,这个调用是在ActivityThread.main()里面写的。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到,在这里通过调用Looper.prepareMainLooper()创建的主线程Handler,并且调用了loop开启了循环。
2. public Handler(Looper looper, Callback callback, boolean async)
这个方法没有什么特别的,我们用到的Looper对象等可以通过该构造方法传进来。
发送消息
Handler发送消息的方法也有很多个,用图片列举如下:
其中发送延时消息sendMessageDelayed内部计算时钟用的是SystemClock.uptimeMillis(),而不能用System.currentTimeMillis()
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
根据Handler源码追溯图片列举方法的实现,发现这些方法最终都是调用的sendMessageAtTime这个方法。
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);
}
这个方法最终是通过enqueueMessage将要发送的msg,添加进了消息队列,调用的是MessageQueue的enqueueMessage。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
移除消息
当我们activity退出的时候,需要将队列中为处理的消息移除,否则容易产生内存泄漏。
分发消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
分发消息的时候首先看msg.callback是否为空,这个callback是Runnable,就是上面的构造方法传进来的,就是上面提到的Handler的第二种用法,如果不为空则会执Runnable的run方法。为空则继续判断,mCallback是上文提到的Handler定义的Callback接口,如果有传入进来,则会执行mCallback.handleMessage,并校验返回值,为false则会继续向下执行handleMessage方法,当然,mCallback为空也会执行,handleMessage是个空方法没有具体实现,需要我们写handler时,根据自己需求实现,就像上文提到的第一种用法里的
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
Looper
Looper可以通过Looper.prepare()创建,一个线程只能存在一个Looper,重复创建会抛出异常,这一点可以从Looper的构造方法看出来。
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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));
}
sThreadLocal是
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
这里利用了ThreadLocal的特性来存储looper,可以保证各个线程的looper互不影响。
looper创建出来之后,可以通过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调用的是prepareMainLooper(),对应获得主线程looper的方法是getMainLooper,这是一个线程安全的方法。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
Looper创建的时候有一个是否允许退出的变量quitAllowed,这个变量会传给MessageQueue,用来在消息队列退出的时候做校验。
Looper去消息需要调用Looper.loop开启循环,然后通过Message.next取一个消息出来,当取到消息的时候会执行message.target.dispatchMessage(),这里的target就是handler,所以会调用到上面提到的handler的dispatchMessage方法来处理消息
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//target就是handler 执行handler的dispatchMessage方法来分发消息
msg.target.dispatchMessage(msg);//
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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.recycleUnchecked();
}
}
Message
Message是消息对象,实现了序列化接口Parcelable,Message有几个主要的成员变量
public int what; //可用来区分消息来源
public Object obj; //可用来消息内容
Handler target; //handler的引用
Runnable callback; //由handler传入的callback接口
Message next; //下一个消息
private static final int MAX_POOL_SIZE = 50; //最大消息数量
Message消息池的管理采用的单链表的形式
获取一个消息实例,用Message.obtain(),而不建议采用new Message()
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();
}
上面的操作涉及到的是链表的处理,回收消息可以调用recycle()方法。
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
MessageQueue
消息队列,主要是对Message的操作,包括将一个消息加入队列,返回一个消息,涉及到的两个方法为next()和enqueueMessage()
next
Message next()
...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
...
nextPollTimeoutMillis = 0;
}
}
enqueueMessage
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
这两个方法(或者说MessageQueue)用Linux系统的epoll机制帮助自己不断地把消息入队和取出消息,其他的主要操作就是对链表的处理。
关于epoll机制可以自行Google或看这两篇文章:
【Linux学习】epoll详解
select、poll、epoll之间的区别总结[整理]
总结
现在简单总结一下Handler机制:
首先创建Handelr,在创建handler之前必须先创建一个looper对象,当然主线程looper系统已经为我们创建好了,不用我们再手动创建。调用Looper.loop可以开启循环,不断的从消息队列中取出消息,加入消息最终调用的是handler的sendMessageAtTime,然后会调用到message.enqueueMessage来将消息入队,通过looper.loop取到消息后,调用msg.target.dispatchMessage(就是Handler的dispatchMessage)来处理消息,这样就会将发送的消息交给handler所在的线程处理。
思考一个问题
我们知道,非静态内部类处理不好很容易引起内存泄漏,handler使用不当引起的内存泄漏就是这种情况之一。判断对象是否可以回收采用的是可达性算法,那么当handle引起内存泄漏时整个引用链是什么样呢?
首先handler持有外部acitvity的引用,Message中有个target变量持有的是handler的引用,而MessageQueue又持有Message,Looper中mQueue持有的是MessageQueue的引用,而loooper是一个static变量,它在这里就是一个GcRoot,所以系统没有办法回收acitvity。