什么是Handler
- Handler是Android SDK来处理异步消息的核心类。用来结合线程的消息队列来发送、处理“Message对象”和“Runnable对象”的工具。每一个Handler实例之后会关联一个线程和该线程的消息队列。当你创建一个Handler的时候,从这时开始,它就会自动关联到所在的线程/消息队列,然后它就会陆续把Message/Runnalbe分发到消息队列,并在它们出队的时候处理掉。
Handler的基本用法
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//处理Message
}
};
//发送消息
handler.sendMessage(message);
handler.post(runnable);
通过重写handleMessage方法我们就可以实例化这个handler。
从以上代码我们并没有看到Looper的身影,那么它有什么用呢?不是说Looper取出消息吗,这样看好像只要使用sendMessage或post发送消息handleMessage接收到处理不就可以了吗,Looper去哪了呢?其实looper已经在存在了,这是因为在主线程被创建的时候会自动创建一个Looper,如果是非主线程,则需要我们手动去创建。
假设我们在非主线程中使用Handler,那么可以这样写
class MyThread extends Thread{
public Handler handler;
@Override
public void run() {
Looper.prepare();
handler = new Handler(){
@Override
public void handleMessage(Message msg){
//处理消息
}
};
Looper.loop();
}
}
源码分析
Message的存储与管理
先看一下sendMessage和post方法的实现
sendMessage
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
post
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
看一下getPostMessage(Runnable r)方法
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
殊途同归,两者其实都是使用的sendMessageDelayed这个方法,因为在post方法中实际会对Runnable对象封装成Message,这里注意一下使用getPostMessage方法对Runnable封装时是把Runnable对象放在了Message.callback里的,下面会讲为什么。
接着往下看我们会捋出来这样一条线
sendMessageDelayed(Message msg, long delayMillis)
——> sendMessageAtTime(Message msg, long uptimeMillis)
——>enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
——>Message.enqueueMessage(Message msg, long when)
OK,终于找到根源了,调用Handler的sendMessage和post方法最终会调用Message.enqueueMessage方法,同时,我们也发现终于和MessageQueue胜利会军,那么enqueueMessage方法是干嘛的呢,我们一起来看一下
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
}
通过enqueueMessage方法我们可以看到这里是把发送的message按照待处理的剩余时间优先级塞到MessageQueue中。
到这里,我们就把发送消息的过程理清晰了,那么,handler又是怎么得到消息的呢?
Message的分发与处理
在上面Handler完成使用的例子中第12行我们看到调用了Looper.loop()方法,这个方法就是用来调度Message的,我们来看一下
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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
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;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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.recycleUnchecked();
}
}
这个方法的实现还挺多的,不过重点也就在那个死循环上,不停的从MessageQueue里取出Message并处理,在第57行我们看到这样一行代码
msg.target.dispatchMessage(msg);
msg.target就是发送该消息的handler,于是在这里又回调到Handler那边了,然后再看下dispatchMessage是做什么的
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
上面我们提到在发送消息的post方法中会把Runnable对象存放在Message.callback中,这里第二行的判断终于出现了不同,可以发现最终在处理消息时,handler对于Runnable消息是做了特殊处理的,先看对于Runnable对象Handler是怎么处理的
private static void handleCallback(Message message) {
message.callback.run();
}
又回到了run方法,在run方法的重写里我们会实现对消息的处理,这个是我们自定义的,而对于sendMesssage方法发送的普通Message会通过回调我们重写的handleMessage方法对消息进行处理,到此可以发现,handler也得到了消息并做处理。
总结
Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。
三者的职责关系简单概括一下:
- Looper :负责关联线程以及消息的分发,在所关联的线程下从 MessageQueue 获取 Message,分发给 Handler ;
- MessageQueue :链表实现的队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message ;
- Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。
扩展
创建 Message 实例的最佳方式
由于 Handler 极为常用,所以为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候尽量复用 Message ,减少内存消耗。
所以我们一般这样实例化Message:
Message msg = Message.obtain();
Message msg = handler.obtainMessage();
Message的回收机制
那么,以上两种方式和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();
}
可以看出这种方式是先尝试从sPool取一个Message,只有sPool为空的情况才会new一个Message对象,这么看sPool像是一个池子来存放可回收的Message,那么,sPool是从哪里回收的Message呢,看一下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 (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
可以看到死循环最后调用了Message的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++;
}
}
}
在这里Message得到了回收,放在sPool里。
Handler引起的内存泄漏
通常我们都会在一个Activity内部定义一个Handler的内部类:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
...
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.lauout.activity_main);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
...
}
}, 10000);
}
}
- 外部类Activity中定义了一个非静态内部类Handler,非静态内部类默认持有对外部类的引用。如果外部Activity突然关闭了,但是MessageQueue中的消息还没处理完,那么Handler就会一直持有对外部Activty的引用,垃圾回收器无法回收Activity,从而导致内存泄漏。
- 如上代码,在postDelayed中,我们在参数中传入一个非静态内部类Runnable,这同样会造成内存泄漏,假如此时关闭了Activity,那么垃圾回收器在接下来的1000000ms内都无法回收Activity,造成内存泄漏。
解决方案
- 将非静态内部类Handler和Runnable转为静态内部类,因为非静态内部类(匿名内部类)都会默认持有对外部类的强引用。
- 改成静态内部类后,对外部类的引用设为弱引用,因为在垃圾回收时,会自动将弱引用的对象回收。
public class HandlerActivity extends AppCompatActivity {
private final MyHandler mHandler = new MyHandler(this);
private static final Runnable mRunnable = new Runnable() {
@Override
public void run() {
// 操作
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fourth);
mHandler.postDelayed(mRunnable, 1000*10);
finish();
}
private static class MyHandler extends Handler {
WeakReference<HandlerActivity> mWeakActivity;
public MyHandler(HandlerActivity activity) {
this.mWeakActivity = new WeakReference<HandlerActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
final HandlerActivity mActivity = mWeakActivity.get();
if (mActivity != null) {
// 处理消息
}
}
}