有个同事问我这个问题,我们往主线程发送一个runnable,有两种方法可以使用,一种是定义一个主线程的handler,通过handler.postRunnable()来执行,一种方法是通过view.post来执行,那么这两种方式有什么区别,具体使用时应该怎么选择。
我们首先要明白的就是两种方式有什么区别,要搞清楚这个问题,就要分析系统究竟是怎么实现的。对于handler.postRunnable 这个不用说,就是往主线程的消息队列扔了一个消息,后面looper会从消息队列取出这条消息,执行这个runnable。
对于view.post 方法就要剖析源码了:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}
首先是判断attachInfo 是否为空,如果不为空的话,就从attachInfo.mHandler来post这个runnable对象,看这个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来往ui线程发送消息;
那这个attachInfo 是什么时候赋值,什么情况下为空,再看源码,
/**
* {@hide}
*/
AttachInfo mAttachInfo;
...
/**
* @param info the {@link android.view.View.AttachInfo} to associated with
* this view
*/
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//System.out.println("Attached! " + this);
mAttachInfo = info;
...
}
mAttachIn默认就是空,dispatchAttachedToWindow这里对mAttachIn赋值,从注释看这个函数的执行时机是这个view被关联到window的时候;
void dispatchDetachedFromWindow() {
....
mAttachInfo = null;
....
}
所以就是当view还没有关联到一个具体的window的时候,这个attachinfo就是空;
根据源码,如果attachinfo未空的话将执行下面的代码:
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
ViewRootImpl.getRunQueue()的源码:
static RunQueue getRunQueue() {
RunQueue rq = sRunQueues.get();
if (rq != null) {
return rq;
}
rq = new RunQueue();
sRunQueues.set(rq);
return rq;
}
再看RunQueue的代码:
/**
* The run queue is used to enqueue pending work from Views when no Handler is
* attached. The work is executed during the next call to performTraversals on
* the thread.
* @hide
*/
static final class RunQueue {
private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
void post(Runnable action) {
postDelayed(action, 0);
}
void postDelayed(Runnable action, long delayMillis) {
HandlerAction handlerAction = new HandlerAction();
handlerAction.action = action;
handlerAction.delay = delayMillis;
synchronized (mActions) {
mActions.add(handlerAction);
}
}
void executeActions(Handler handler) {
synchronized (mActions) {
final ArrayList<HandlerAction> actions = mActions;
final int count = actions.size();
for (int i = 0; i < count; i++) {
final HandlerAction handlerAction = actions.get(i);
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
actions.clear();
}
}
}
看到这里应该明白其实就是往RunQueue的mActions这个ArrayList里添加数据,执行的函数是executeActions(Handler handler),而这个方法的调用地方是在:
private void performTraversals() {
....
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(mAttachInfo.mHandler);
....
}
这就跟清楚了,就是在对view进行绘制的时候会把mActions
里的内容全部通过主线程handler发送到主线程消息队列。
这样就是把view.post分析清楚了;
总结下: view.post也是往主线程发消息,只是发消息要看view的状态,如果view已经被关联到window的话,直接通过handler发送,如果不是的话,则把消息添加到RunQueue.mActions中,到下次view绘制时再把mAction中的消息发到主线程消息队列,这里如果aAction中的runnable对象没有被及时发出去的话,回导致内存泄露,以为runnable常常作为匿名内部类,会持有对外部类的引用.
从使用上看在有view引用变量的情况下,使用view.post方便,不需要自己去定义一个handler,定义handler一不小心就会引起内存泄露。