UI 優化系列專題,來聊一聊 Android 渲染相關知識,主要涉及 UI 渲染背景知識、如何優化 UI 渲染兩部內容。
UI 優化系列專題
- UI 渲染背景知識
《View 繪制流程之 setContentView() 到底做了什么?》
《View 繪制流程之 DecorView 添加至窗口的過程》
《深入 Activity 三部曲(3)View 繪制流程》
《Android 之 LayoutInflater 全面解析》
《關于渲染,你需要了解什么?》
《Android 之 Choreographer 詳細分析》
- 如何優化 UI 渲染
《Android 之如何優化 UI 渲染(上)》
《Android 之如何優化 UI 渲染(下)》
在上一篇文章《View 加載過程之 setContentView() 到底做了什么 ?》,我們分析了 View 繪制流程的整個創建過程,再來簡單回顧下該內容。
- 每個 Activity 都有一個關聯的 Window 對象,用來描述應用程序窗口,每個窗口內部又包含一個 DecorView 對象,DecorView 對象用來描述窗口的視圖 — xml 布局。通過 setContentView() 設置的 View 布局最終添加到 DecorView 的 content 容器中。
DecorView 作為 Activity 的最頂層視圖,它的整個創建過程我們已經知道了,那它又是如何添加至窗口中的呢?還是通過幾個問題來了解下今天要分析的內容。
- DecorView 添加至窗口的過程
- View 繪制流程的開始時機?
- WindowManager 是什么?它的作用有哪些?
- View 繪制流程
- ViewRootImpl 是什么?它的核心工作是什么?
- 為什么 View 的 requestLayout() 最終會執行到 ViewRootImpl 的 requestLayout() ?
- View 繪制流程為什么一定要在 Main 線程?
如果以上問題你都能夠熟練并正確的回答出來,那么恭喜你可以直接跳過該篇文章。
View 的繪制時機
要分析 DecorView 如何添加到窗口上面的,我們要從 Activity 的創建過程開始說起,首先 Activity 的創建以及生命周期的調用,都是通過進程間通信完成的,在應用進程內完成調度任務的是 ActivityThread。ActivityThread 是我們應用進程的入口類(main 函數所在)。
在 ActivityThread 中,完成 Activity 創建任務的方法是 handleLaunchActivity 方法:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//... 省略
// 獲取遠程WindowManagerService代理對象
WindowManagerGlobal.initialize();
// 創建Activity,并調用其onCreate方法
// 創建PhoneWindow、DecorView等
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
//回調Activity的onResume
//也會完成 Actiivty UI 繪制流程
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
}
//... 省略
}
handleLaunchActivity 方法主要完成以下兩部分內容:
performLaunchActivity 方法,完成 Activity 的創建以及 onCreate 生命周期方法回調。
handleResumeActivity 方法,完成 onResume 生命周期方法回調后,發起 DecorView 向窗口添加過程,開始 View 繪制流程。
- performLaunchActivity 方法,Activity 的創建過程。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//... 省略
try {
// 通過反射創建當前Activity實例
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
//... 省略
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
//調用Activity的attach方法,其中PhoneWindow就是該該方法中被創建
activity.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.configCallback);
activity.mCalled = false;
// 回調Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
// 當前Activity賦值給保存它記錄的ActivityClientRecord
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
// 回調Activity的onStart方法
activity.performStart();
r.stopped = false;
}
// Activity的狀態恢復onRestoreInstanceState方法被回調
// 如果Bundle為null,表示無數據,是不會調用onRestoreInstanceState
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
// 回調Activity的onPostCreate方法
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
// 保存當前ActivityClientRecord
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
// ... 省略
}
return activity;
}
系統通過 ClassLoader 反射創建當前 Activity 實例,然后調用 Activity 的 attach 方法,還記的上篇文章《View 繪制流程之 setContentView 到底做了什么?》,PhoneWindow 就是在 attach 方法中被創建的。
attach 方法執行結束后,依次回調 Activity 生命周期函數:onCreate() —> onStart() —> onRestoreInstanceState()(注意該方法不是必須,取決于是否真的保存了數據,即 Bundle != null)—> onPostCreate()。
- handleResumeActivity 方法,View 在 Activity 的繪制起始點。
如果僅從名字來看,大家肯定會想到它最終會回調 Activity 的 onResume 生命周期方法。但實際它可能比我們想象的要復雜。
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
// 獲取當前Activity的ActivityClientRecord
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
mSomeActivitiesChanged = true;
// 執行Activity的onResume生命周期方法
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
// 活動是否可見狀態
willBeVisible = ActivityManager.getService().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
// 重點在這里
if (r.window == null && !a.mFinished && willBeVisible) {
//獲取當前PhoneWindow,就是在setContentView中創建的
r.window = r.activity.getWindow();
//獲取當前Activity的根視圖DecorView
View decor = r.window.getDecorView();
//設置DecorView不可見
decor.setVisibility(View.INVISIBLE);
//WindowManager是一個接口,實現類是WindowManagerImpl
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
//當前DecorView賦值給Activity
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// 首次創建此時,該DecorView還沒有關聯ViewRootImpl
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 這里將DecorView關聯到WindowManager
// 最后也會與ViewRootImpl進行關聯
wm.addView(decor, l);
} else {
//如果Window已經被添加,則只需要更新屬性
a.onWindowAttributesChanged(l);
}
}
} else if (!willBeVisible) {}} else {}
}
先來看下 onResume 生命周期方法的回調過程,performResumeActivity 方法如下:
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide, String reason) {
//獲取當前Activity的ActivityClientRecord
ActivityClientRecord r = mActivities.get(token);
//判斷Activity沒有被Finish
if (r != null && !r.activity.mFinished) {
if (clearHide) {
r.hideForNow = false;
r.activity.mStartedActivity = false;
}
try {
//onStateNotSave方法在onResume之前
//表示當前Activity的狀態不在被保存
//一般在onNewIntent方法或者onActivityResult時可能會使用
r.activity.onStateNotSaved();
//同上,這里是通知該Activity的Fragment
r.activity.mFragments.noteStateNotSaved();
if (r.pendingIntents != null) {
deliverNewIntents(r, r.pendingIntents);
r.pendingIntents = null;
}
if (r.pendingResults != null) {
deliverResults(r, r.pendingResults);
r.pendingResults = null;
}
//回調Activity的onResume方法
r.activity.performResume();
//... 省略
r.paused = false;
r.stopped = false;
r.state = null;
r.persistentState = null;
} catch (Exception e) {
//... 省略
}
}
return r;
}
注意 onStateNotSaved 方法,如果不是看源碼可能還真不一定知道它的存在,Activity 提供了onSaveInstanceState(Bundle outState) 幫助我們保存 Activity 相關狀態信息,onStateNotSaved 方法就是告知我們當前 Activity 相關狀態信息不再被保存。
performResume 方法最終回調 Activity 的 onResume 方法。
不過,我們今天要分析的重點是 DecorView 如何添加到窗口中的?回到
handleResumeActivity 方法,重點部分來了(注意查看上面貼出源碼):
//獲取當前Activity中PhoneWindow,就是在setContentView時關聯的
r.window = r.activity.getWindow();
//獲取當前Activity的根視圖DecorView
View decor = r.window.getDecorView();
//設置DecorView不可見
decor.setVisibility(View.INVISIBLE);
//WindowManager是一個接口,實現類是WindowManagerImpl
//WindowManager是在PhoneWindow創建后,調用其setWindowManager
//被添加,這里直接通過getWindowManager獲取
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
getWindow() 就是在 Activity 的 attach 方法關聯的 PhoneWindow。
getDecorView(),獲取當前 PhoneWindow 中 DecorView,將其置為不可見狀態。
getWindowManager(),獲取當前 PhoneWindow 的
WindowManager。調用 WindowManager 的 addView 方法,將 DecorView 添加到 WindowManager 中。
WindowManager 是何妨神圣?翻看它的源碼發現是一個接口,繼承自 ViewManager:
public interface WindowManager extends ViewManager {
//... 省略
}
ViewManager 中主要包含三個操作 View 的關鍵方法:
public interface ViewManager {
//添加View到Window
public void addView(View view, ViewGroup.LayoutParams params);
//更新View的LayoutParams
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
//在Window中移除該View
public void removeView(View view);
}
那它的實現類是誰呢,其實大家根據 Google 工程師的命名規范也大概能猜到,沒錯 WindowManagerImpl,它是在哪里被創建的呢? 前面我們也有分析過 PhoneWindow 是在 Activity 的 attach 方法中被創建,而 WindowManager 就是在 PhoneWindow 被創建后,調用它的 setWindowManager 方法完成:
// PhoneWindow 的 setWindowManager 方法
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
if (wm == null) {
//這個是SystemServerRegistry中注冊的WindowManagerImpl,即應用進程窗口管理者WindowManager。
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//為當前PhoneWindow創建本地WindowManagerImpl
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
看下代表 Activity 級別的窗口管理者 WindowManager 創建過程:
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
//為當前 PhoneWindow 關聯一個 WindowManagerImpl
return new WindowManagerImpl(mContext, parentWindow);
}
驗證下我們的猜測,WindowManagerImpl 是否實現了 WindowManager ?它的聲明如下:
public final class WindowManagerImpl implements WindowManager {
//實際WindowManagerImpl中的操作代理給了WindowManagerGlobal
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
//... 省略
}
系統為每個 PhoneWindow 都關聯一個 WindowManagerImpl。這里要說明的是,在 Android 中,Window(PhoneWindow) 是 View 的容器,每個窗口都會關聯一個 Surface。而 WindowManager 則負責管理這些窗口,并把它們的數據傳遞給 SurfaceFlinger。
即 addView 方法實際調用到 WindowManagerImpl 中:
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
//丟給了WindowManagerGlobal的addView
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
mGlobal 是 WindowManagerGlobal。它又是何妨神圣?
- 實際上 WindowManager 管理窗口的操作又委托給了 WindowManagerGlobal,WindowMnagerGlobal 是一個單例,即應用進程內僅有一個,這好像也是命名 Global 的原因吧。
WindowManagerGlobal 中主要包含幾個比較重要的 Window 管理容器:
//保存DecorView
private final ArrayList<View> mViews = new ArrayList<View>();
//保存DecorView對應的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//保存DecorView的LayoutParams
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
//保存已經被removeView的DecorView
private final ArraySet<View> mDyingViews = new ArraySet<View>();
WindowManagerImpl 將添加操作委托給 WindowManagerGlobal, addView 方法如下:
//view是DecorView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
//DecorView不能為null
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
//每個Activity都會關聯一個Display
//通過ContextImpl創建
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
//必須是WindowManager.LayoutParams
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
//應用是否配置了硬件加速
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
//硬件加速
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
//重要的ViewRootImpl
//繪制流程的真正發起點
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// ... 省略
//判斷DecorView是否已經添加到Window
int index = findViewLocked(view, false);
//如果已經存在該DecorView
if (index >= 0) {
//此時它必須是已經被移除Window,在mDyingViews容器
if (mDyingViews.contains(view)) {
//如果存在,就將這個已經存在的view對應的window移除
mRoots.get(index).doDie();
} else {
//否則說明已經被添加,不需要再添加
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
// ... 省略
//創建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//將DecorView保存在Window中
mViews.add(view);
//保存當前ViewRootImpl
mRoots.add(root);
mParams.add(wparams);
try {
//調用ViewRootImpl的setView
//參數DecorView
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// ... 省略
}
}
}
- 注意 findViewLocked 方法,如果 DecorView 被重復添加將會拋異常。
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
- 創建 ViewRootImpl 對象,它最后會與當前 DecorView 進行關聯,作為 DecorView 的 parent。ViewRootImpl 是 View 繪制流程的真正發起點。后面會分析到。
- 在 Android 中,所有的元素都在 Surface 這張畫紙上進行繪制和渲染,普通 View(例如非 SurfaceView 或 TextureView) 是沒有 Surface 的,一般 Activity 包含多個 View 形成 View Hierachy 的樹形結構,只有最頂層的 DecorView 才是對 WindowManagerService “可見的”。而為普通 View 提供 Surface 的正是 ViewRootImpl。
保存 DecorView 和對應的 ViewRootImpl 在 WindowManager 中,繼續跟蹤最后 root.setView(view, wparams, panelParentView) 方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
// 判斷是否已經關聯過View
if (mView == null) {
// DecorView賦值到ViewRootImpl中
mView = view;
// 獲取當前屏幕最新的狀態,并且給當前DecorView注冊屏幕監聽,用來檢測滅屏和亮屏
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
// ... 省略
// 獲取屏幕兼容模式,一般情況下不考慮 mTranslator是空的
CompatibilityInfo compatibilityInfo =
mDisplay.getDisplayAdjustments().getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
// If the application owns the surface, don't enable hardware acceleration
if (mSurfaceHolder == null) {
enableHardwareAcceleration(attrs);
}
// ... 省略
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// 繪制流程真正開始的地方
requestLayout();
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
// mWindowSession本質是一個Binder,它的實際類型是Session,這里獲取到的是遠程代理對象
// 發送跨進程消息,將DecorView顯示出來
// mWindow是ViewRootImpl中的一個靜態類,這個類可以用來和WindowSession交互,就等于間接在和WindowManagerService交互
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
// ... 省略
} finally {
if (restore) {
attrs.restore();
}
}
// 處理WindowManagerService的返回結果,這里你會看到一些非常熟悉的異常
// 一大堆兒關于Window操作異常
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
mAdded = false;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
throw new WindowManager.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
// Silently ignore -- we would have just removed it
// right away, anyway.
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- another window of type "
+ mWindowAttributes.type + " already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- permission denied for window type "
+ mWindowAttributes.type);
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified display can not be found");
case WindowManagerGlobal.ADD_INVALID_TYPE:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified window type "
+ mWindowAttributes.type + " is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
// ... 省略
// 將ViewRootImpl添加到DecorView
// 這也是后續調用View.requestLayout,最終會調用到ViewRootImpl的requestLayout
// 調用View的requestLayout會不斷的調用parent.requestLayout,實際最終走到ViewRootImpl的requestLayout
view.assignParent(this);
// ... 省略
}
}
}
- requestLayout 繪制流程的真正起始方法:
// 這個是不是很熟悉
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//檢查操作線程
//Android中檢查UI繪制流程是否在主線程是在ViewRootImpl中
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
注意 checkThread(),相信每個 Android 開發人員都曾遇到過在子線程更新 View 操作會拋異常。
void checkThread() {
// 這就是為什么在子線程對View繪制過程操作會拋異常
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
然后 scheduleTraversals 方法發送 Runnable 消息到主線程,完成 View 繪制的三大流程:measure、layout、draw。這部分內容在下節詳細介紹。
- mWindowSession 本質是一個 Binder 對象,它的實際類型是 Session。每個應用進程都會對應一個 Session。WindowManagerService(WMS) 保存這些 Session 用來記錄所有向 WMS 提出窗口管理服務的客戶端。
//通過WindowManagerService遠程調用為應用進程創建一個Session對象
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
//每個應用進程都關聯一個Session
Session session = new Session(this, callback, client, inputContext);
return session;
}
- mWindow 是 ViewRootImpl 靜態內部類 W,內部通過弱引用持有當前 ViewRootImpl,它將作為客戶端保存在 WMS 中,用來和 WindowSession 交互,實際間接在與 WMS 交互。
//mWindow 是 ViewRootImpl 的靜態內部類
//內部持有當前ViewRootImpl
static class W extends IWindow.Stub {
//弱引用持有當前ViewRootImpl
//因為它將被保存在WindowManagerService
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
mWindowSession = viewAncestor.mWindowSession;
}
// ... 省略
/**
* View 顯示狀態發生變化
* 重新執行scheduleTraversals遍歷過程
*/
public void dispatchAppVisibility(boolean visible) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchAppVisibility(visible);
}
}
/**
* 熟悉的Window焦點發生變化
*/
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
//最終回到View的windowFocusChanged方法
//和Activity的windowFocusChanged方法
viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
}
}
// ... 省略
}
簡單看下它在 WindowManagerService 中的保存:
//mWindowMap是HashMap
//client是應用進程ViewRootImpl中W(mWindow)靜態內部類,這里持有需要窗口服務的代理類
//win是WindowState,保存窗口信息,在WMS用于描述一個窗口
mWindowMap.put(client.asBinder(), win);
注意看 if ( res < WindowManagerGlobal.ADD_OKAY ),這里是不是有很多眼熟的異常信息,一大堆兒異常撲面而來。相信在你的 Bug 記錄中它們肯定出現過。
方法最后 view.assignParent(this),為什么 view 的 requestLayout 方法,最終會調用到 ViewRootImpl 的 requestLayout 方法。答案就在這里。
void assignParent(ViewParent parent) {
if (mParent == null) {
//將ViewRootImpl作為DecorView的parent
mParent = parent;
} else if (parent == null) {
mParent = null;
} else {
throw new RuntimeException("view " + this + " being added, but"
+ " it already has a parent");
}
}
將 ViewRootImpl 作為 DecorView 的 parent。這就是為什么調用 View 的 requestLayout 方法后最終會走到 ViewRootImpl 的 requestLayout 方法。
View 的 requestLayout() 會不斷調用其 Parent 的 requestLayout(),最后調用到 DecorView 的 Parent,此時 Parent 就是 ViewRootImpl。到了 ViewRootImpl 的 requestLayout() 就會重新發起繪制流程。
至此,我們可以回答文章開頭提出的相關問題了。通過一張圖來了解下 DecorView 添加至窗口的過程。
View 在 Activity 的繪制時機是在生命周期函數 onResume 之后,真正開始的方法是在 ViewRootImpl 的 requestLayout()。
WindowManager 只是一個接口,定義了 Window 操作的基礎 API,實現類是 WindowManagerImpl;每個 PhoneWindow 都會關聯一個 WindowManagerImpl,實際管理工作又委托給了 WindowManagerGlobal,負責管理應用進程所有 Window(實際是 DecorView)。
ViewRootImpl 會作為 DecorView 的 parent,View 的 requestLayout 方法會一直調用 parent 的 requestLayout()。即最終會調用到 ViewRootImpl 的 requestLayout 方法。
在 requestLayout 方法會去檢查當前執行 View 繪制的線程,如果不是 Main 線程就會拋出異常。
以上便是個人在學習 DecorView 添加至 Window 過程的學習和體會,文中如有不妥或有更好的分析結果,歡迎大家指出。
文章如果對你有幫助,請留個贊吧!