Handler是安卓消息体系里面很重要的一个部分,它实现了线程之间的通信,没有它,安卓将无法正常运行。
1.各部分介绍:
1.message:消息;
2.MessageQueue:消息队列;
3.Looper:消息循环器;
4.Handler:可以通过sendMessage()发送message到MessageQueue,之后通过Looper.loop() 开始循环MessageQueue不断获取消息,再将消息交给Handler,由handleMessage()处理消息。
2.原理介绍:
2.1.Handler如何和线程联系起来?
Handler通过Looper和线程联系起来,首先在创建Handler之前要先创建Looper:
public Handler(Callback callback, boolean async) {
//检查当前的线程是否有 Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//Looper 持有一个 MessageQueue
mQueue = mLooper.mQueue;
}
从上面代码可知,在创建Handler的时候,会检查是否创建了Looper,如果没有会报错。
完整例子如下:
class LooperThreadTest extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();//创建Looper,同时创建消息队列
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();//开始循环消息队列
}
}
在查看Looper.prepare():
public static void prepare() {
98 prepare(true);
99 }
private static void prepare(boolean quitAllowed) {
102 if (sThreadLocal.get() != null) {
103 throw new RuntimeException("Only one Looper may be created per thread");
104 }
105 sThreadLocal.set(new Looper(quitAllowed));//创建Looper
106 }
在sThreadLocal.set(new Looper(quitAllowed))中,因为线程中有且只有一个map,然后该线程作为key,新建的Looper对象作为值。然后线程就和Looper绑定了,就这样线程通过Looper和Handler联系起来了。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//线程是常量,是不可变,唯一的。
因为该线程是唯一的,map是唯一的,key是唯一的,再加上在Looper.prepare()中,我们可以知道当key对应的值不为空时,会报错,这就保证了值不可变。这也说明了,一个线程只能有一个Looper对象。但是一个线程可以有多个Handler。
2.2 message的获取和处理
Looper.loop()开启循环消息:
public static void loop() {
138 final Looper me = myLooper();
139 if (me == null) {
140 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
141 }
142 final MessageQueue queue = me.mQueue;//获取消息队列
143
158 //循环获取消息
159 for (;;) {
160 Message msg = queue.next(); // 可能会阻塞
161 if (msg == null) {
162 // 没有消息表示消息队列正在退出。
163 return;
164 }
165
221 //...
232 try {
//处理消息
193 msg.target.dispatchMessage(msg);
194 dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
195 } finally {
196 if (traceTag != 0) {
197 Trace.traceEnd(traceTag);
198 }
199 }
//...
233 msg.recycleUnchecked();
234 }
235 }
//Handler
public void dispatchMessage(Message msg) {
//msg.callback 是 Runnable ,如果是 post方法则会走这个 if
//callback拥有优先处理消息的权利
if (msg.callback != null) {
handleCallback(msg);
} else {
//callback 见【3.4】
if (mCallback != null) {
//利用 Callback 这个拦截机制来拦截 Handler 的消息
if (mCallback.handleMessage(msg)) {
return;
}
}
//回调到 Handler 的 handleMessage 方法
handleMessage(msg);
}
}
Handler.handleMessage()
所在的线程是 Looper.loop() 方法被调用的线程,也可以说成 Looper 所在的线程,并不是创建 Handler 的线程;
2.3.Handler引起内存泄露的原因
Handler作为Activity的内部类,基于Java特性,内部类持有外部类,对Activity有一个引用,而MessageQueue消息队列持有Message消息,Message消息持有Handler,Handler持有Activity,因为MessageQueue消息队列长期存活在内存中,间接长期持有Activity,导致Activity销毁后无法回收。
解决办法:使Handler作为Activity的静态内部类,内部类持有外部类的弱引用,并及时移除所有消息。
private static class SafeHandlerTest extends Handler {
//弱引用
private WeakReference<HandlerActivity> ref;
public SafeHandler(HandlerActivity activity) {
this.ref = new WeakReference(activity);
}
@Override
public void handleMessage(final Message msg) {
HandlerActivity activity = ref.get();
if (activity != null) {
activity.handleMessage(msg);
}
}
}
@Override
protected void onDestroy() {
//移除所有消息
SafeHandlerTest.removeCallbacksAndMessages(null);
super.onDestroy();
}
2.4.为什么主线程可以直接new Handler ?
因为在主线程中的 ActivityThread.main() 方法中,创建了Looper。
//android.app.ActivityThread
public static void main(String[] args) {
//...
Looper.prepareMainLooper();//主线程的Looper对象的创建。
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2.5.主线程和子线程的Looper如何退出?
主线程的Looper不允许退出,退出意味着退出APP,子线程可以调用Looper.quit(),接着会调用MessageQueue.quit(),随后next()会返回null,然后loop()退出了。
2.6.loop()方法是死循环,为什么不会导致应用卡死?
因为当MessageQueue没有消息时,会阻塞在MessageQueue.next()方法中的nativePollOnce(ptr, nextPollTimeoutMillis),之后主线程会释放CPU资源,陷入休眠,等有消息的时候,才会被唤醒。
2.7.创建Message的最佳方式
在安卓中被处理的Message不会被销毁,而是会被复用,节约开销,减少内存消耗。
因此可以采取Message.obtain()和handler.obtainMessage()方法,实际上内部也是Message.obtain()方法。
2.8.使用Handler.postDelay(),消息队列会有怎样变化?
首先调用这个方法,不是延迟一段时间再将消息加入消息队列,而是和队列的时间顺序排序和唤醒相结合实现的。