Android 图形架构 之五—— 深入分析addView所发生的的一切

前言

前几篇文章,分析了在SurfaceFlinger 进程,WMS进程 中,图形架构的流程和关键类的介绍。现在我们来分析一下,app进程中是如何与这些进程进行交互,以及何时交互。

Android 图形架构 之一 ——概述
Android 图形架构 之二—— SurfaceFlinger 启动和连接
Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl
Android 图形架构 之四——图形缓冲区的申请和消费流程及核心类
Android 图形架构 之五——深入分析addView所发生的的一切
Android 图形架构 之六——深入分析draw()是如何工作的
Android 图形架构 之七——Choreographer 源码分析
Android图形架构 之八——硬件VSync、VSync-app、Vsync-sf

UI显示过程的三个进程

Android显示的整个过程由App进程、System_server进程、SurfaceFlinger进程一起配合完成。

  1. App进程: App需要将自己的内容显示在屏幕上,所以需要负责发起Surface创建的请求。同时触发对控件的测量、布局、绘制以及输入事件的派发处理,这些主要在ViewRootImpl中触发;

  2. System_server进程: 主要是WindowManagerService,负责接收App请求,同时和SurfaceFlinger建立连接,向SurfaceFlinger发起具体请求创建Surface,并且创建Surace的辅助管理类SurfaceControl(和window一一对应)(AMS作用是统一调度所有App的Activity);

  3. SurfaceFlinger: 为App创建具体的Surface,在SurfaceFLinger对应成Layer,然后负责管理、合成所有图层,最终显示。

在这里插入图片描述

前几篇文章分析了第二点,第三点,这篇文章主要分析第一点

Activity、Window、PhoneWindow、DecorView、View的对应关系

在这里插入图片描述

  1. 一个Activity对应创建一个Surface,每个Surface 对应SurfaceFlinger中的一个Layer

  2. Window:每一个Activity都包含一个Window对象(抽象类,提供了绘制窗口的一组通用API),通常由PhoneWindow实现。是Activity和整个View系统交互的接口。

  3. PhoneWindow:继承于Window,是Window类的具体实现。该类内部包含了一个DecorView对象,该DecorView对象是所有应用窗口(Activity界面)的根View。
    简而言之,PhoneWindow类是把一个FrameLayout类,即DecorView对象进行一定的包装,将他作为应用窗口的根View,并提供一组通用的窗口操作接口。

  4. DecorView:PhoneWindow构造函数中定义,继承FrameLayout类,是所有应用窗口的根View。

  5. WindowManager 继承ViewManager ,它们都是操作UI的接口

  6. WindowManagerImpl 实现了WindowManager,并持有WindowManagerGlobal 的对象

  7. WindowManagerGlobal 是单例模式,每个进程只有一个。持有ViewRootImpl 的对象

  8. ViewRootImpl 在整个Android的GUI系统中占据非常重要的位置,如果把Activity和View 看作 ‘MVC’ 中的V, 把各种后台服务看作Modal,ViewRootImpl 则是’MVC’ 中的’C’ - Controller. Controller在MVC架构里承担着承上启下的作用,一般来说它的逻辑最为复杂。从下图可以看到,ViewRootImpl 与 用户输入系统(接收用户按键,触摸屏输入), 窗口系统(复杂窗口的布局,刷新,动画),显示合成系统(包括定时器Choreograph, SurfaceFlinger), 乃至Audio系统(音效输出)等均有密切的关联。

流程分析

我们从 activity的源码 handleResumeActivity 开始分析,熟悉activity启动流程的应该知道,handleResumeActivity 中会执行onResume,添加显示界面。下面这张时序图,可以说概括了,本系列文章的主干流程,可以细细品味 (图片来源
在这里插入图片描述

开始源码分析

frameworks/base/core/java/android/app/ActivityThread.java

代码一:
    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);

       ....
        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;

           ....
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                //获取WindowManagerImpl,因为它implements WindowManager,后者implements ViewManager ,此处是重点,接下来会分析
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                }
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        //重要,添加根布局 
                        wm.addView(decor, l);
                    } else {
                        ....
                    }
                }

                ....
                if (r.activity.mVisibleFromClient) {
                    //显示根布局
                    r.activity.makeVisible();
                }
            }

             ....
        }
    }

接着分析上面ViewManager wm = a.getWindowManager();
代码路径:frameworks/base/core/java/android/app/Activity.java

代码二:
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
        //activity 关联的Window 是PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
         。。。省略代码,一些设置回调和赋值的语句,。。。
		//该函数为mWindowManager 变量赋值,获取到
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
    }

(WindowManager)context.getSystemService(Context.WINDOW_SERVICE) 获取WINDOW_SERVICE服务,它是在

代码路径:frameworks/base/core/java/android/view/Window.java

代码三:
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        //创建本地的 WindowManagerImpl,它关联了当前的Window(PhoneWindow),之后所有的操作 都是交给WindowManagerImpl ,这些操作定义在接口ViewManager
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

到此,我们回到代码一,看看如何添加根布局的 wm.addView(decor, l);

代码路径:frameworks/base/core/java/android/view/WindowManagerImpl.java

代码四:
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //这里的mGlobal 全局变量,一个进程中只有一个实例
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

代码路径:frameworks/base/core/java/android/view/WindowManagerGlobal.java

代码五:
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
             ....
             //因为本类是单例,所以这里创建的ViewRootImpl ,一个进程中也只有一个
             //代码六 分析
             root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
			
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                // 通过ViewRootImpl 把View 添加到Surface
                // 代码九 分析
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

接着 看看ViewRootImpl 的创建,因为这里涉及到Session,它负责创建Surface、SurfaceSession(java端的SurfaceComposerClient)

代码路径:frameworks/base/core/java/android/view/ViewRootImpl.java

代码六:
    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        // 还是由WindowManagerGlobal ,来创建Session
        //ViewRootImpl 持有Session 对象后,后续就可以控制UI的绘制了(measure,layout,draw等),涉及到Surface 等操作,就交给mWindowSession 来操作了
        mWindowSession = WindowManagerGlobal.getWindowSession();
        ....
    }
代码七:
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    //获取WindowManagerService ,IWindowManager 是aidl 
                    IWindowManager windowManager = getWindowManagerService();
                    //创建Session
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                // 获取WMS服务,并转换为IWindowManager.Stub 类型
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                ....
            }
            return sWindowManagerService;
        }
    }

这里的openSession 是跨进程通信。Session、WMS 是在另外的一个进程

代码八:
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
        ....
        //创建Session,所以每一个View 对应一个
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }

上面创建了Session,这里我们回到代码五 root.setView(view, wparams, panelParentView);

这里的root ,是ViewRootImpl对象,调用了setView函数

代码九:
   public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
               ....
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                //这里最终就是调用了三大UI 函数 onMesure, onLayout,onDraw
                requestLayout();
                try {
                  ....
                   //调用Session 为添加UI 做准备,
                   //这里主要是创建WindowState(每个View 都对应一个)、创建SurfaceSession,下面会分析
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                }
                 ....
        }
}

这里先分析mWindowSession.addToDisplay ,再分析requestLayout() ,因为它主要执行的就是把View输出到屏幕,在下一次屏幕刷新时,也会有相应的逻辑,放在后面分析,流程看上去更符合逻辑。

这里为什么requestLayout() 的执行要放在mWindowSession.addToDisplay 之前,我没太明白,知道的大佬,评论里指点一下

代码路径:frameworks/base/services/core/java/com/android/server/wm/Session.java

代码十:
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
             //调用WMS 的addWindow
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

代码十一:
 public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        
        synchronized(mWindowMap) {
           。。。。。
            //创建WindowState 对象,每一个View 对应一个
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
             。。。。。       
            //下面看看这个函数
            win.attach();
           。。。。
        }

        。。。。

        return res;
    }

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java

代码十二:
    void attach() {
        if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
        //调用到Session ,创建SurfaceSession
        mSession.windowAddedLocked(mAttrs.packageName);
    }

代码路径:frameworks/base/services/core/java/com/android/server/wm/Session.java

代码十三:
    void windowAddedLocked(String packageName) {
        mPackageName = packageName;
        mRelayoutTag = "relayoutWindow: " + mPackageName;
        if (mSurfaceSession == null) {
			//创建mSurfaceSession
            mSurfaceSession = new SurfaceSession();
            //把当前的Session对象,与WMS 关联起来
            mService.mSessions.add(this);
            ....
        }
        mNumWindow++;
    }

看到这个SurfaceSession 的创建,我们应该很熟悉了,里面的流程 在Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl 文章中分析过了

到此,我们已经有了可以控制Surface 的SurfaceSession (也就是SurfaceComposerClient),也有了View,下面就该创建Surface,并开始绘制了。

我们现在回到代码九,看看requestLayout(),它

代码十四:
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
           //检查是否在主线程,
            checkThread();
            mLayoutRequested = true;
            //执行UI遍历,它就是向Choreographer 发送CALLBACK_TRAVERSAL事件消息
            scheduleTraversals();
        }
    }
   
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            //发送同步消息屏障,会优先处理异步消息
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
             //该函数发送了事件后,就等待mChoreographer ,回调mTraversalRunnable,最终执行doTraversal
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

关于同步消息屏障的,可阅读深入源码分析Handler 消息机制 、Looper、MessageQueue 消息同步屏障、IdleHandler、Message 复用

Android 图形架构 之六——Choreographer 源码分析文章中,我们可以知道,屏幕收到刷新的信号,会调用到Choreographer,由该类去处理分发事件,屏幕绘制会调用到 ViewRootImpl#doTraversal()

下面我们就来分析ViewRootImpl#doTraversal函数:

代码十五:
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //取消同步消息屏障
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); 
            .....
            performTraversals();
            .....
        }
    }
代码十六:
   private void performTraversals() {
       
       relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
       //下面这三大函数,大家应该很熟悉了,就不分析了
       .....
       performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
       .....
       performLayout(lp, mWidth, mHeight);
       .....
       performDraw();
    }
代码十七:
    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {

        .....
        //调用Session 的relayout,
        //最后一个参数是mSurface,该成员变量的初始值 final Surface mSurface = new Surface(); 
        //但是此时的mSurface 还没有与底层的surface 关联起来,后面会通过copyFrom 进行关联
        int relayoutResult = mWindowSession.relayout(
                mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
                mPendingMergedConfiguration, mSurface);

         .....
        return relayoutResult;
    }

继续 进入Session#relayout 进行

代码十八:
    public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags,
            int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
            MergedConfiguration mergedConfiguration, Surface outSurface) {
        .....
        继续进入WMS 看看relayoutWindow
        int res = mService.relayoutWindow(this, window, seq, attrs,
                requestedWidth, requestedHeight, viewFlags, flags,
                outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);
        .....
        return res;
    }

下面进入到WMS 中relayoutWindow 函数

代码十九:
    public int relayoutWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int requestedWidth,
            int requestedHeight, int viewVisibility, int flags,
            Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
            MergedConfiguration mergedConfiguration, Surface outSurface) {
         ...
         //这里创建SurfaceControl,它会触发调用Native层的
         result = createSurfaceControl(outSurface, result, win, winAnimator);
         ...
   }

这里在java 层创建SurfaceControl,继而调用copyFrom ,把java层的surface与Native层的surface关联起来,继续跟进看看

代码二十:
    private int createSurfaceControl(Surface outSurface, int result, WindowState win,
            WindowStateAnimator winAnimator) {

        WindowSurfaceController surfaceController;
        try {
            //这里继续创建SurfaceControl,一直调用到Native层
            //代码二十一 分析此函数
            surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
        } 
        
        if (surfaceController != null) {
           //有了surfaceController,就去给surface 赋值
            surfaceController.getSurface(outSurface);
        } else {
            outSurface.release();
        }

        return result;
    }

上面有两处重要的函数实现,这里先分析winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);,它进入到了 WindowStateAnimator.java类中,路径frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java

代码二十一:
    WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
        final WindowState w = mWin;
           // 通过该类来判断创建哪一种SurfaceControl
            mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
                    attrs.getTitle().toString(),
                    width, height, format, flags, this, windowType, ownerUid);
            mSurfaceFormat = format;
        ....
        return mSurfaceController;
    }

继续进入到WindowSurfaceController,看它的构造函数

代码二十二:
    public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format,
            int flags, WindowStateAnimator animator, int windowType, int ownerUid) {

        if (DEBUG_SURFACE_TRACE) {
            mSurfaceControl = new SurfaceTrace(
                    s, name, w, h, format, flags, windowType, ownerUid);
        } else {
            //一般会创建这个SurfaceControl
            mSurfaceControl = new SurfaceControl(
                    s, name, w, h, format, flags, windowType, ownerUid);
        }

        if (mService.mRoot.mSurfaceTraceEnabled) {
            mSurfaceControl = new RemoteSurfaceTrace(
                    mService.mRoot.mSurfaceTraceFd.getFileDescriptor(), mSurfaceControl, win);
        }
    }

最后来看看SurfaceControl,

代码二十三:
    public SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
            SurfaceControl parent, int windowType, int ownerUid)
                    throws OutOfResourcesException {
        mName = name;
        //调用Native 函数,调用SurfaceComposerClient的createSurface,
        // 会调用到createSurfaceChecked,在Native层,创建Layer和Surface,
        mNativeObject = nativeCreate(session, name, w, h, format, flags,
            parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
        if (mNativeObject == 0) {
            throw new OutOfResourcesException(
                    "Couldn't allocate SurfaceControl native object");
        }

        mCloseGuard.open("release");
    }

这里的逻辑就与 文章 Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl 代码十四 接上了,就不再展开

至此,SurfaceControl 创建完成,Native层的layer和surfaceControl 也创建完了,接着回到代码二十,看看函数surfaceController.getSurface(outSurface);

代码二十四:
    void getSurface(Surface outSurface) {
        outSurface.copyFrom(mSurfaceControl);
    }

    public void copyFrom(SurfaceControl other) {
         //调用到Native层,创建Surface(在Native 中,也是通过surfaceControl 来创建的,所以这里把参数surfaceControlPtr 传入进去)
        long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);

        synchronized (mLock) {
            if (mNativeObject != 0) {
                nativeRelease(mNativeObject);
            }
            //获取到Native层的surface ,保存到java层surface 的mNativeObject 变量
            setNativeObjectLocked(newNativeObject);
        }
    }

这里的逻辑就与 文章 Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl 代码十五 接上了,就不再展开

至此,surface 相关的就已经创建初始化完毕,我们回到代码十六,最终调用了performDraw();,通过draw函数来绘制界面,具体原理,我们放在下一篇文章分析

结语

在framework层,关于view 的主要流程,解决分析完了,下一篇文章,我们深入分析一下draw 是如何把数据写入surface的,它是如何与生产消费数据交互的

参考:

Android 图形显示框架
Android SurfaceFlinger 学习之路(八)----Surface管理图形缓冲区

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页