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 的 requestLaout() 最终会执行到 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 mViews = new ArrayList();
//保存DecorView对应的ViewRootImpl
private final ArrayList mRoots = new ArrayList();
//保存DecorView的LayoutParams
private final ArrayList mParams =
new ArrayList();
//保存已经被removeView的DecorView
private final ArraySet mDyingViews = new ArraySet();
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 mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference(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 过程的学习和体会,文中如有不妥或有更好的分析结果,欢迎大家指出。
文章如果对你有帮助,请留个赞吧!