首先,提出一個問題,下面三處打印輸出的結果是什麼呢?
帶着問題思考一下,然後猜測一下輸出結果,之後我們再帶着問題去探尋源碼;
public class MyActivity extends Activity {
private static final String TAG = MyActivity.class.getSimpleName();
private Button mButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = findViewById(R.id.my_button);
// 打印輸出日誌 1
Log.e(TAG, "打印輸出日誌 1---> onCreate() 獲取View的測量寬度:" + mButton.getWidth());
// 打印輸出日誌 2
mButton.post(new Runnable() {
@Override
public void run() {
Log.e(TAG, "打印輸出日誌 2---> onCreate() 中 通過post方法獲取View的測量寬度:" + mButton.getWidth());
}
});
}
@Override
protected void onResume() {
super.onResume();
// 打印輸出日誌 3
Log.e(TAG, "打印輸出日誌 3---> onResume() 獲取View的測量寬度:" + mButton.getWidth());
}
}
這裏首先給出一下輸出結果,看看給你的答案一致不?
從圖上的打印輸出,我們可以看到,onCreate() 和 onResume() 中我們是獲取不到View的測量值的,在onCreate() 中通過post方法可以獲取到View的測量值;
如果心中仍疑惑不解的,那麼我們就來帶着問題探尋源碼吧…
在Activity的 onCreate() 方法中,我們通過 setContentView(R.layout.activity_view_dispatch) 指定了頁面的佈局,之後我們就可以從我們設置的佈局中獲取到指定的View。爲何我們已經指定了佈局,也可以查找到指定的View,確不能獲取到指定View的測量值呢?
我們來看一下 Activity#setContentView(@LayoutRes int layoutResID) 都做了些什麼?
跟進去查看一下
Activity#setContentView(@LayoutRes int layoutResID):
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow() 方法返回的是一個抽象類 Window,而在Android源碼中的唯一實現是 PhoneWindow,找到 PhoneWindow 查看
PhoneWindow#setContentView(int layoutResID):
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
// 初始化 DecorView
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
......省略代碼
}
PhoneWindow#installDecor():
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 註釋 1
mDecor = generateDecor(-1);
......省略代碼
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 註釋 2
mContentParent = generateLayout(mDecor);
......省略代碼
if (decorContentParent != null) {
......省略代碼--DecorView的父容器配置icon等
} else {
......省略代碼--根據代碼設置,是否展示Title
}
......省略代碼--設置一些轉場動畫等屬性
}
}
註釋 1: 初次加載窗口時,mDecor 爲空,則調用 generateDecor(-1) 方法生成一個 DecorView 對象並返回。
PhoneWindow#generateDecor(int featureId):
protected DecorView generateDecor(int featureId) {
......省略代碼--判斷系統上下文環境Context
return new DecorView(context, featureId, this, getAttributes());
}
該方法很短,就是生成一個 DecorView 對象返回,DecorView extends FrameLayout 不同版本的源碼有稍微的區別,低版本 DecorView 是PhoneWindow的內部類,高版本是一個單獨的類。
註釋 2: 根據條件設置,初始化要加載到 DecorView 中的佈局,我們來看看源碼
PhoneWindow#generateLayout(DecorView decor):
protected ViewGroup generateLayout(DecorView decor) {
......省略代碼
......省略代碼--根據各種主題設置默認佈局等
int layoutResource;
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
} else {
// 默認佈局樣式,無須任何樣式的裝飾
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
// 將佈局解析到 DecorView 中,加載的佈局是一個系統內嵌的佈局
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// ID_ANDROID_CONTENT 是 android.R.id.content, 這個 View 是從 DecorView 裏面去找的,
// 也就是從系統的 layoutResource 裏面找一個id是 android.R.id.content 的一個 FrameLayout,
// 我們在代碼中通過 setContentView 載入的佈局文件就是添加到這個裏面的
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......省略代碼
return contentParent;
}
接下來查看
DecorView#onResourcesLoaded(LayoutInflater inflater, int layoutResource):
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
......省略代碼
// 加載系統內嵌的佈局文件
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root, new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// Put it below the color views.
// 將加載的系統佈局添加到當前 DecorView 中
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
該方法的作用就是,將加載的系統內嵌的佈局文件,通過 DecorView#addView() 方法添加到當前的 DecorView 裏面,DecorView 繼承自FrameLayout,所以這裏調到的是父類 ViewGroup#addView() 方法。
ViewGroup#addView(View child, int index, LayoutParams params):
public void addView(View child, int index, LayoutParams params) {
......省略代碼
// addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
// 此處的 requestLayout 是 View 的方法
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
對於看過源碼的同學,此時是不是該有疑問了?requestLayout() 和 invalidate(true) 方法刷新界面的時候,不會去重新測量,擺放和繪製View的嗎?
結合下圖,這裏我用大白話來解釋一下,平時在開發程序寫界面的時候,遇到需要刷新界面展示的時候,我們可以調用 requestLayout() 和 invalidate() 來進行界面佈局的重新測量、擺放和重繪。只是這個時候測量重繪事件需要通過一層層調用,最終將事件傳遞到最頂層的佈局,也就是ViewRootImpl中,然後由最頂層的佈局一層層的向下傳遞,這也是Android中View的工作原理。這裏不展開說,書上或者很多博客都有講到View工作原理,有興趣的童鞋可以自行查看。
回到剛纔的問題,我們通過 setContentView() 方法,設置佈局,初始化DecorView,然後通過 DecorView 加載所需的系統內嵌佈局,然後再將其由 addView() 加入到 DecorView 中,再刷新 View 的視圖,只是此時我們調用的 requestLayout() 和 invalidate() 是 View 的方法。而我們平時需要測量重繪界面的時候,最終走到的是頂層的 ViewRootImpl 的 requestLayout() 方法,在 setContentView() 方法調用之後,只是初始化了一些佈局,ViewRootImpl 還沒有被創建出來,所以此時走不到 ViewRootImpl 的 requestLayout() 方法,也就不會啓動 View 的一系列測量繪製流程。因此我們自然就獲取不到指定 View 的測量值咯!
那麼 ViewRootImpl 是在哪裏被創建出來的呢?
我們來看一下 Activity 的啓動流程,不詳說,不然有點跑題,博文也太長。
我這裏就當自己做筆記,把之前看的 9.0 系統的啓動流程記錄一下,9.0 系統和之前的代碼不太一樣,我們從 realStartActivityLocked() 這個方法來看,realStartActivityLocked() 之前的流程 9.0 系統之後 和 9.0 系統之前的差不多,從這裏之後,變的不一樣了
先看一下 9.0 以前的源碼
9.0 系統之前 ActivityStackSupervisor#realStartActivityLocked():
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
try {
......省略代碼
// 9.0之前是通過進程間通信的,調用 Activity 的 內部類 ApplicationThread 的 scheduleLaunchActivity
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global and
// override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, !andResume,
mService.isNextTransitionForward(), profilerInfo);
......省略代碼
} catch (RemoteException e) {
......省略代碼
}
......省略代碼
return true;
}
9.0 之前的系統是通過進程間通信的方式,調用 ActivityThread 的內部類 ApplicationThread 的 scheduleLaunchActivity 方法。之後的方法,熟悉源碼的應該都知道吧,發消息,然後由內部類 H 來處理消息,然後走到 handleLaunchActivity() 方法,再走到 performLaunchActivity() 方法等等,不再一一贅述了。
再對比看一下 9.0 之後的源碼
9.0 系統之後 ActivityStackSupervisor#realStartActivityLocked():
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
......省略代碼
try {
try {
......省略代碼
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
......省略代碼
} catch (RemoteException e) {
......省略代碼
}
} finally {
endDeferResume();
}
......省略代碼
return true;
}
9.0 系統之後,這裏是創建了一個事務,然後進行事物的調度。mService.getLifecycleManager() 方法返回一個 ClientLifecycleManager 對象,接下來我們需要找到這個類對象,然後查看
ClientLifecycleManager#scheduleTransaction(ClientTransaction transaction):
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient();
// 繼續調用 ClientTransaction 的 schedule() 方法
transaction.schedule();
if (!(client instanceof Binder)) {
// If client is not an instance of Binder - it's a remote call and at this point it is
// safe to recycle the object. All objects used for local calls will be recycled after
// the transaction is executed on client in ActivityThread.
transaction.recycle();
}
}
該方法很短,裏面繼續調用 ClientTransaction 的 schedule() 方法
ClientTransaction#schedule() :
public void schedule() throws RemoteException {
mClient.scheduleTransaction(this);
}
這個方法也很簡單,這裏的 mClient 是 IApplicationThread 類型的,我們需要找到他的實現類,也就是 ActivityThread 的內部類 ApplicationThread,查看他的
ActivityThread#ApplicationThread#scheduleTransaction(ClientTransaction transaction):
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
ActivityThread.this.scheduleTransaction(transaction);
}
這裏,ActivityThread 中沒有 scheduleTransaction() 方法,我們找到他的父類 ClientTransactionHandler,然後查看
ClientTransactionHandler#scheduleTransaction(ClientTransaction transaction):
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
// 發送消息到子類 ActivityThread 的 內部類 H 進行處理,H繼承自 Handler
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
ActivityThread#H#handleMessage(Message msg):
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
.......省略代碼
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled.
transaction.recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions.
break;
......省略代碼
}
......省略代碼
}
Handler消息處理中,調用了
TransactionExecutor#execute(ClientTransaction transaction):
public void execute(ClientTransaction transaction) {
......省略代碼
// 關鍵代碼 1
executeCallbacks(transaction);
// 關鍵代碼 2
executeLifecycleState(transaction);
mPendingActions.clear();
......省略代碼
}
execute() 關鍵代碼 1
TransactionExecutor#executeCallbacks(ClientTransaction transaction):
public void executeCallbacks(ClientTransaction transaction) {
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
......省略代碼--判空操作,日誌打印
final IBinder token = transaction.getActivityToken();
ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState() : UNDEFINED;
// Index of the last callback that requests some post-execution state.
final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
// 遍歷獲取到 ClientTransactionItem 對象,然後開啓事務
final ClientTransactionItem item = callbacks.get(i);
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
final int postExecutionState = item.getPostExecutionState();
final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
item.getPostExecutionState());
if (closestPreExecutionState != UNDEFINED) {
cycleToPath(r, closestPreExecutionState, transaction);
}
// 執行 ClientTransactionItem 事務
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);
if (r == null) {
// Launch activity request will create an activity record.
r = mTransactionHandler.getActivityClient(token);
}
if (postExecutionState != UNDEFINED && r != null) {
// Skip the very last transition and perform it by explicit state request instead.
final boolean shouldExcludeLastTransition =
i == lastCallbackRequestingState && finalState == postExecutionState;
cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
}
}
}
剛開始看到這裏,我也是一臉懵,這是要幹嘛哈,點進所有能看的,也沒看懂接下來要幹嘛?最後我想看看,這個循環遍歷拿到的 ClientTransactionItem 是幹嘛的?他的 execute() 方法做了什麼?
既然是從列表中 getCallbacks() 取出來的,能取那當然就有存的,我們就來找一下,在哪裏 addCallback() 進去的吧。
前面我們說過,9.0 系統之後 Activity 的啓動流程有了改變,那麼還是回到改變比較大的地方重新找找吧,我們回到
ActivityStackSupervisor#realStartActivityLocked():
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
......省略代碼
try {
......省略代碼
try {
......省略代碼
// Create activity launch transaction.
// 創建一個 Activity 的啓動事務
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.appToken);
final DisplayContent dc = r.getDisplay().mDisplayContent;
// 看到這裏是不是有點小激動了,找到了addCallback,看到了與啓動有點兒關係的類,找到看看吧
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
r.icicle, r.persistentState, results, newIntents,
dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
r.assistToken));
// Set desired final state.
// 看下面的各個類的名字,我們可以猜測這裏是處理和生命週期有關的
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
......省略代碼
} catch (RemoteException e) {
......省略代碼
}
} finally {
endDeferResume();
}
......省略代碼
return true;
}
重看 realStartActivityLocked() 方法,9.0 系統之後改成現在的,創建一個 Activity 的啓動事務,並調度事務來進行 Activity 的啓動,我們看一下這個事務是如何構建的?
public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {
ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
if (instance == null) {
instance = new ClientTransaction();
}
instance.mClient = client;
instance.mActivityToken = activityToken;
return instance;
}
這裏通過 ClientTransaction.obtain() 方法,獲取到一個 ClientTransaction 對象,方法中可以看到,這裏用到了線程池,應該是到線程池中去獲取一個可重用的事務,具體代碼不跟了,擔心越寫越多了,跑偏咯!
然後就是給這個獲取到事務,賦值一些與啓動和生命週期相關的回調事件,至此,我們找到了事務中與啓動和生命週期有關的回調是如何添加的。
回到 TransactionExecutor#executeCallbacks(ClientTransaction transaction): 方法中,我們知道了循環遍歷拿到的 ClientTransaction 對象是 LaunchActivityItem 類,他實現了抽象類 ClientTransactionItem,並重寫其 execute() 方法。
LaunchActivityItem#execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) :
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client, mAssistToken);
// client 的實現類是 ActivityThread
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
到這裏,我們終於看到了熟悉的 ActivityThread#handleLaunchActivity() 方法了,但是這裏和 9.0 系統之前的代碼不一樣,之前的代碼是執行 ActivityThread#performLaunchActivity() 方法,然後走到 ActivityThread#handleResumeActivity() 方法。現在 9.0 系統之後的代碼,ActivityThread#handleLaunchActivity() 方法中,執行了 ActivityThread#performLaunchActivity() 方法之後,沒有了 ActivityThread#handleResumeActivity() 方法。
還記得在 ActivityStackSupervisor#realStartActivityLocked() 方法中添加的與 Activity 生命週期有關的回調嗎?ActivityLifecycleItem 類,他是一個抽象類,ResumeActivityItem 和 PauseActivityItem 都繼承自他。加入之後,他在哪裏執行的呢?回到 TransactionExecutor#execute(ClientTransaction transaction): 中的關鍵代碼 2
execute() 關鍵代碼 2
TransactionExecutor#executeLifecycleState(ClientTransaction transaction):
private void executeLifecycleState(ClientTransaction transaction) {
// realStartActivityLocked() 方法中,由於我們是第一次啓動,andResume爲True
final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
......省略代碼--判空操作
final IBinder token = transaction.getActivityToken();
final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
......省略代碼--日誌打印、判空
// Cycle to the state right before the final requested state.
cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);
// Execute the final transition with proper parameters.
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}
在 realStartActivityLocked() 方法中,由於我們是第一次啓動,andResume爲True,所以添加到事務中的是 ResumeActivityItem 對象,接着執行其 execute() 方法
ResumeActivityItem#execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions):
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
// client 的實現類是 ActivityThread,看到了熟悉的方法
client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
"RESUME_ACTIVITY");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
看到了熟悉的方法,跟進查看
ActivityThread#handleResumeActivity():
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
......省略代碼--判空操作
final Activity a = r.activity;
......省略代碼--日誌打印
final int forwardBit = isForward
? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
......省略代碼
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 (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 執行到 WindowManagerImpl 的 addView()
// 然後會跳轉到 WindowManagerGlobal 的 addView()
wm.addView(decor, l);
}
......省略代碼
}
}
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
......省略代碼--由於第一次啓動,只有添加add操作,暫時用不到更新操作
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
......省略代碼
}
主要看一下 addView() 方法做了什麼?找到實現類 WindowManagerImpl,看一下 addView() 方法
WindowManagerImpl#addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params):
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
// 繼續調用 WindowManagerGlobal 的 addView
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
跟進查看
WindowManagerGlobal#addView():
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
......省略代碼--判空、異常等
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
......省略代碼
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
......省略代碼
// 初始化 ViewRootImpl 的實例
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
// 調用 ViewRootImpl#setView 設置 root 佈局
// 其中 view 爲傳下來的 DecorView 對象
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
該方法中,先初始化一個 ViewRootImpl 實例,也即是上面圖示的,PhoneWindow的根佈局,那麼,我們是不是找到了 ViewRootImpl 在哪裏實例化的了呢?不容易哈!然後調用 ViewRootImpl#setView() 方法
ViewRootImpl#setView():
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
......省略代碼
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
// 調用 ViewRootImpl 的 requestLayout()
requestLayout();
......省略代碼
}
}
}
ViewRootImpl#requestLayout():
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
// 關鍵代碼,這也是很多博客開始分析View的繪製流程的入口
scheduleTraversals();
}
}
ViewRootImpl#scheduleTraversals():
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 通過postSyncBarrier()設置Handler消息的同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// Choreographer 通過 postCallback 提交一個任務,mTraversalRunnable是要執行的回調
// 有了同步屏障mTraversalRunnable就會被優先執行
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
mTraversalRunnable 是 TraversalRunnable 類的實例, TraversalRunnable 是一個實現了 Runnable 接口的任務回調類,他的 run() 方法,中會調用 ViewRootImpl#doTraversal() 方法,ViewRootImpl#doTraversal() 方法中 會調用 ViewRootImpl#performTraversals()。
Choreographer 通過 postCallback 提交一個任務,mTraversalRunnable是要執行的回調,有了同步屏障mTraversalRunnable就會被優先執行,至於爲何有了同步屏障mTraversalRunnable就會被優先執行?可以查看分析Handler之同步屏障機制與Android的屏幕刷新機制在源碼中的應用
ViewRootImpl#performTraversals():
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
mIsInTraversal = true;
......省略代碼
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
// 關注方法 1 performMeasure 測量方法
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
......省略代碼
// 由measureAgain判斷是否需要再次測量
if (measureAgain) {
// 關注方法 1 performMeasure 測量方法
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
} else {
maybeHandleWindowMove(frame);
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
// 關注方法 2 performLayout 位置擺放方法
performLayout(lp, mWidth, mHeight);
......省略代碼
}
......省略代碼
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
// 關注方法 3 performDraw 繪製方法
performDraw();
} else {
......省略代碼
}
mIsInTraversal = false;
}
View 的測繪繪製流程就是從 ViewRootImpl#performTraversals() 開始的,而這個方法的調用是在 onResume() 方法之後,所以在 onCreate() 和 onResume() 方法中拿不到 View 的測量值也就很容易理解了吧。
爲何打印輸出日誌2 可以獲取到 View 的測量值呢?由於篇幅太長了,留待後面再寫吧!
如有不足可以提出,互相交流哈!!!