开发Android一般都用遇到使用handler的情况,现在因为rxjava的时候可能就减少了handler的使用。
使用handler需要注意内存泄漏问题(可以通过弱引用Context解决,或者在不需要使用后调用Handler.removeCallbacksAndMessages(null)),当然rxjava也是会出现该种情况(RxLifecycle和AutoDispose都是为了不打断rxjva链式调用设计的生命周期监听)
说到handler总是离不开looper、message、messageQueue的。但是使用Handler的时候也总是离不开Thread之间的使用。
Thread
构建Thread最终会调起init方法。在init方法中仅仅是对thread一些成员变量的初始化而已,并没有任何线程创建的过程。
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
Thread parent = currentThread();
if (g == null) {
g = parent.getThreadGroup();
}
//告诉ThreadGroup中就绪线程+1,但没调用g.add所以没添加进ThreadGroup中
g.addUnstarted();
this.group = g;
this.target = target;
this.priority = parent.getPriority();
this.daemon = parent.isDaemon();
setName(name);
init2(parent);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
tid = nextThreadID();
}
private void init2(Thread parent) {
this.contextClassLoader = parent.getContextClassLoader();
this.inheritedAccessControlContext = AccessController.getContext();
if (parent.inheritableThreadLocals != null) {
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
parent.inheritableThreadLocals);
}
}
线程的使用最后总离不开start()方法。
public synchronized void start() {
// threadStatus==0表示线程未start过
if (threadStatus != 0 || started)
throw new IllegalThreadStateException();
//添加线程进ThreadGroup中,并通知start,减少就绪线程计数器
group.add(this);
started = false;
try {
//真正创建线程的地方
nativeCreate(this, stackSize, daemon);
started = true;
} finally {
try {
if (!started) group.threadStartFailed(this);
} catch (Throwable ignore) {
}
}
}
在native方法中去创建线程,最终通过linux层的pthread方式创建线程并调用Thread中的run的方法指针。这时候的run方法才真正在的子线程中运行。
Looper
使用looper时很简单,短短两句话就可以实现创建一个当前线程的Looper
Looper.prepare();
Looper.loop();
调用prepare方法时,当前线程中只能有一个looper的存在。当多个时会导致RuntimeException。代码中的ThreadLocal是什么呢?最后会稍微说下的。
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));
}
Handler的构造方法有几个重构方法。当传入Looper时,handler使用传入的looper去获取对应message。不传入Looper时使用当前线程Looper(Looper.myLooper())。而在UI线程中早已创建MainLooper(在ActivityThread中的main方法,涉及到activity创建流程,这里不详述了),所以可以直接通过空构建方法使用Handler(这时的Looper.myLooper()==Looper.getMainLooper())
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
OK,说完Looper.prepare()。这时说下Looper.loop(),该方法主要是开启轮询去获取MessageQueue中的Message
public static void loop() {
final Looper me = myLooper();
//略。。。。
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 {
//通过Message.target(Handler)分发处理回调
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//略。。。。
msg.recycleUnchecked();
}
}
上述代码可以看出,Looper主要是通过MessageQueue.next()去获取对应Message。获取到Message后,通过Handler.dispatchMessage方法下发处理Message。最后通过msg.recycleUnchecked回收Message,供Message.obtain复用。
因为Looper.loop方法的循环等待,所以Looper.loop方法是会阻塞当前线程的。所以handler的创建应该房间prepare和loop方法之间。在ActivityThread中也能看到调用Looper.loop()之前有一段代码sMainThreadHandler = thread.getHandler()。其实在主线程中activity的生命周期处理及其他相关操作都是通过内部的H.class的Handler通讯实现的。
MessageQueue
大概逻辑就是当有新的Message插入队列时会唤醒队列,若同步消息处理时间未到则再休眠指定时候后唤醒。
主要看下next方法是怎么获取对应message的。
Message next() {
//检查loop是否已经为退出状态。mPrt是Native层的MessageQueue的地址。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
//如果不是第一次获取消息,调用Native的函数,让虚拟机刷新所有的IBinder命令,确保进程在执行可能阻塞的任务之前,释放之前的对象。
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.
//队列已退出返回空,looper也相应结束
if (mQuitting) {
dispose();
return null;
}
//略。。。。。。
}
//略。。。。。。
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
上面看到,当msg.target为空(即消息屏障)是会忽略同步消息。那在系统中什么时候会创建一个taget为空的消息呢?ViewRootImpl.scheduleTraversals()方法,即是在绘图之前会插入一个消息屏障,绘制之后移除。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//添加消息屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
Message
平时我们一般发送的Message一般为同步Message,可以通过Messasge.setAsynchronous(true)设为异步消息。
Handler.obtain和Message.obtain就简单了,主要是Message内部维持着一个Message链表,获取时先在链表中获取对应缓存Message。Message使用完后,在Looper中通过Message.recycleUnchecked()回收
Handler
Looper的创建需要通过Looper.prepare()来调用,但是每个线程只能有一个Looper。那是不是就意味着每次创建子线程Handler都需要new Thread在Thread内使用Looper的prepare和loop后再创建Handler呢?Android其实还有HandlerThread可以了解一下(不需要子线程处理后,要自行调用quit方法释放资源哟~)。但是要记住的是Looper.loop是会阻塞当前进程的。
剩下Handler就只有post、sendMessage、handleMessage需要说了
post和sendMessage可以合并一块说
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
//最终起调方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
是不是很惊喜很意外,其实最终还是当成了Message处理。通过post方法传入的Runnable最后转为了Message.callback成员。所以Handler无论sendEmptyMessage还是post,最后还是回落到Message.obtain并对Message初始化发送的流程。
但是前面MessageQueue在next方法中已经休眠了,所以在MessageQueue.enqueueMessage会根据添加Message判断是否需要立刻唤醒队列。
boolean enqueueMessage(Message msg, long when) {
//省略判断代码,message无依附Handler或者还在使用中直接抛出异常
synchronized (this) {
if (mQuitting) {
//队列已结束,返回
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//队列为空,唤醒
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 插在中间无需唤醒,插入队列头或者是异步消息则需要唤醒
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;
}
剩下Handler.dispatchMessage(Message msg)需要说明下Handler处理消息流程而已了。在Looper中可以看到dispatchMessage的起调是在Looper.loop方法里。Looper在获取到需要处理的Message之后,调用Message.target.dispatchMessage处起调的。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
代码简单易懂。
- Message有callback,回调message.callback(所以post方法Runable会优先执行)
- Handler有callback,回调Handler.callback
- 最后回落handleMessage
最后在稍微说下Looper中说到的ThreadLocal
ThreadLocal
最后稍微介绍下ThreadLocal。这个类就是保证每个线程只有一个Looper的关键。
这个类主要作用就是线程内数据共享,不同线程为不同副本。
class Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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));
}
ThreadLocal.get方法主要是通过当前线程的ThreadLocalMap去获取当前ThreadLocal的值。
class ThreadLocal
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap是ThreadLocal定义的一个内部类,以ThreadLocal为key值。
- 每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals(存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量))。
- 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
- 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
所以在不同线程中调用ThreadLocal.set()实际上调用的是当前线程中的ThreadLocalMap,从而保证线程安全
而Looper只有通过静态的Looper.prepare()方法去创建Looper,从而保证每个线程只有一个Looper
参考资料
- 你真应该再多了解些Handler机制
- 深入理解MessageQueue
- Linux中pthread线程使用详解
- 源码//滑稽