当前位置: 首页 > news >正文

【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 来处理。
整个窗口的显示逻辑会涉及到下图中的三个模块:

在这里插入图片描述

    1. 应用端: 控制其内部的窗口的添加及 UI 绘制逻辑
    1. WMS 模块: 作为系统窗口管理模块,处理应用端的窗口显示逻辑

应用端与 WMS 通信通过匿名 Binder -> Session 完成

    1. SurfaceFlinger 模块:WMS 只能控制上层的窗口逻辑,真正想要在屏幕上显示内容还需要 SurfaceFlinger 来完成

WMS 与 SurfaceFlinger 的通信通过匿名 Binder -> Clinent 完成

1.2 流程概览

在这里插入图片描述
google 把窗口的显示分为了三个流程:

    1. addWindow流程

    WMS 是维护系统所有窗口的模块,所以应用必须先向 WMS 请求添加窗口,这一阶段 WMS 的处理为:

    • 为应用端创建对应的 WindowState 并挂载
    1. relayoutWindow流程

    addWindow 流程后执行后,屏幕上就有新的 WindowState 添加了,WMS 也需要对屏幕上所有的窗口执行一遍 layout 来确定各个窗口所在的位置。
    而应用端想要绘制 UI 数据,则也需要知道自己的窗口大小,位置信息,并且还需要一个 Surface 来承载 UI 数据。所以这一阶段 WMS 的处理为:

    • 为窗口申请 Surface 并返回给应用端
    • 计算返窗口的大小,位置信息并返回给应用端
    1. finishDrawingWindow流程

    执行完上一流程后,应用端就可以绘制 UI 了,绘制完成后需要将 UI 显示到屏幕上,这一步还是需要 WMS 来完成。

    • 通知 SurfaceFlinger 进行显示这个 Surface

在正式看这3个流程之前,先看一下应用端内是如何创建一个 Window 的。

2. 应用端–Window创建

应用进程启动后,会执行 LaunchActivityItem 和 ResumeActivityItem 这2个事务,对应执行到 Activity 的 onCreate 和 onResume 生命周期,这其中肯定也涉及到了 Window 相关的操作。

调用链如下:

LaunchActivityItem::executeActivityThread::handleLaunchActivityActivityThread::performLaunchActivityInstrumentation::newActivity      -- 创建ActivityActivity::attach                  -- 创建WindowWindow::initWindow::setWindowManagerInstrumentation::callActivityOnCreate  Activity::performCreateActivity::onCreate ResumeActivityItem::executeActivityThread::handleResumeActivityActivityThread::performResumeActivity   Activity::performResume   Instrumentation::callActivityOnResumeActivity::onResume        WindowManagerImpl::addView           -- 创建ViewRootImplWindowManagerGlobal::addView   ViewRootImpl::setView        -- 与WMS通信触发窗口的显示逻辑

根据这个调用链可知:先执行onResume,再执行 addView ,而这个 addView 后的逻辑才是触发 Window 显示流程,所以执行了 onResume 只是 Activity 可见,不代表 View 都显示了,这个时候都没触发 WMS 的绘制,如果后续的流程中出了问题,我们写在 XML 里的布局是不会显示在屏幕上的。(以前写App的时候以为执行了onResume屏幕上就显示UI了)

这一小节只关心 LaunchActivityItem 部分逻辑,看一下应用端创建 Window 的逻辑。

这部分内容和【Activity启动流程-3】末尾内容是重复,熟悉的跳过。

2.1 创建Window流程

# ActivityThreadprivate Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {......// 定义windowWindow window = null;......// token传递的是ActivityRecord的token// 这里的 window 正常逻辑目前还为nullactivity.attach(...,r.token,, window, ...);......}# Activity// 定义Window对象private Window mWindow;final void attach(......) {......// 重点* 1. 创建 PhoneWindowmWindow = new PhoneWindow(this, window, activityConfigCallback);// 一些设置mWindow.setWindowControllerCallback(mWindowControllerCallback);// 重点* 2. 将Activity作为回调设置给WindowmWindow.setCallback(this);......//  重点* 3. 设置token为 ActivityRecordmToken = token;......// 重点* 4.1 set WindowManagermWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);......// 重点* 4.2 get WindowManagermWindowManager = mWindow.getWindowManager();......}public Window getWindow() {return mWindow;}
    1. 日常开发中,通过 Activity::getWindow 方法返回的其实是 PhoneWindow ,这是因为 Window 是个抽象类,而 PhoneWindow 是 Window 的唯一子类
    1. 创建 PhoneWindow 后有一堆设置,这里需要注意 Window::setCallback 方法,是将 Activity 设置给了 Window,这里有什么用呢?
      这个是因为 Activity 也实现了 Window.Callback 接口,所以能传递 this ,这个接口主要是处理 Input 事件的。

Input 事件的传递流程都是先走到 Window,因为在 WMS 模块没有 Activity 的概念,只有 Window ,那么最后是怎么走到 Activity 呢?就是这里设置的 setCallback 。当然这个在当前分析的addWindow 流程没有关系,但是需要有点印象。

    1. 这块token 可以理解为就是 ActivityRecord
    1. setWindowManager 和 getWindowManager 这个两个方法写在这乍一看有点矛盾,在一个地方 set 又 get 感觉很多余。这是因为这里 set 和 get 返回的对象,其实不是同一个对象。

2.2 setWindowManager,getWindowManager

# Window// 应用Tokenprivate 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设置给mAppTokenmAppToken = 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 方法如下:

    # WindowManagerImplpublic 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@Overridepublic 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窗口也会执行)

接下来看一遍代码的执行流程:

# ActivityThreadpublic void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean isForward, String reason) {......// 重点* 1. 触发onResumeif (!performResumeActivity(r, finalStateRequest, reason)) {return;}......// 拿到activityfinal Activity a = r.activity;......if (r.window == null && !a.mFinished && willBeVisible) {// 将window 设置到 activityRecord中r.window = r.activity.getWindow();// 获取DecorViewView decor = r.window.getDecorView();// 设置 View 不可见  decor.setVisibility(View.INVISIBLE);// 实际上是WindowManagerImplViewManager wm = a.getWindowManager();// 获取参数WindowManager.LayoutParams l = r.window.getAttributes();// DecorView设置给Activitya.mDecor = decor;// 重点* 2. 设置Activity的windowType,注意这个type,才是应用的窗口类型l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;......if (a.mVisibleFromClient) {if (!a.mWindowAdded) {// 重点* 3. 执行addView,并设置mWindowAdded=truea.mWindowAdded = true;wm.addView(decor, l);} else {......}}} else if (!willBeVisible) {......}......if (r.activity.mVisibleFromClient) {// 设置可见r.activity.makeVisible();}......}

这段代码信息量还挺多的,相关注释已经加在代码上了,这里对3个重点单独提一下:

    1. performResumeActivity 会触发 onResume 。执行顺序在下面的 addView 之前,说明 onResume 只是 Activity 可见,而不是 UI 可见
    1. Activity 的 WindowType 为 TYPE_BASE_APPLICATION = 1, 还有个 TYPE_APPLICATION=2 目前已知是在创建 ActivityRecord 时使用
    1. 通过 WindowManagerImpl::addView 触发后续逻辑
    # WindowManagerImpl// 单例private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance()@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyTokens(params);// 这里的mParentWindow 就是PhoneWindowmGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());}

这个方法并没有啥复杂的,直接调到了 WindowManagerGlobal,不过这里也有2个需要注意的点:

    1. WindowManagerGlobal 是个单例,那就是说一个进程仅此一个
    1. 这里将mParentWindow传递了过去,上面分析的时候知道这个 mParentWindow 其实就是我们创建的 PhoneWindow
# WindowManagerGlobal// 应用内 View 集合@UnsupportedAppUsageprivate 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 {......}// 定义ViewRootImplViewRootImpl 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. 创建ViewRootImplroot = new ViewRootImpl(view.getContext(), display);} else {root = new ViewRootImpl(view.getContext(), display,windowlessSession);}// 设置参数到 decorViewview.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 thingstry {// 重点* 4. ViewRootImpl::setViewroot.setView(view, wparams, panelParentView, userId);} catch (RuntimeException e) {......}}}

这段代码最重要的就是做了4件事:

    1. Window::adjustLayoutParamsForSubWindow 调整参数,比如设置 token ,title,和硬件加速的标志位这3个比较重要的参数
    1. ViewRootImpl 的创建 (这个类很重要)
    1. WindowManagerGlobal 是进程单例维护了这个应用中多有的 DecorView ,而且看得出来定义了3个集合,且3个集合的元素是一一对应的
    1. 执行 ViewRootImpl::setView 方法,传递的 view 就是 PhoneWindow 持有的 DecorView 。 这里是应用端处理窗口的最重要一步,也是需要分析的主流程。

3.1 ViewRootImpl::setView

到这里要注意的,现在开始就没有对 Window 进行操作了,操作的是 DecorView 。

# ViewRootImpl// 与 WMS 通信的 binderfinal IWindowSession mWindowSession;// 对应的DecorViewView mView;public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {synchronized (this) {// 当前第一次执行肯定为nullif (mView == null) {// 重点* 将DecorView赋值给 mViewmView = view;......mAdded = true; // 表示已经addint res; // 定义稍后跨进程add返回的结果// 重点* 非常重要的方法,relayoutWindow和finishDrawingWindow都在这触发requestLayout();  InputChannel inputChannel = null; // 用于窗口接收inputif ((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::setViewViewRootImpl::requestLayoutViewRootImpl::scheduleTraversals             ViewRootImpl.TraversalRunnable::run          --- Vsync相关--scheduleTraversalsViewRootImpl::doTraversalViewRootImpl::performTraversals ViewRootImpl::relayoutWindow        --- relayoutWindowViewRootImpl::performMeasure        --- View绘制三部曲ViewRootImpl::performLayoutViewRootImpl::performDraw        ViewRootImpl::createSyncIfNeeded    --- 绘制完成finishDrawingWindowSession.addToDisplayAsUser                     --- addWindow

这里要注意:虽然看顺序好像 addWindow 流程是在 relayoutWindow 执行前,但是因为 doTraversal 是异步的,所以还是先执行 addWindow 流程。

可以看到窗口显示的3个流程都在 ViewRootImpl::setView 里触发,在看着3个刘之前,先看2个变量:

    1. mWindowSession 是什么?
    1. addToDisplayAsUser 放到第一个参数里的 mWindow 是什么?

3.2 mWindowSession 和 mWindow

3.2.1 mWindowSession 是什么

# ViewRootImplfinal 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);......}
    1. mWindowSession 是个 Binder 对象,用于当前APP进程与WMS跨进程通信
    1. mWindowSession 的赋值在 ViewRootImpl 构造方法
    1. Activity 场景下分析,在 WindowManagerGlobal::addView 方法中构造 ViewRootImpl 是2个参数的构造方法,所以 mWindowSession 就是 WindowManagerGlobal::getWindowSession

这里提一嘴,既然会这么设计,那么说明在 Framework 中肯定不是这一种方式获取 Session,比如画中画就是另一种,以后会提到,当前留个印象。

接下来就需要看看 WindowManagerGlobal::getWindowSession 方法。

# WindowManagerGlobalprivate static IWindowSession sWindowSession;@UnsupportedAppUsagepublic 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 @UnsupportedAppUsageInputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();IWindowManager windowManager = getWindowManagerService();// 赋值sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {@Overridepublic void onAnimatorScaleChanged(float scale) {ValueAnimator.setDurationScale(scale);}});} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}// 返回return sWindowSession;}}

继续看 WindowManagerService::openSession 。

# WindowManagerService@Overridepublic IWindowSession openSession(IWindowSessionCallback callback) {return new Session(this, callback);}
# Sessionclass Session extends IWindowSession.Stub implements IBinder.DeathRecipient {final WindowManagerService mService;......@Overridepublic 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);}
}
    1. 所以这里的 WindowManagerGlobal::getWindowSession 返回的就是一个 Session 对象。Session 继承 IWindowSession.Stub,并且内部持有 WMS 引用
    1. 应用端调用的 Session::addToDisplayAsUser 最终执行的是 WindowManagerService:: addWindow
    1. Session 是一个匿名 Binder (没有注册到 ServiceManager)

3.2.2 mWindow是什么

调用 Session::addToDisplayAsUser 方法传递了一个 mWindow ,乍一看还以为是把应用端的 Window 对象传递到 WMS 了,但是细想也不可能, WMS 模块的业务并不依赖应用端的 Window 对象,从上一小节的分析也看到了 mWindow 是 W 类型的变量,也是在 ViewRootImpl 的构造方法里赋值的,那这个W是什么呢?

# ViewRootImplfinal 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;}......}
    1. ViewRootImpl 下的 mWindow 是其内部类 W 的对象,这个 W 继承了 IWindow.Stub,那也是用于 binder 通信的
    1. 这个 W 是个匿名 Binder 作为 BN 端,用于 WMS 调用应用端的相关方法
    1. 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 分支

    1. 由 LaunchActivityItem 事务触发 handleLaunchActivity
    1. 创建 Activity
    1. 创建 Window,而 Window 是抽象类,PhoneWindow 唯一实现类。
    1. 执行到 onCreate 生命周期

onResume 分支

    1. 由 ResumeActivityItem 事务触发 handleResumeActivity
    1. 先触发 onResume 的执行流程
    1. 执行 WindowManagerImpl::addView 实际上流程是交给了 WindowManagerGlobal::addView 处理
    1. 创建核心类 ViewRootImpl
    1. 执行关键函数 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 就可以与 应用端的一个窗口进行通信

相关文章:

【Android 14源码分析】WMS-窗口显示-流程概览与应用端流程分析

忽然有一天&#xff0c;我想要做一件事&#xff1a;去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…...

双指针---(部分地更新)

双指针 复写零 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&#xff0c;不要从函数返回任何东西。 …...

【Windows】自定义显示器的分辨率

背景 由于本人更新驱动导致2个显示器里面&#xff0c;有一个显示器的分辨率只剩下2个可以调节 这样就导致2个显示器分辨率不同&#xff0c;更新了多次驱动都修复不了&#xff0c;所以想着看能不能自定义分辨率 工具下载 显示器自定义分辨率工具 或者百度搜索 Custom Resolu…...

组播基础-2-IGMP协议

文章目录 IGMPIGMPv1IGMPv2IGMPv3IGMP总结IGMP Snooping IGMP 运行于主机和路由器之间 因特网组管理协议&#xff0c;TCP/IP 协议族中负责 IP 组播成员管理的协议&#xff0c;用来在接收者与其他直接相邻的组播路由器之间建立、维护组播组成员关系 负责组播成员管理&#xf…...

基于Springboot+Vue的视频点播系统设计与实现登录 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统中…...

执行力怎么培养?

执行力怎么培养&#xff1f; 并行&#xff1a;适合在初期养成习惯&#xff0c;不抱对结果的期望天才就是强迫症&#xff1a;适合中期修身&#xff1a;适合高级 并行&#xff1a;适合在初期养成习惯&#xff0c;不抱对结果的期望 在你开始做任何事情的时候&#xff0c;不要一开…...

Power apps:一次提交多项申请

1、添加一个Form&#xff0c;导入sharepoint列表&#xff0c;添加确认&#xff0c;继续&#xff0c;取消按钮 2、在页面的onvisible属性中添加 Set(applynumber,Last(付款申请表).申请编号1); #定义一个申请编号变量&#xff0c;每次申请&#xff0c;就将列表最后一个…...

Oracle数据库物理结构操作管理

实验步骤 &#xff08;1&#xff09;查询数据库初始化参数中参数名包含sga的参数的名称、值和描述信息。 SQL> select name,value,description from V$PARAMETER where name like %sga%; &#xff08;2&#xff09;设置sga_max_size的大小为1G SQL> alter system set sg…...

Python自然语言处理之spacy模块介绍、安装与常见操作案例

文章目录 spacy模块介绍安装spacy常见操作案例及代码1. 加载模型并处理文本2. 词性标注3. 命名实体识别4. 依存句法分析5. 可视化&#xff08;在Jupyter Notebook中&#xff09; spacy模块介绍 spacy是一个强大的Python库&#xff0c;用于自然语言处理&#xff08;NLP&#xf…...

DSPy101

DSPy 介绍 DSPy&#xff08;Declarative Self-improved Language Programs in Python&#xff09; 是一个用于系统化和增强在流水线内使用语言模型的框架&#xff0c;它通过数据驱动和意图驱动的系统来优化大型语言模型&#xff08;LLM&#xff09;的使用。 DSPy 的核心是模块…...

网格交易策略:从原理、应用到实战Python回测

01 引言 随着金融市场的快速发展&#xff0c;量化交易成为投资者追求收益的一种重要手段。在众多的量化交易策略中&#xff0c;网格交易策略&#xff08;Grid Trading Strategy&#xff09;因其简单易用、风险控制灵活等优点而备受青睐。网格交易策略的核心思想是“低买高卖”&…...

软考论文《论大数据处理架构及其应用》精选试读

论文真题 模型驱动架构设计是一种用于应用系统开发的软件设计方法&#xff0c;以模型构造、模型转换和精化为核心&#xff0c;提供了一套软件设计的指导规范。在模型驱动架构环境下&#xff0c;通过创建出机器可读和高度抽象的模型实现对不同问题域的描述&#xff0c;这些模型…...

fatfs API使用手册

配置 /*---------------------------------------------------------------------------/ / Configurations of FatFs Module /---------------------------------------------------------------------------*/#define FFCONF_DEF 80286 /* Revision ID *//*---------------…...

9.23作业

仿照string类&#xff0c;自己手动实现 My_string 代码如下 MyString.h #ifndef MYSTRING_H #define MYSTRING_H #include <iostream> #include <cstring>using namespace std;class My_string { private:char *ptr; //指向字符数组的指针int size; …...

Unity3D 房间去重叠化算法详解

前言 在Unity3D游戏开发中&#xff0c;经常需要生成和处理多个房间的场景&#xff0c;特别是在地牢生成、房屋布局或迷宫设计等应用中。为了确保生成的房间不会重叠&#xff0c;我们需要一种有效的去重叠化算法。以下将详细介绍该算法的原理和代码实现。 对惹&#xff0c;这里有…...

mybatis 配置文件完成增删改查(五) :单条件 动态sql查询,相当于switch

文章目录 单条件 动态sql查询写测试方法 疑问总结 单条件 动态sql查询 <select id"selectByConditionBySingle" resultMap"brandResultMap">.select *from tb_brandwhere<choose>/*相当于switch*/<when test"status ! null">…...

全球IP归属地查询-IP地址查询-IP城市查询-IP地址归属地-IP地址解析-IP位置查询-IP地址查询API接口

IP地址城市版查询接口 API是指能够根据IP地址查询其所在城市等地理位置信息的API接口。这类接口在网络安全、数据分析、广告投放等多个领域有广泛应用。以下是一些可用的IP地址城市版查询接口API及其简要介绍 1. 快证 IP归属地查询API 特点&#xff1a;支持IPv4 提供高精版、…...

Vue3+FastAPI中Token的刷新机制(含代码示例)

在Vue3和FastAPI的应用中&#xff0c;token刷新机制通常涉及以下几个步骤&#xff1a; 登录过程&#xff1a;用户登录时&#xff0c;后端FastAPI验证用户信息&#xff0c;验证通过后生成一个访问令牌&#xff08;access token&#xff09;和一个刷新令牌&#xff08;refresh t…...

【GAN 图像生成】

理论知识学习&#xff1a; PART 1&#xff1a; 生成对抗网络GAN 深度学习模型&#xff0c;用于生成数据 对抗式训练&#xff0c;生成器v判别器 DCGAN>WGAN>StyleGAN技术不断进化 GAN在艺术创作。数据增强领域应用越来越广泛 应用&#xff1a; GAN在图像合成&#x…...

【自然语言处理】词嵌入模型

词嵌入&#xff08;Word Embedding&#xff09; 是一种将词汇表示为实数向量的技术&#xff0c;通常是低维度的连续向量。这些向量被设计为捕捉词汇之间的语义相似性&#xff0c;使得语义相似的词在嵌入空间中的距离也更近。词嵌入可以看作是将离散的语言符号&#xff08;如单词…...

了解针对基座大语言模型(类似 ChatGPT 的架构,Decoder-only)的重头预训练和微调训练

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 随着自然语言处理&#xff08;NLP&#xff09;技术的飞速进步&#xff0c;基于 Transformer 架构的大语言模型在众多任务中取得了显著成就。特别是 Decoder-only 架构&#xff0c;如 GPT 系列模型&…...

cmake如何在编译时区分-std=c++17和-std=gnu++17?检查宏

如何在编译时区分-stdc17和-stdgnu17&#xff1f;检查宏&#xff1f;-腾讯云开发者社区-腾讯云 我正在使用__int128扩展的g。-stdc17的问题是&#xff0c;一些C库不具备对该扩展的全部支持(即std::make_unsigned<>失败)。当使用-stdgnu17时&#xff0c;它工作得很好。 我…...

速通数据结构与算法第七站 排序

系列文章目录 速通数据结构与算法系列 1 速通数据结构与算法第一站 复杂度 http://t.csdnimg.cn/sxEGF 2 速通数据结构与算法第二站 顺序表 http://t.csdnimg.cn/WVyDb 3 速通数据结构与算法第三站 单链表 http://t.csdnimg.cn/cDpcC 4 速通…...

灵当CRM index.php接口SQL注入漏洞复现 [附POC]

文章目录 灵当CRM index.php接口SQL注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 灵当CRM index.php接口SQL注入漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技…...

修复: Flux女生脸不再油光满面, 屁股下巴 -- 超实用Comfyui小技巧

ComfyUI上目前最强画图模型公认为Flux. 初次用Flux基础模型画真实的女生时, 和SD比起来, 会觉得画出来细节更多, 更真实. 但是当画多了, 就会觉得画出来的女生总是似曾相识. 仔细观察, 会发现一些共同的特征. 人偏老气, 像30~50的女生. 改了提示词也效果不大. 颧骨凸起, 嘴…...

Actions Speak Louder than Words Meta史诗级的端到端推荐大模型落地

发现好久之前整理的推荐系统被遗忘在了草稿箱&#xff0c;让它出来见见世面。。。后续空了持续更新 文章目录 1.Background2.Related works2.1 典型推荐模型2.1.1 DIN2.1.2 DIEN2.1.3 SIM2.1.4 MMoE2.1.5 其他 2.2. 生成式推荐 3.Method3.1 统一特征空间3.2 重塑召回排序模型3.…...

金智维KRPA之Excel自动化

Excel自动化操作概述 Excel自动化主要用于帮助各种类型的企业用户实现Excel数据处理自动化&#xff0c;Excel自动化是可以从单元格、列、行或范围中读取数据&#xff0c;向其他电子表格或工作簿写入数据等活动。 通过相关命令&#xff0c;还可以对数据进行排序、进行格式…...

哪款宠物空气净化器能有效去除浮毛?希喂、352实测分享

你是否曾经站在家电卖场里&#xff0c;面对琳琅满目的宠物空气净化器产品而感到无所适从&#xff1f;或者在浏览网上商城时&#xff0c;被海量的参数和功能描述搞得头晕眼花&#xff1f;别担心&#xff0c;你不是一个人。在这个科技飞速发展的时代&#xff0c;选择一台既能满足…...

2024.9.28更换启辰R30汽车火花塞

2024.9.28周六汽车跑了11万公里&#xff0c;实在加速肉&#xff0c;起步顿挫&#xff0c;油耗在8个&#xff0c;决定更换火花塞。第一个火花塞要拆掉进气歧管。第二和第三个可以直接换。打开第二个火花塞一看电极都被打成深坑&#xff0c;针电极都被打凸。我有两个旧的火花塞&a…...

2024上海网站建设公司哪家比较好TOP3

判断一家网建公司的好坏&#xff0c;第一是看公司背景&#xff0c;包括成立时间&#xff0c;工商注册信息等&#xff0c;第二可以去看看建站公司做的案例&#xff0c;例如&#xff0c;网站开发、设计、引流等等的以往案例&#xff0c;了解清楚具体的业务流程。 一、公司背景 …...

ssh安装wordpress/百度投放平台

#把其他静止的数转化为10进制 ainput(‘需要被转化的数’) bint(input(转化前&#xff0c;数的进制是: )) aint(a,b) print(str(a))...

低价网站建设推广优化/seo外包 杭州

概述&#xff1a; 随着电力供应商品化、市场化的发展&#xff0c;变配电站数量猛增。由于电站分布范围广&#xff0c;距离远&#xff0c;又多处于野外&#xff0c;现场情况不便于管理&#xff0c;因此无人值守的变电站管理模式得到广泛的推广&#xff0c;通过建立变电站设备运行…...

青岛网站定制/明星百度指数在线查询

假设你两个时间戳为$a,$b;你可以用$c$a-$b;(反正就是大的减小的)&#xff0c;这时$c就是两个时间间隔的秒数了。想求两个时间间隔的天数就用:$c/(60*60*24)一天的毫秒数是:86400&#xff0c;所以直接$c/86400 答案也是一样的想求两个时间间隔的小时数就用:$c/(60*60)//PHP 计算…...

设计制作个人网站/国内网络销售平台有哪些

学习内容简单查询汇总分析复杂查询多表查询如何提高SQL查询效率简单查询创建学校数据库的表查找学生查询姓‘猴’的学生名单查询姓名中最后一个字是‘猴’的学生名单查询姓名中带‘猴’的学生名单select * from student where 姓名 like 猴%;select * from student where 姓名 …...

深圳网站建设公司jsp/百度指数在线查询

https://blog.csdn.net/vvyingning/article/details/76087148 https://jingyan.baidu.com/article/e4d08ffdace06e0fd2f60d39.html...

商丘做网站用什么程序比较好/百度品牌专区怎么收费

http://www.ruanyifeng.com/blog/2009/07/xpath_path_expressions.html 简单说&#xff0c;xpath就是选择XML文件中节点的方法。 所谓节点&#xff08;node&#xff09;&#xff0c;就是XML文件的最小构成单位&#xff0c;一共分成7种。 - element&#xff08;元素节点&#xf…...