android addView是如何渲染到屏幕上的(2)——WindowManager和ViewRootImpl

         上一节 描述了一个xml 通过LayoutInflactor 变成了一颗view树,并且存储在ViewGroup的mChildren数组里,被添加到PhoneWindow的mDecorView成员里的过程。最后是跟到handleResumeActivity,mDecorView 在handleResumeActivity里被设置给WindowManager,那么WindowManager拿着mDecorView,传递给了谁,怎么显示到界面上的呢。

        复习一下ActivityThread.handleResumeActivity() ,就开始分析WindowManager。 

public final class ActivityThread extends ClientTransactionHandler {
    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
                                    boolean reallyResume) {
            ……
            final Activity a = r.activity;
                // 1.获取WindowManager
                ViewManager wm = a.getWindowManager();
                a.mDecor = decor;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    // 2.将DecorView添加到窗口中
                    wm.addView(decor, l);
                }
            }
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                // 代码省略
                if (r.activity.mVisibleFromClient) {
                    // 3.使得Activity变得可见,其实是设置DecorView变为可见
                    r.activity.makeVisible();
                }
            }
                // 4.最后通知ActivityManagerNative,该Activity己经变为resume状态
                ActivityManagerNative.getDefault().activityResumed(token);
        }
    }
}

         我们看到标注1 的地方,PhoneWindow的DecorView时通过WindowManager.addView()添加给 WindowManager 的,WindowManager 在 Java 层的具体实现是WindowManagerImpl,

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    // Window对象
    private final Window mParentWindow;
    private WindowManagerImpl(Display display, Window parentWindow) {
        mDisplay = display;
        mParentWindow = parentWindow;
    }
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

}

        WindowManagerImpl的addview实现其实调用的是WindowManagerGlobal这个类。添加View、移除View、更新View的布局等具体的工作都交给了WindowManagerGlobal这个类,WindowManagerGlobal的addView方法:

public final class WindowManagerGlobal {
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    // 将View添加到WindowManager中,也就是在手机窗口中显示该View
    public void addView(View view, ViewGroup.LayoutParams params,
                        Display display, Window parentWindow) {
        // 检查参数有效性等代码省略
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            // ……
            // 1.构建ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }
        try {
            // 2.调用ViewRootImpl的setView方法将View显示到手机窗口中
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
        }
    }
}

        看标注1的地方!终于看到非常重要的ViewRootImpl 了!ViewRootImpl在View的绘制发挥重要的作用,所有View的绘制以及事件分发等交互都是通过它来执行或传递的。ViewRootImpl管理Window中所有的View,每个Activity中ViewRootImpl的数量取决于这里的mWindowManager.addView的调用次数。

       从WindowManagerGlobal名称可以看出,它是一个全局的WindowManager,其内部维护了一个View的List ,存放了当前的View(一路从PhoneWindow传过来的mDecorView )。在 WindowManagerGlobal  的addView()里, 主要就是去构建ViewRootImpl ,把view添加进List,然后调用ViewRootImpl的setView方法,这篇笔记剩下的内容都是围绕这个setView()的过程展开的。

public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    public final Context mContext;
    public final Surface mSurface = new Surface();
    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        // 获取Window Session, 也就是与WindowManagerService建立连接
        mWindowSession = WindowManagerGlobal.getWindowSession();
        final IWindowSession mWindowSession;
    }
    public void setView(View view, WindowManager.LayoutParams attrs,
                        View panelParentView) {
        synchronized (this) {
            // 1.请求布局
            requestLayout();
            try {
                // 2.向WMS发起请求
               res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                              getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                              mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                              mAttachInfo.mDisplayCutout, inputChannel,
                              mTempInsets, mTempControls);
                      setFrame(mTmpFrame);
            }
        }
}

          ViewRootImpl的setView() 里出现了一个新的类:WindowSession 。这显然是一次通信,我们先中断一下对setView的跟踪,研究一下谁和谁通信,怎么通信。

        我们看到,直到ViewRootImpl的setView方法,程序还跑在APP端的UI线程,而WindowManagerService是运行在另一个的进程的,和APP享有不同的内存空间,APP和WMS的跨进程通信是怎么进行的呢?就是靠WindowSession了,WindowSession是在创建ViewRoot时,通过WindowManagerGlobal.getWindowSession()方法获取的 :
        mWindowSession = WindowManagerGlobal.getWindowSession();

class WindowManagerGlobal{
    private static IWindowSession sWindowSession;
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    // 1.获取WindowManagerService
                    IWindowManager windowManager = getWindowManagerService();
                    // 2.与WindowManagerSerice建立—个Session
                    //    Framework层与Native层建立通信,
                    //    双方有什么需求都通过这个Session来交换信息
                    sWindowSession = windowManager.openSession(
                    imm.getClient(), imm.getInputContext());
                } 
            }
            return sWindowSession;
        }
    }
    // 3.获取WindowManagerService
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
            // 4.ServiceManager.getService返回的是 IBinder 对象
            //也就是说 Android Framework与WMS之间是通过Binder机制进行通信
            sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
        }
        return sWindowManagerService;
       }
    }
}

        这段代码出现了两个新的对象:IWindowManager和 IWindowSession

  •         IWindowManager:WMS 继承了IWindowManager.Stub,是一个binder,对外提供了IWindowManager,供跨进程调用;
  •         Session 继承自 IWindowSession.Stub,同样是一个binder,对外提供IWindowSession,供跨进程调用;

         建立连接的过程大概是这样的:(看WindowManagerGlobal的静态方法getWindowManagerService())WindowManger向WMS发送一个建立连接的Binder进程间通信请求。WMS服务接收到这个请求之后,就会在内部为这个应用保留一个单独的Session,这是一个类型为Session的Binder本地对象,WMS将这个Binder返回给应用程序进程,当App需要通信时,调用Binder的openSession()就会得到一个Session代理对象。

        具体来说,在创建ViewRootImpl时,ViewRootImpl通过请求WMS获取到了这个Session代理对象,保存在自己的成员变量mWindowSession中。拥有了mWindowSession的ViewRootImpl 因此可以成为  Framework 层与 Native 层(WMS是运行在Native层的)的通信桥梁。ViewRootImpl和WMS的通信是双向的,不仅仅是ViewRootImpl.setView()过程发生APP和WMS的通信,WMS向ViewRootImpl传递touch事件也是通过mWindowSession完成间接的通信。

         这里可以对比一下AMS与应用进程的IPC

        ActivityManager类内部调用ActivityManagerNative的getDefault函数得到一个ActivityManagerProxy对象,AMS继承ActivityManagerNative,通过它可与AMS通信。所以说,ActivityManagerProxy 是AMS在客户端进程的一个代理,通过AMP里面的方法请求AMS。那么客户端进程在AMS的代理呢?这个代理就是ApplicationThreadProxy,如果AMS要通知Activity,那么就是使用ApplicationThreadProxy。描述参考

        好了,知道ViewRootImpl 时怎么完成和WMS的跨进程通信之后,说回本文的ViewRootImpl.setView(),上文已经跟到这段代码:

public class ViewRootImpl   
   
    public void setView(View view, WindowManager.LayoutParams attrs,
                        View panelParentView) {
        synchronized (this) {
            // 1.请求布局
            requestLayout();
            try {
                // 2.向WMS发起请求
               res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                              getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                              mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                              mAttachInfo.mDisplayCutout, inputChannel,
                              mTempInsets, mTempControls);
                      setFrame(mTmpFrame);
            }
        }
    }
}

       ViewRootImpl.setView()把View和ViewRootImpl关联了起来,过程很复杂,我们主要关注这两件事

  •  requestLayout() 是用来整个视图树的绘制操作,执行我们熟悉的 measure > layout > draw 的过程。performDraw() 内容绘制等另开一篇讲。
  • mWindowSession.addToDisplayAsUser(),这就是之前分析的通过这个sWindowSession间接与WMS通信的过程

        sWindowSession.addToDisplay() 调用到远端Session对象的addToDisplay方法。远端Session对象收到这个请求后,转接给WMS。如此一来显示Dialog或者Activity中的mDecorView请求就交给了WMS去处理了。

       WMS 到底由哪些能力 最后,可以从IWindowSession接口的看出——

/**
 * System private per-application interface to the window manager.
 */
interface IWindowSession {
    int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outFrame, out Rect outContentInsets,
                     out Rect outStableInsets, out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel, out InsetsState insetsState, out InsetsSourceControl[] activeControls);
    int addToDisplayAsUser(……);
    void remove(IWindow window);
    //更改窗口的参数。根据新参数返回屏幕上窗口的新框架(不包含位置信息)和窗口的Surface。
    //如果窗口不可见,则Surface将无效,否则可以使用它绘制窗口的内容。
    int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
                 int requestedWidth, int requestedHeight, int viewVisibility,
                 int flags, long frameNumber, out Rect outFrame,
                 out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
                 out Rect outBackdropFrame,
                 out DisplayCutout.ParcelableWrapper displayCutout,
                 out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
                 out InsetsState insetsState, out InsetsSourceControl[] activeControls,
                 out Point outSurfaceSize, out SurfaceControl outBlastSurfaceControl);

    boolean outOfMemory(IWindow window);
    ……
}

        分析这个接口的方法,这些接口都没有和View有关的信息,而是以IWindow为单位操作,可以大致感受到,WMS的能力在于窗口的添加、移除、调整顺序等,图像的绘制与合成之类的不在WMS的能力范围内

        另外,有一种说法说“WMS其实管理的并不是Window,而是View”,也是对的,虽然类的注释里写的是 "to the window manager",窗口管理,但是WMS管理的其实是当前状态下哪个View应该在最上层显示,是这些View的z-order。也就是说,WMS管理的其实也不是WIndow,是属于某个Window下的View。

       好了,到这里,我们知道了我们可以通过ViewRootImpl的 WindowSession,把一个view给到WMS。WMS 会收到很多的view并决定哪一个在最上层显示,真正完成图像绘制的是APP端,而完成图层合成的是SurfaceFlinger服务。谁在真正绘制这些view,SurfaceFlinger怎么工作,我们应该给WMS 一个怎么样的view, 需要从我们前面那跳过的 requestLayout() 入手去跟,会再开一篇讲。

总结

  • ActivityThread的 handleResumeActivity处,回去获取activity内部的WindowManager,是个全局单例。并且把PhoneWindow的decorView传递给 WindowManagerGlobal.
  • WindowManagerGlobal 内部维护了一个ViewRootImpl list,每次addView()时会创建时会创建一个ViewRootImpl。ViewRootImpl负责DecorView的测量布局绘制的调用,也负责从WMS获取TouchEvent事件后分发。
  • ViewRootImpl 拥有mWindowSession,实现了ViewRootImpl和WMS的跨进程通信,这是一种基于Binder的跨进程通信。ViewRootImpl通过sWindowSession向WMS发起显示Dialog或者Activity中的mDecorView请求,sWindowSession.addToDisplay方法调用到远端Session对象的addToDisplay方法。远端Session对象收到这个请求后,转接给WMS。如此一来添加请求就交给了WMS去处理了

---------------------------------------------------------------------------------------------------------------------------------

 AndroidUI渲染专题其他内容: 

        (一)LayoutInflactor和PhoneWindow 

        (二)WindowManager和ViewRootImpl

        (三)surface

Guess you like

Origin blog.csdn.net/qq_33298609/article/details/116767181