【Android 14源码分析】WMS-窗口显示-流程概览与应用端流程分析
忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
– 服装学院的IT男
本篇已收录于Activity短暂的一生系列
欢迎一起学习讨论Android应用开发或者WMS
V:WJB6995
Q:707409815
窗口显示流程一共分为以下5篇:
窗口显示-流程概览与应用端流程分析
窗口显示-第一步:addWindow
窗口显示-第二步:relayoutWindow -1
窗口显示-第二步:relayoutWindow -2
窗口显示-第三步:finishDrawingWindow
正文
在了解完Activity启动流程后,现在目标应用的进程已经启动了,但是离用户在屏幕上看到Activity下的UI内容还有一段距离。
本文以 WMS 的角度来分析这个流程。
1. 整体流程概述
1.1 涉及模块
WMS 负责管理设备上所有的 Window ,所以应用想显示一个 Window 则要通过 WMS 来完成。
而 WMS 毕竟还是上层,窗口的内容要显示到屏幕上,还需要 SurfaceFlinger 来处理。
整个窗口的显示逻辑会涉及到下图中的三个模块:
-
- 应用端: 控制其内部的窗口的添加及 UI 绘制逻辑
-
- WMS 模块: 作为系统窗口管理模块,处理应用端的窗口显示逻辑
应用端与 WMS 通信通过匿名 Binder -> Session 完成
-
- SurfaceFlinger 模块:WMS 只能控制上层的窗口逻辑,真正想要在屏幕上显示内容还需要 SurfaceFlinger 来完成
WMS 与 SurfaceFlinger 的通信通过匿名 Binder -> Clinent 完成
1.2 流程概览
google 把窗口的显示分为了三个流程:
-
- addWindow流程
WMS 是维护系统所有窗口的模块,所以应用必须先向 WMS 请求添加窗口,这一阶段 WMS 的处理为:
- 为应用端创建对应的 WindowState 并挂载
-
- relayoutWindow流程
addWindow 流程后执行后,屏幕上就有新的 WindowState 添加了,WMS 也需要对屏幕上所有的窗口执行一遍 layout 来确定各个窗口所在的位置。
而应用端想要绘制 UI 数据,则也需要知道自己的窗口大小,位置信息,并且还需要一个 Surface 来承载 UI 数据。所以这一阶段 WMS 的处理为:- 为窗口申请 Surface 并返回给应用端
- 计算返窗口的大小,位置信息并返回给应用端
-
- finishDrawingWindow流程
执行完上一流程后,应用端就可以绘制 UI 了,绘制完成后需要将 UI 显示到屏幕上,这一步还是需要 WMS 来完成。
- 通知 SurfaceFlinger 进行显示这个 Surface
在正式看这3个流程之前,先看一下应用端内是如何创建一个 Window 的。
2. 应用端–Window创建
应用进程启动后,会执行 LaunchActivityItem 和 ResumeActivityItem 这2个事务,对应执行到 Activity 的 onCreate 和 onResume 生命周期,这其中肯定也涉及到了 Window 相关的操作。
调用链如下:
LaunchActivityItem::execute
ActivityThread::handleLaunchActivity
ActivityThread::performLaunchActivity
Instrumentation::newActivity -- 创建Activity
Activity::attach -- 创建Window
Window::init
Window::setWindowManager
Instrumentation::callActivityOnCreate
Activity::performCreate
Activity::onCreate
ResumeActivityItem::execute
ActivityThread::handleResumeActivity
ActivityThread::performResumeActivity
Activity::performResume
Instrumentation::callActivityOnResume
Activity::onResume
WindowManagerImpl::addView -- 创建ViewRootImpl
WindowManagerGlobal::addView
ViewRootImpl::setView -- 与WMS通信触发窗口的显示逻辑
根据这个调用链可知:先执行onResume,再执行 addView ,而这个 addView 后的逻辑才是触发 Window 显示流程,所以执行了 onResume 只是 Activity 可见,不代表 View 都显示了,这个时候都没触发 WMS 的绘制,如果后续的流程中出了问题,我们写在 XML 里的布局是不会显示在屏幕上的。(以前写App的时候以为执行了onResume屏幕上就显示UI了)
这一小节只关心 LaunchActivityItem 部分逻辑,看一下应用端创建 Window 的逻辑。
这部分内容和【Activity启动流程-3】末尾内容是重复,熟悉的跳过。
2.1 创建Window流程
# ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
// 定义window
Window window = null;
......
// token传递的是ActivityRecord的token
// 这里的 window 正常逻辑目前还为null
activity.attach(...,r.token,, window, ...);
......
}
# Activity
// 定义Window对象
private Window mWindow;
final void attach(......) {
......
// 重点* 1. 创建 PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
// 一些设置
mWindow.setWindowControllerCallback(mWindowControllerCallback);
// 重点* 2. 将Activity作为回调设置给Window
mWindow.setCallback(this);
......
// 重点* 3. 设置token为 ActivityRecord
mToken = token;
......
// 重点* 4.1 set WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
......
// 重点* 4.2 get WindowManager
mWindowManager = mWindow.getWindowManager();
......
}
public Window getWindow() {
return mWindow;
}
-
- 日常开发中,通过 Activity::getWindow 方法返回的其实是 PhoneWindow ,这是因为 Window 是个抽象类,而 PhoneWindow 是 Window 的唯一子类
-
- 创建 PhoneWindow 后有一堆设置,这里需要注意 Window::setCallback 方法,是将 Activity 设置给了 Window,这里有什么用呢?
这个是因为 Activity 也实现了 Window.Callback 接口,所以能传递 this ,这个接口主要是处理 Input 事件的。
- 创建 PhoneWindow 后有一堆设置,这里需要注意 Window::setCallback 方法,是将 Activity 设置给了 Window,这里有什么用呢?
Input 事件的传递流程都是先走到 Window,因为在 WMS 模块没有 Activity 的概念,只有 Window ,那么最后是怎么走到 Activity 呢?就是这里设置的 setCallback 。当然这个在当前分析的addWindow 流程没有关系,但是需要有点印象。
-
- 这块token 可以理解为就是 ActivityRecord
-
- setWindowManager 和 getWindowManager 这个两个方法写在这乍一看有点矛盾,在一个地方 set 又 get 感觉很多余。这是因为这里 set 和 get 返回的对象,其实不是同一个对象。
2.2 setWindowManager,getWindowManager
# Window
// 应用Token
private IBinder mAppToken;
private WindowManager mWindowManager;
// wm :WindowManager对象,注意下传进来的值
// appToken :这个就是AMS中与当前Activity对应的ActivityRecord
// appName :Activity全类名
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
// 将ActivityRecord设置给mAppToken
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
// 根据强转也能看出 mWindowManager 是 WindowManagerImpl 的类型
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
public WindowManager getWindowManager() {
return mWindowManager;
}
这里将传递进来的 wm 强转成 WindowManagerImpl 后调用其 createLocalWindowManager方法。
看的出来这个 wm 就是 WindowManagerImpl 类型。
这里的 WindowManager 其实就是一个接口,看命令就 Window 的管理者,FrameWork 有很多跨进程通信的类命名方式就是 BP 端叫 XXManager 然后对应的 BN 端就是 XXManagerService 。
但是! 这里的 WindowManager 不是这样的,他真的就是一个单纯的 java 语言里的接口,定义了一些约束,然后由 WindowManagerImpl 实现。
另外这里是通过 “getSystemService” 这份方式获取的,也容易给人误解他是一个 Service 。其实这个 WindowManager 和其实现类 WindowManagerImpl 与 WindowManagerService 几乎一点关系都没有。
WindowManager 又实现了 ViewManager 接口,这个接口中定义了对 View 的三个操作:addView ,removeView ,updateViewLayout 。
继续看流程,返回值想 WindowManagerImpl::createLocalWindowManager 该方法重新创建返回了一个 WindowManagerImpl 对象。 所以说setWindowManager 和 setWindowManager 的不是同一个对象, WindowManagerImpl::createLocalWindowManager 方法如下:
# WindowManagerImpl
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
}
private WindowManagerImpl(Context context, Window parentWindow,
@Nullable IBinder windowContextToken) {
mContext = context;
mParentWindow = parentWindow;
mWindowContextToken = windowContextToken;
}
这边注意的是将 Window 设置给了 mParentWindow。 相当于通过新创建的PhonWindow创建了一个 WindowManagerImpl ,作为其mWindowManager的对象。
到这里创建Window相关的就分析完了,创建的这个Window其实是 PhoneWindow 。
建议要理清楚 Window, PhoneWindow ,WindowManagerImpl , WindowManager 这几个类的区别和联系,不要搞混了
2.3 DecorView
每个 Window 都拥有一个 View
# PhoneWindow.java
// This is the top-level view of the window, containing the window decor.
// 翻译:这是窗户的顶层视图,包含窗户装饰。
private DecorView mDecor;
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
......
}......
}
// 返回 DecorView
@Override
public final @NonNull View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
// 为空就创建
installDecor();
}
return mDecor;
}
这个 mDecor 是 DecorView 本质上也是一个 View ,在 PhoneWindow::generateDecor 方法赋值,这个方法有多处会执行,最常见的就是我们在 Activity::onCreate 里执行 setContentView 的时候。
# DecorView.java
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
......
}
也就是说 Window 通过 mDecor 的变量持有一个 View。
3. 应用端–Window的显示流程
这部分流程由应用端 ResumeActivityItem 开始事务触发,最终会执行到 Activity::onResume 生命周期,并且也会执行 ViewRootImpl::setView 方法,这个方法才是触发 窗口显示的真正逻辑。(非Activity窗口也会执行)
接下来看一遍代码的执行流程:
# ActivityThread
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
......
// 重点* 1. 触发onResume
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
......
// 拿到activity
final Activity a = r.activity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
// 将window 设置到 activityRecord中
r.window = r.activity.getWindow();
// 获取DecorView
View decor = r.window.getDecorView();
// 设置 View 不可见
decor.setVisibility(View.INVISIBLE);
// 实际上是WindowManagerImpl
ViewManager wm = a.getWindowManager();
// 获取参数
WindowManager.LayoutParams l = r.window.getAttributes();
// DecorView设置给Activity
a.mDecor = decor;
// 重点* 2. 设置Activity的windowType,注意这个type,才是应用的窗口类型
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
......
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
// 重点* 3. 执行addView,并设置mWindowAdded=true
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
......
}
}
} else if (!willBeVisible) {
......
}
......
if (r.activity.mVisibleFromClient) {
// 设置可见
r.activity.makeVisible();
}
......
}
这段代码信息量还挺多的,相关注释已经加在代码上了,这里对3个重点单独提一下:
-
- performResumeActivity 会触发 onResume 。执行顺序在下面的 addView 之前,说明 onResume 只是 Activity 可见,而不是 UI 可见
-
- Activity 的 WindowType 为 TYPE_BASE_APPLICATION = 1, 还有个 TYPE_APPLICATION=2 目前已知是在创建 ActivityRecord 时使用
-
- 通过 WindowManagerImpl::addView 触发后续逻辑
# WindowManagerImpl
// 单例
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance()
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
// 这里的mParentWindow 就是PhoneWindow
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
这个方法并没有啥复杂的,直接调到了 WindowManagerGlobal,不过这里也有2个需要注意的点:
-
- WindowManagerGlobal 是个单例,那就是说一个进程仅此一个
-
- 这里将mParentWindow传递了过去,上面分析的时候知道这个 mParentWindow 其实就是我们创建的 PhoneWindow
# WindowManagerGlobal
// 应用内 View 集合
@UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
@UnsupportedAppUsage
// 应用内 ViewRootImpl 集合
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
@UnsupportedAppUsage
// 应用内 View 参数 集合
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
......
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
// 重点* 1. 调整 window参数,
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
......
}
// 定义ViewRootImpl
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
......
// 如果一个窗口执行过 addView 的话,再执行就报错
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
......
IWindowSession windowlessSession = null;
......
// 对于应用来说 windowlessSession 是为null的
if (windowlessSession == null) {
// 重点* 2. 创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession);
}
// 设置参数到 decorView
view.setLayoutParams(wparams);
// 重点* 3. 添加到对应集合,看得出来在WindowManagerGlobal中这3个对象应该是要一一对应的
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
// 重点* 4. ViewRootImpl::setView
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
......
}
}
}
这段代码最重要的就是做了4件事:
-
- Window::adjustLayoutParamsForSubWindow 调整参数,比如设置 token ,title,和硬件加速的标志位这3个比较重要的参数
-
- ViewRootImpl 的创建 (这个类很重要)
-
- WindowManagerGlobal 是进程单例维护了这个应用中多有的 DecorView ,而且看得出来定义了3个集合,且3个集合的元素是一一对应的
-
- 执行 ViewRootImpl::setView 方法,传递的 view 就是 PhoneWindow 持有的 DecorView 。 这里是应用端处理窗口的最重要一步,也是需要分析的主流程。
3.1 ViewRootImpl::setView
到这里要注意的,现在开始就没有对 Window 进行操作了,操作的是 DecorView 。
# ViewRootImpl
// 与 WMS 通信的 binder
final IWindowSession mWindowSession;
// 对应的DecorView
View mView;
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
// 当前第一次执行肯定为null
if (mView == null) {
// 重点* 将DecorView赋值给 mView
mView = view;
......
mAdded = true; // 表示已经add
int res; // 定义稍后跨进程add返回的结果
// 重点* 非常重要的方法,relayoutWindow和finishDrawingWindow都在这触发
requestLayout();
InputChannel inputChannel = null; // 用于窗口接收input
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
......
try {
......
// 重点* 这里通过binder通信,调用WMS的 addWindow方法
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls);
......
}
// 后续流程与addWindow主流程无关,但是也非常重要
......
// 计算一次 Window的尺寸
final WindowConfiguration winConfig = getConfiguration().windowConfiguration;
mWindowLayout.computeFrames(mWindowAttributes, state,
displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),
UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
mInsetsController.getRequestedVisibilities(),
getAttachedWindowFrame(), 1f /* compactScale */, mTmpFrames);
setFrame(mTmpFrames.frame);
......
if (res < WindowManagerGlobal.ADD_OKAY) {
......// 对WMS调用addWindow后的结果判断是什么错误
}
......
// DecorView::getParent 返回的是 ViewRootImpl 的原因
view.assignParent(this);
......
}
}
}
这个方法是核心方法,处理了很多事,注释都加上了。为了有一个宏观的印象,这里将其触发的各个调用链整理如下:
ViewRootImpl::setView
ViewRootImpl::requestLayout
ViewRootImpl::scheduleTraversals
ViewRootImpl.TraversalRunnable::run --- Vsync相关--scheduleTraversals
ViewRootImpl::doTraversal
ViewRootImpl::performTraversals
ViewRootImpl::relayoutWindow --- relayoutWindow
ViewRootImpl::performMeasure --- View绘制三部曲
ViewRootImpl::performLayout
ViewRootImpl::performDraw
ViewRootImpl::createSyncIfNeeded --- 绘制完成finishDrawing
WindowSession.addToDisplayAsUser --- addWindow
这里要注意:虽然看顺序好像 addWindow 流程是在 relayoutWindow 执行前,但是因为 doTraversal 是异步的,所以还是先执行 addWindow 流程。
可以看到窗口显示的3个流程都在 ViewRootImpl::setView 里触发,在看着3个刘之前,先看2个变量:
-
- mWindowSession 是什么?
-
- addToDisplayAsUser 放到第一个参数里的 mWindow 是什么?
3.2 mWindowSession 和 mWindow
3.2.1 mWindowSession 是什么
# ViewRootImpl
final W mWindow;
final IWindowSession mWindowSession;
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
......
mWindow = new W(this);
......
}
-
- mWindowSession 是个 Binder 对象,用于当前APP进程与WMS跨进程通信
-
- mWindowSession 的赋值在 ViewRootImpl 构造方法
-
- Activity 场景下分析,在 WindowManagerGlobal::addView 方法中构造 ViewRootImpl 是2个参数的构造方法,所以 mWindowSession 就是 WindowManagerGlobal::getWindowSession
这里提一嘴,既然会这么设计,那么说明在 Framework 中肯定不是这一种方式获取 Session,比如画中画就是另一种,以后会提到,当前留个印象。
接下来就需要看看 WindowManagerGlobal::getWindowSession 方法。
# WindowManagerGlobal
private static IWindowSession sWindowSession;
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
// Emulate the legacy behavior. The global instance of InputMethodManager
// was instantiated here.
// TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
IWindowManager windowManager = getWindowManagerService();
// 赋值
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
// 返回
return sWindowSession;
}
}
继续看 WindowManagerService::openSession 。
# WindowManagerService
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
return new Session(this, callback);
}
# Session
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
......
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
}
}
-
- 所以这里的 WindowManagerGlobal::getWindowSession 返回的就是一个 Session 对象。Session 继承 IWindowSession.Stub,并且内部持有 WMS 引用
-
- 应用端调用的 Session::addToDisplayAsUser 最终执行的是 WindowManagerService:: addWindow
-
- Session 是一个匿名 Binder (没有注册到 ServiceManager)
3.2.2 mWindow是什么
调用 Session::addToDisplayAsUser 方法传递了一个 mWindow ,乍一看还以为是把应用端的 Window 对象传递到 WMS 了,但是细想也不可能, WMS 模块的业务并不依赖应用端的 Window 对象,从上一小节的分析也看到了 mWindow 是 W 类型的变量,也是在 ViewRootImpl 的构造方法里赋值的,那这个W是什么呢?
# ViewRootImpl
final W mWindow;
final IWindowSession mWindowSession;
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
......
mWindow = new W(this);
......
}
static class W extends IWindow.Stub {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
mWindowSession = viewAncestor.mWindowSession;
}
......
}
-
- ViewRootImpl 下的 mWindow 是其内部类 W 的对象,这个 W 继承了 IWindow.Stub,那也是用于 binder 通信的
-
- 这个 W 是个匿名 Binder 作为 BN 端,用于 WMS 调用应用端的相关方法
-
- W 内部有一个 ViewRootImpl 弱引用。
3.2.3 跨进程通信小结
我们知道 WMS 是实名 Binder Service 。从可行性上来说 ViewRootImpl 是完全可以直接通过 Binder 与 WMS 进行直接通信的,但是为什么要加这个 IWindowSession 呢?
这是因为 WMS 是核心服务,系统中有很多地方都需要与其通信,所以如果能减少直接与其 Binder 通信的频率也能提升系统效率。
所以 WMS 为每个 APP 端提供了一个匿名实现:IWindowSession ,处理一些“小请求”,减少各个进程直接与 WMS Binder 通信的频率。
这个和 SurfaceFlinger 的设计一致,SurfaceFlinger 也提供了一个匿名 Binder :Client 。
(观点参考-- 《深入理解Android内核设计思想》)
4. APP进程处理小结
4.1 流程小结
应用端的流程在 ViewRootImpl::setView 方法中就结束了,内部会触发3大流程。
应用进程启动后,会执行 2 个事务,分别触发到 Activity 的 onCreate 和 onResume 2个常见的生命周期,所以这里分为了2个分支。
onCreate 分支:
-
- 由 LaunchActivityItem 事务触发 handleLaunchActivity
-
- 创建 Activity
-
- 创建 Window,而 Window 是抽象类,PhoneWindow 唯一实现类。
-
- 执行到 onCreate 生命周期
onResume 分支:
-
- 由 ResumeActivityItem 事务触发 handleResumeActivity
-
- 先触发 onResume 的执行流程
-
- 执行 WindowManagerImpl::addView 实际上流程是交给了 WindowManagerGlobal::addView 处理
-
- 创建核心类 ViewRootImpl
-
- 执行关键函数 ViewRootImpl::setView ,这里会触发 WMS 三大流程
3大流程的逻辑还没看,但是可以先有个概念:
- addWindow 流程: ViewRootImpl 调用 Session::addToDisplayAsUser 触发 WindowManagerService::addWindow
- relayoutWindow 流程:ViewRootImpl 调用 Session::relayout 触发 WindowManagerService::relayoutWindow
- finishDrawingWindow 流程:ViewRootImpl 调用 Session::finishDrawing 触发 WindowManagerService::finishDrawingWindow
4.2 关键类小结
给上面分析遇到的一些关键类的知识点单独做个小结:
-
Activity :
- 是通过反射创建的 ,通过成员变量 mWindow 持有一个 Window
- 一个应用可以有多个 Activity ,而每个 Activity 的结构都是刚才分析的,这些 Activity 被存在 ActivityThread 下的 mActivities 的变量中
-
PhoneWindow,Window :Window 是个抽象类,唯一实现是 PhoneWindow ,通过成员变量 mDecor 持有一个 View
-
DecorView : 本质上也是一个 View 是开发常说的 “根View” ,Window 本身是没有内容的,真正 UI 载体就是这个 DecorView
-
ViewManager ,WindowManager : 前面2者都是接口类,WindowManagerImpl 实现其接口
-
WindowManagerImpl :WindowManagerImpl 表面上是管理 Window ,但是实际业务还是交给了 WindowManagerGlobal
-
WindowManagerGlobal :
- 应用内全局单例,是真正的应用内 Window 的总管家,但是其实管理的是 Window 下的 DecorView
- 当应用内有 Window 创建后,WindowManagerImpl 会调用 WindowManagerGlobal::addView 方法将 DecorView 传递过去
- WindowManagerGlobal 会为每个 DecorView 创建一个 ViewRootImpl 来管理这棵“View树”
- 内部有3个集合,将 Window 的 DecorView 参数 LayoutParams 和新创建的 ViewRootImpl 一一添加到对应的集合中
-
ViewRootImpl :在 GUI 系统中是应用端的核心类,管理一个“View树”,负责这个“View树”的绘制等事务,同时还需要控制其与 WMS 通信
-
Session : WMS 为应用端的单独开放的跨进程通信通道
-
W : 应用端的 Binder Service ,会传递到 WMS 中,这样 WMS 就可以与 应用端的一个窗口进行通信