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

news2024/10/2 12:04:35

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
                  
                  
                                           – 服装学院的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::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;
    }

    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
    // 应用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个重点单独提一下:

    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()

        @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个需要注意的点:

    1. WindowManagerGlobal 是个单例,那就是说一个进程仅此一个
    1. 这里将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件事:

    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 通信的 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个变量:

    1. mWindowSession 是什么?
    1. 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);
            ......
        }
    1. mWindowSession 是个 Binder 对象,用于当前APP进程与WMS跨进程通信
    1. mWindowSession 的赋值在 ViewRootImpl 构造方法
    1. 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);
    }
}
    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是什么呢?

# 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;
            }
            ......
        }
    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 就可以与 应用端的一个窗口进行通信

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2184374.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

资源《Arduino 扩展板1-LED灯》说明。

资源链接&#xff1a;Arduino 扩展板1-LED灯 1.文件明细&#xff1a; 2.文件内容说明 包含&#xff1a;AD工程、原理图、PCB。 3.内容展示 4.简述 该文件为PCB工程&#xff0c;采用AD做的。 该文件打板后配合Arduino使用&#xff0c;属于Arduino的扩展板。 该文件主要有…

Pytorch实现RNN实验

一、实验要求 用 Pytorch 模块的 RNN 实现生成唐诗。要求给定一个字能够生成一首唐诗。 二、实验目的 理解循环神经网络&#xff08;RNN&#xff09;的基本原理&#xff1a;通过构建一个基于RNN的诗歌生成模型&#xff0c;学会RNN是如何处理序列数据的&#xff0c;以及如何在…

计算机毕业设计Spark+PyTorch股票预测系统 股票推荐系统 股票可视化 股票数据分析 量化交易系统 股票爬虫 股票K线图 大数据毕业设计 AI

《SparkPyTorch股票预测系统》开题报告 一、研究背景与意义 随着信息技术的飞速发展和全球金融市场的日益繁荣&#xff0c;股票投资已成为广大投资者的重要选择之一。然而&#xff0c;股票市场的复杂性和不确定性使得投资者在做出投资决策时面临巨大的挑战。传统的股票分析方…

防sql注入的网站登录系统设计与实现

课程名称 网络安全 大作业名称 防sql注入的网站登录系统设计与实现 姓名 学号 班级 大 作 业 要 求 结合mysql数据库设计一个web登录页面密码需密文存放&#xff08;可以采用hash方式&#xff0c;建议用sha1或md5加盐&#xff09;采用服务器端的验证码&#…

今天推荐一个文档管理系统 Dorisoy.Pan

Dorisoy.Pan 是一个基于 .NET 8 和 WebAPI 构建的文档管理系统&#xff0c;它集成了 Autofac、MediatR、JWT、EF Core、MySQL 8.0 和 SQL Server 等技术&#xff0c;以实现一个简单、高性能、稳定且安全的解决方案。 这个系统支持多种客户端&#xff0c;包括网站、Android、iO…

PID控制原理:看下这三个故事,你就明白了

一、PID的故事 小明接到这样一个任务&#xff1a;有一个水缸点漏水(而且漏水的速度还不一定固定不变)&#xff0c;要求水面高度维持在某个位置&#xff0c;一旦发现水面高度低于要求位置&#xff0c;就要往水缸里加水。 小明接到任务后就一直守在水缸旁边&#xff0c;时间长就觉…

Python | Leetcode Python题解之第450题删除二叉搜索树中的节点

题目&#xff1a; 题解&#xff1a; class Solution:def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:cur, curParent root, Nonewhile cur and cur.val ! key:curParent curcur cur.left if cur.val > key else cur.rightif cur i…

Linux学习笔记(四):组与权限、任务调度、磁盘管理、网络配置、进程管理

Linux学习笔记&#xff08;四&#xff09;&#xff1a;组与权限、任务调度、磁盘管理、网络配置、进程管理 1. 组与权限 1.1 文件所有者 查看文件所有者&#xff1a; 使用 ls -ahl 或 ll 命令可以查看文件的详细信息&#xff0c;其中包括文件所有者。 修改文件所有者&…

基于SSM的定制衣服系统的设计与实现(定制衣服管理平台的设计与开发、智慧服装定制系统的设计与实现、定制衣服管理系统的设计与实现(源码+定制+参考文档)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

可视化图表与源代码显示配置项及页面的动态调整功能分析

可视化图表与源代码显示配置项及页面的动态调整功能分析 文章目录 可视化图表与源代码显示配置项及页面的动态调整功能分析1.分析图表源代码2.分析源代码显示功能**完整代码参考&#xff1a;** 3.分析源代码显示及动态调整**完整代码参考&#xff1a;** 4.分析代码编辑器及运行…

第1篇:Window日志分析----应急响应之日志分析篇

0x01 Window事件日志简介 Windows系统日志是记录系统中硬件、软件和系统问题的信息&#xff0c;同时还可以监视系统中发生的事件。用户可以通过它来检查错误发生的原因&#xff0c;或者寻找受到攻击时攻击者留下的痕迹。 Windows主要有以下三类日志记录系统事件&#xff1a;应…

一文彻底搞懂多模态 - 多模态理解+视觉大模型+多模态检索

文章目录 技术交流多模态理解一、图像描述1. 基于编码器-解码器的方法2. 基于注意力机制的方法3. 基于生成对抗网络的方法 二、视频描述三、视觉问答 视觉大模型一、通用图像理解模型二、通用图像生成模型 多模态检索一、单模态检索二、多模态检索三、跨模态检索 最近这一两周看…

ml sys

https://zhuanlan.zhihu.com/p/65242220 sys是为了ml落地&#xff1a;机器学习分为若干阶段&#xff1a;数据收集和标定&#xff0c;处理数据&#xff0c;特征工程&#xff0c;编写模型&#xff0c;训练模型&#xff0c;模型管理&#xff0c;模型部署&#xff0c;其实每个阶段…

【LeetCode: 1870. 准时到达的列车最小时速 | 二分】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Reality Expansion Vault:基于Vision Pro + AI的冥想应用

在当今快节奏的生活中,人们常常感到与精神原则脱节。为了帮助用户重新与深层的智慧和洞见建立联系,一款名为Reality Expansion Vault(现实扩展库)的应用应运而生。这款专为Apple Vision Pro设计的应用,通过增强现实技术将精神智慧嵌入用户的环境中,改变人们对世界的看法。…

应用于人形手机器人超小型HarmonicDrive哈默纳科减速机

人形手机器人需要高度的精准性和灵活性以完成各种复杂的任务。减速机的应用&#xff0c;为其提供了关键的动力传输和运动控制支持&#xff0c;它能够将电机的高速旋转转换为适合人形手机器人动作的低速高扭矩输出&#xff0c;确保机器人的动作平稳、准确。HarmonicDrive哈默纳科…

LabVIEW回转支承间隙自动化检测系统

开发了一种基于LabVIEW软件的回转支承间隙检测系统&#xff0c;通过高精度传感器和数据采集卡&#xff0c;自动化、高效地测量回转支承的轴向间隙和径向间隙&#xff0c;提高了检测精度和生产质量。以下是对系统的详细描述与应用案例分析&#xff0c;希望能为有类似需求的开发者…

房屋水电费:重新布局,重构JS代码

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>房租水电费</title><script type"…

Windows 环境安装配置 Python 保姆级教程

Python Python 是一种解释型、高级、通用的编程语言。它由 Guido van Rossum 于 1989 年底发明&#xff0c;并于 1991 年首次发布。Python 的设计哲学强调代码的可读性和简洁的语法&#xff08;尤其是使用空格缩进来表示代码块&#xff0c;而非使用大括号或关键词&#xff09;。…

利用PDLP扩展线性规划求解能力

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…