文章目錄
- Android View#post()源碼分析
- 概述
- onCreate和onResume不能獲取View的寬高
- post可以獲取View的寬高
- 總結
Android View#post()源碼分析
概述
在 Activity 中,在 onCreate() 和 onResume() 中是無法獲取 View 的寬高,可以通過 View#post() 獲取 View 的寬高。
onCreate和onResume不能獲取View的寬高
Activity 的生命周期都是在 ActivityThread 中,當調用 startActivity() ,最終會調用 ActivityThread#performLaunchActivity()。
// ActivityThread#performLaunchActivity()
// 核心代碼:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {Activity activity = null;java.lang.ClassLoader cl = appContext.getClassLoader();// 通過反射新建一個Activityactivity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);try {if (activity != null) {// 創建并初始化Windowactivity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.activityConfigCallback,r.assistToken, r.shareableActivityToken);r.activity = activity;if (r.isPersistable()) {// 回調Activity#onCreate()mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {mInstrumentation.callActivityOnCreate(activity, r.state);} } }return activity;
}
// ActivityThread#handleResumeActivity()
// 核心代碼:
public void handleResumeActivity()(ActivityClientRecord r, boolean finalStateRequest,boolean isForward, String reason) {// 最終會回調Activity#onResume()if (!performResumeActivity(r, finalStateRequest, reason)) {return;}final Activity a = r.activity;if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE); // 設置不可見ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;l.softInputMode |= forwardBit;if (r.mPreserveWindow) {a.mWindowAdded = true;r.mPreserveWindow = false;ViewRootImpl impl = decor.getViewRootImpl();if (impl != null) {impl.notifyChildRebuilt();}}if (a.mVisibleFromClient) {if (!a.mWindowAdded) {a.mWindowAdded = true;// 添加View,開始View的操作wm.addView(decor, l);} else { a.onWindowAttributesChanged(l);}} }
}
說明:Activity 先執行 onCreate(),再執行 onResume(),最后才調用 wm.addView() 將 DecorView 添加到視圖中,也就是從這里才開始執行 View 測量布局繪制流程。簡單說 View 的流程晚于 onResume()。
post可以獲取View的寬高
// View#post()
public boolean post(Runnable action) {final AttachInfo attachInfo = mAttachInfo;if (attachInfo != null) {// 如果attachInfo不為null,表示View已經添加到Window,直接通過Handler發送到主線程隊列return attachInfo.mHandler.post(action);}// 如果attachInfo為null,表示View未添加到Window,暫存在mRunQueue中getRunQueue().post(action);return true;
}private HandlerActionQueue getRunQueue() {if (mRunQueue == null) {mRunQueue = new HandlerActionQueue();}return mRunQueue;
}
說明:在 onCreate() 和 onResume() 中調用 View#post(),最終都會調用 getRunQueue().post(action)。
wm.addView(decor, l) 執行流程:
- 調用 WindowManagerImpl#addView()。
- 調用 WindowManagerGlobal#addView()。
- 執行 new ViewRootImpl,創建 root 對象(布局管理器),并在內部創建 mAttachInfo 對象、Handler 對象。
- 調用 ViewRootImpl#setView()。
// ViewRootImpl#setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {synchronized (this) {if (mView == null) {mView = view;// 請求布局,最終調用ViewRootImpl#performTraversals()requestLayout(); try {// 通過Binder調用WMS添加Windowres = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls); } }}
}
// ViewRootImpl#performTraversals()
private void performTraversals() {final View host = mView;// 調用View#dispatchAttachedToWindow()分發mAttachInfo host.dispatchAttachedToWindow(mAttachInfo, 0);// 測量流程performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);// 布局流程performLayout(lp, mWidth, mHeight);// 繪制流程performDraw()
}
// View#dispatchAttachedToWindow()
void dispatchAttachedToWindow(AttachInfo info, int visibility) {mAttachInfo = info;// 執行緩存任務if (mRunQueue != null) {mRunQueue.executeActions(info.mHandler);mRunQueue = null;}
}
// HandlerActionQueue#executeActions()
public class HandlerActionQueue {private HandlerAction[] mActions;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;}}
}
說明:執行 mRunQueue.executeActions(),會將所有緩存的任務發送到 handler 中,等待主線程執行完 performTraversals() 方法后,就會執行 mActions 中的任務,這時就可以獲取到 View 的寬高。
總結
執行流程:
- Activity#onCreate()
- Activity#onResume()
- WindowManagerImpl#addView()
- new ViewRootImpl()
- ViewRootImpl#setView()
- View的測量流程
- View的布局流程
- View的繪制流程
- WMS添加Window
- 獲取View的寬高