Handler机制原理解析(二)prepare,loop,post
上一篇已经介绍了Handler机制的原理,如果不熟悉可以看Handler机制原理解析(一)。这一篇,介绍下Handler周边的知识点。我们已经知道,要获得looper对象,必须要先执行prepare。
1,Looper.prepare
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));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
从prepareMainLooper的注释我们能看到,初始化当前线程为looper,并且标记为应用的主looper,这个主looper是Android帮忙创建的,所有不需要手动调用。我们跟踪这个方法。在ActivityThread中我们看到
public static void main(String[] args) {
....
Looper.prepareMainLooper();
....
Looper.loop();
....
}
这里我们只需知道,这个方法会在创建应用的时候调用,并且在主Activity的onCreate前已经调用了即可。至于Activity的启动流程,这里不做详述。这里我们同时也看到了另外一个方法,loop。需要注意的是,如果想在工作线程使用Handler,需要手动调用这两个方法,至于顺序,相信你一定知道。
2,post(Runnable)
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
只有这一句,接着看吧,先看getPostMessage
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
这里将Runnable对象r封装进Message对象了。后面就不用再分析了,因为现在已经变成了send(message),前面我们说过,最终都会调用到一个方法上。发送是相同的,可是处理是不同的。还是贴一下处理的代码吧
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果是发送的runnable,则会走第一个分支
private static void handleCallback(Message message) {
message.callback.run();
}
这里也没啥,就是让其run起来。再回顾一下,无论在哪个线程post一个Runnable,最终都是会通过dispatch方法,让其run起来,而我们知道,dispatch方法是在主线程。那么View的post方法呢,应该也是封装了Handler的post吧,我们一起看看
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
先看第一个分支,如果有attachinfo,就通过handler post出去。这个应该跟handler的方法一样。
/**
* A Handler supplied by a view's {@link android.view.ViewRootImpl}. This
* handler can be used to pump events in the UI events queue.
*/
final Handler mHandler;
看一下Handler,果然一样。这种是view已经attach到window上了,如果没有呢?看另一个分支
public void post(Runnable action) {
postDelayed(action, 0);
}
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
这里还是不清楚,继续往下看
public static <T> T[] append(T[] array, int currentSize, T element) {
assert currentSize <= array.length;
if (currentSize + 1 > array.length) {
@SuppressWarnings("unchecked")
T[] newArray = ArrayUtils.newUnpaddedArray(
(Class<T>) array.getClass().getComponentType(), growSize(currentSize));
System.arraycopy(array, 0, newArray, 0, currentSize);
array = newArray;
}
array[currentSize] = element;
return array;
}
这里能看明白,就是把handlerAction加入到数组中了(Runnable封装在handlerAction中)。这似乎也没得到执行啊。确实
,没办法,既然有post方法,我们姑且尝试看看有没有handle什么之类名字的方法吧。
public void executeActions(Handler handler) {
synchronized (this) {
final HandlerAction[] actions = mActions;
for (int i = 0, count = mCount; i < count; i++) {
final HandlerAction handlerAction = actions[i];
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
mActions = null;
mCount = 0;
}
}
果不其然,能executeAction,并且通过handler post出去了,猜测应该没问题,那就往上找找看吧。在View的dispatchAttachedToWindow中调用的
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
哦,原来当view被attach到window上时,就可以通过handler post出去了。其实呢,跟View post方法的第一个分支一样,只不过保证view一定要执行过attach。