Handler机制(View.post(Runnable action)原理)
场景
实际开发的过程中,我们一定会遇到.当在Activity 的onCreate() 中,通过 view.getTop()/ View.getWidth() 等方法来获取,控件的长宽等距离属性的时候。往往,事与愿违,获取的结果为0:。
原因: 当我们在获取的时候,View 并没有完成,测量,布局等操作。因此,通过这些方法获取到的参数为0。
这里我们只讨论一下,为什么在 View. post() 方法中可以正确获取呢?
//View.java
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
//用来存储预处理的Runnable ,直到,View被attach 到window 且拥有handler
private HandlerActionQueue mRunQueue;
//发送一个runnable
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
//1.attachInfo 中的handler 发送Runnable
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.
//2.将Runnable 添加到 HandlerActionQueue 中
getRunQueue().post(action);
return true;
}
/**
* Returns the queue of runnable for this view.
*
* @return the queue of runnables for this view
*/
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
}
attachInfo.mHandler.post(action)
AttachInfo :视图附加到其父窗口时所得到的一组信息。里面也包括了UI 线程的Handler。
//View.java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
}
mWindowAttachCount++;
// We will need to evaluate the drawable state at least once.
mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
if (mFloatingTreeObserver != null) {
info.mTreeObserver.merge(mFloatingTreeObserver);
mFloatingTreeObserver = null;
}
registerPendingFrameMetricsObservers();
if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {
mAttachInfo.mScrollContainers.add(this);
mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;
}
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
ListenerInfo li = mListenerInfo;
final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
li != null ? li.mOnAttachStateChangeListeners : null;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
}
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
if (isShown()) {
// Calling onVisibilityAggregated directly here since the subtree will also
// receive dispatchAttachedToWindow and this same call
onVisibilityAggregated(vis == VISIBLE);
}
}
// Send onVisibilityChanged directly instead of dispatchVisibilityChanged.
// As all views in the subtree will already receive dispatchAttachedToWindow
// traversing the subtree again here is not desired.
onVisibilityChanged(this, visibility);
if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0) {
// If nobody has evaluated the drawable state yet, then do it now.
refreshDrawableState();
}
needGlobalAttributesUpdate(false);
notifyEnterOrExitForAutoFillIfNeeded(true);
}
当View 添加到父容器的时候,就会调用 dispatchAttachedToWindow(AttachInfo info, int visibility)方法,接下来会获取当前view 的 mRunQueue ,交给 handler 处理
HandlerActionQueue
//HandlerActionQueue.java
public class HandlerActionQueue {
// 保存HandlerAction 的数组
private HandlerAction[] mActions;
//数组长度
private int mCount;
//HandlerAction 对象持有 runnable 和 delay(延迟执行时间) 对象
private static class HandlerAction {
final Runnable action;
final long delay;
public HandlerAction(Runnable action, long delay) {
this.action = action;
this.delay = delay;
}
public boolean matches(Runnable otherAction) {
return otherAction == null && action == null
|| action != null && action.equals(otherAction);
}
}
//预处理Runnable 加入mActions 数组中
public void post(Runnable action) {
postDelayed(action, 0);
}
//预处理Runnable 加入mActions 数组中
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
//将action追加到mActions尾部(当mActions已经满的话,则自动扩充数组),有兴趣可以看下 GrowingArrayUtils 这个类
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
//移除runnable
public void removeCallbacks(Runnable action) {
synchronized (this) {
final int count = mCount;
int j = 0;
final HandlerAction[] actions = mActions;
for (int i = 0; i < count; i++) {
if (actions[i].matches(action)) {
// Remove this action by overwriting it within
// this loop or nulling it out later.
continue;
}
//将要移除的runnable 后面的runnable往前移动,导致,最后要移除的runnable 排到了数组末尾
if (j != i) {
// At least one previous entry was removed, so
// this one needs to move to the "new" list.
actions[j] = actions[i];
}
j++;
}
// The "new" list only has j entries.
mCount = j;
// Null out any remaining entries.
//将要移除的runnable 置空
for (; j < count; j++) {
actions[j] = null;
}
}
}
//将mActions 中保存的runnable 交给 handler去处理
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;
}
}
public int size() {
return mCount;
}
public Runnable getRunnable(int index) {
if (index >= mCount) {
throw new IndexOutOfBoundsException();
}
return mActions[index].action;
}
public long getDelay(int index) {
if (index >= mCount) {
throw new IndexOutOfBoundsException();
}
return mActions[index].delay;
}
}
当View 发送Runnable的时候:
- 如果 view ,还未attach 到父容器时,没有attachInfo ,则会先将runnable 加入到 HandlerActionQueue 中,等到attach 成功后,则会调用主线程的Hander去处理 这些 runnable
- 如果,view已经attach到父容器,则直接用 attachInfo 中的handler ,去发送Runnable
由此可见,view.post( ) 方法是一个异步的过程。View 在添加到 父容器的时候,已经完成了自己的测量,布局,绘制等操作,因此,在post的runnable 中 ,可以正确获得,它的高,宽,坐标等数值。