WindowManagerService Window View 关系(二)

上一篇文章《WindowManagerService Window View 关系(一)》 主要理清了从上层Activity的角度去看待Window机制,包括WindowManager、PhoneWindow、DecorView等之间的关联和作用。

回到之前提到的问题:

  1. Activity、WindowManager、Window之间怎么关联
  2. DecorView、PhoneWindow之间怎么关联(setContentView)
  3. Activity 怎么创建添加一个Window(从App进程的角度看)
  4. WMS 怎么创建添加一个Window (从WMS进程的角度看)
  5. Token的创建和作用

Activity 怎么创建添加一个Window(从App进程的角度看)

之前我们都知道Activity在attach() 创建了PhoneWindow和WindowManagerImpl,并且PhoneWindow加载了DecorView,但是并没有讨论WindowManagerImpl 和 WindowManagerGlobal怎么跟DecorView、ViewRootImpl、PhoneWindow关联起来的。以下就是讨论这一点。

在Activity的启动过程中,ActivityThread的主线程中调用了handleLaunchActivity()之后( 后续调用了Activity的attach()方法),会走到了handleResumeActivity(),其中有部分也是跟Window有关。

// ActivityThread
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    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);
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        ...    
        ViewRootImpl impl = decor.getViewRootImpl(); // 获取ViewRootImpl,应该为空
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                wm.addView(decor, l); // 添加decorview、layoutParam
            } 
        }
        ...
    }
}

前面说到,handleLaunchActivity()创建了phoneWindow、DecorView,handleResumeActivity()调用WindowManagerImpl.addView()。

WindowManagerImpl使用了桥接模式,实际上由WindowManagerGlobal管理窗口机制。

再看一次WindowManagerGlobal几个重要的变量:

private final ArrayList mViews = new ArrayList(); // DecorView
private final ArrayList mRoots = new ArrayList(); // ViewRootImpl
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>(); // LayoutParams

mViews 存储了所有的DecorView,是 view 的根布局;
mRoots 存储了所有的ViewRootImpl,它在View的绘制发挥重要的作用,所有View的绘制以及事件分发等交互都是通过它来执行或传递的;
mParams 存储对应的window的属性;

// WindowManagerImpl
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

// WindowManagerGlobal
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) {
        ...
        // 查找对应的DecorView在mViews中的下标
        int index = findViewLocked(view, false);
        if (index >= 0) {
            if (mDyingViews.contains(view)) { 
            // 该DecorView存在mDyingViews,已经被设置为待销毁,
                mRoots.get(index).doDie();// 调用ViewRootImpl的doDie()销毁
            } else {
                throw new IllegalStateException("View " + view
                        + " has already been added to the window manager.");
            }
        }
        if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            final int count = mViews.size();
            for (int i = 0; i < count; i++) {
                if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                    panelParentView = mViews.get(i);
                }
            }
        }
        // 生成ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        // 给DecorView设置LayoutParams
        view.setLayoutParams(wparams);
        // 添加DecorView
        mViews.add(view);
        // 添加RootViewImpl
        mRoots.add(root);
        // 添加LayoutParams
        mParams.add(wparams);

        // do this last because it fires off messages to start doing things
        try {
            // 将DecorView交给ViewRootImpl
            root.setView(view, wparams, panelParentView);
        } 
        ...
    }
}

WindowManagerGlobal生成新Window的流程:

  1. 生成对应的ViewRootImpl;
  2. 保存DecorView,RootViewImpl,LayoutParams到本地;
  3. 调用ViewRootImpl的setView,将DecorView交给ViewRootImpl;

接下来看ViewRootImpl的setView(),只抽取了部分跟窗口机制相关的代码。

// ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs, 
        View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            // 保存DecorView
            mView = view;
            ...
            int res;
            requestLayout(); // 首次执行了测量布局流程
            ....  
            // 通过IWindowSession 跟Session通信,间接跟WMS通信
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                    mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                    mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); 
            ....
            // 设置DecorView的ViewRootImpl
            view.assignParent(this);            
        }
    }

ViewRootImpl完成了最后的两个工作:

  1. 视图绘制
  2. 利用binder跟WMS跨进程通信:添加新的窗口(这一点会在下一节分析)
小结:

WindowManager生成ViewRootImpl,并将DecorView交给ViewRootImpl,最后进行视图绘制

WMS 怎么创建添加一个Window (从WMS进程的角度看)

上面所有的分析都是在App的进程或者Activity的视角去看怎么创建Window,而事实上所有的Window都是由WMS统一管理控制,包括Activity的Window、Dialog的Window、Toast的Window。

WMS(WindowManagerService) 跟 AMS (ActivityManagerService)一样是SystemServer启动的守护进程。AMS负责四大组件流程的控制,WMS负责窗口机制的控制。

WindowManagerService 重要的成员:

final WindowManagerPolicy mPolicy;
final ArraySet mSessions = new ArraySet<>();
final WindowHashMap mWindowMap = new WindowHashMap();
final ArrayList mFinishedStarting
final ArrayList mWindowReplacementTimeouts

  • WindowManagerPolicy
    窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了WindowManager所有的特定的 UI 行为。它的具体实现类为 PhoneWindowManager,这个实现类在 WMS 创建时被创建 。

  • Session
    主要用于进程间通信,其他的应用程序进程想要和 WMS 进程进行通信就需要经过 Session, 每个应用程序进程都会对应一个 Session, WMS 保存这些 Session 用来记录所有向 WMS 提出窗口管理服务的客户端。

  • WindowState
    WindowState用于保存窗口的信息,在 WMS 中它用来描述一个窗口。mWindowMap 用来保存 WMS 中各种窗口的集合。

  • AppWindowToken
    AppWindowToken 是继承自WindowToken,应用程序进程中的每一个Activity组件在Activity管理服务ActivityManagerService中都对应有一个ActivityRecord对象,ActivityManagerService中每一个ActivityRecord对象在Window管理服务WindowManagerService中都对应有一个AppWindowToken对象。
    WindowToken可以作为创建window的凭证

创建Session

我们知道,WMS处于一个独立的进程,APP处于另外一个进程,两者交流是通过Android的binder机制;

WindowManagerService 继承了IWindowManager.Stub,是一个binder,对外提供了IWindowManager,供跨进程调用;

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

App向WMS通信需要经过Session,每个进程都有对应的Session,WMS保存所有的Session。

在WindowManagerGlobal内部就有WMS和Session binder的身影。

private static IWindowManager sWindowManagerService;
private static IWindowSession sWindowSession;

WindowManagerGlobal对外提供了IWindowManager binder和IWindowSession binder的接口:

// WindowManagerGlobal
public static IWindowManager getWindowManagerService() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowManagerService == null) {
            sWindowManagerService = IWindowManager.Stub.asInterface(
                    ServiceManager.getService("window"));
            try {
                if (sWindowManagerService != null) {
                    ValueAnimator.setDurationScale(
                            sWindowManagerService.getCurrentAnimatorScale());
                }
            }
        }
        return sWindowManagerService;
    }
}
// WindowManagerGlobal
public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                InputMethodManager imm = InputMethodManager.getInstance();
                IWindowManager windowManager = getWindowManagerService();
                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;
    }
}

getWindowSession()内部是利用WMS创建Session,跨进程调用WMS的openSession(),每个进程都有一个唯一的Session。

//AMS
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
        IInputContext inputContext) {
    if (client == null) throw new IllegalArgumentException("null client");
    if (inputContext == null) throw new IllegalArgumentException("null inputContext");
    Session session = new Session(this, callback, client, inputContext);
        return session;
}

AMS为进程窗口一个Session,最后将IWindowSession binder通过IWindowManager binder传递给App进程。

WMS添加window

在ViewRootImpl的内部也有IWindowSession,它就是来自WindowManagerGlobal的,因为他们同处一个进程。

final IWindowSession mWindowSession;

public ViewRootImpl(Context context, Display display) {
	...
    mWindowSession = WindowManagerGlobal.getWindowSession();
    ...
}

ViewRootImpl.setView()中跨进程调用了mWindowSession.addToDisplay()

// Session
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
        Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
            outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
}

// WMS
public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
	
}

Session只是充当了ViewRootImpl跟WMS通信的手段,最后走到了WMS的addWindow(),addWindow()做了很多事情,不做分析,这里注重整体流程。

至此,Window的创建和关联完成。

小结:

《WindowManagerService Window View 关系(二)》

Token的创建和作用

其实,从一开始到现在一直在规避一个东西就是Token,从Activity的启动到Window的添加都看到他们的身影:ActivityRecord的token,WMS中的AppWindowToken等。

先说结论:AMS中的token,WMS中的token都是一个token,都是ActivityRecord构造函数中创建的Token对象。

Token 它是一个binder对象,使得每个Activity跟window能够挂钩起来。AMS根据Token可以找到ActivityRecord;WMS通过Token匹配WindowState,保存在mWindowMap中。

  • ActivityRecord的token

    ActivityRecord的变量appToken的数据类型为Token,Token继承于IApplicationToken.Stub,是一个binder。

    static class Token extends IApplicationToken.Stub {
        private final WeakReference<ActivityRecord> weakActivity;
        private final String name;
    
        Token(ActivityRecord activity, Intent intent) {
            weakActivity = new WeakReference<>(activity);
            name = intent.getComponent().flattenToShortString();
        }
    }
    

    每个Activity就对应一个ActivityRecord,AMS为Activity创建和保存ActivityRecord。

    Activity的启动过程中,在ActivityStarter的startActivity方法中会创建ActivityRecord;在ActivityRecord构造函数中生成了appToken。

  • WMS中的token
    WMS 的 token在addWindow()传递过来的,保存在mWindowMap中,并且生成了AppWindowToken。添加窗口时,WMS会校验token。

    public int addWindow(Session session, IWindow client, int seq,
      	LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
      	Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
      	DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
    	...
    	mWindowMap.put(client.asBinder(), win);
    	...
     }
    
  • Activity的token

    Activity的token是在调用attach()传递过来,并且会将token设置到Window中,最后也会保存到Window.LayoutParams中。

    // Activity
    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) {
    
         ....
         mToken = token;
         ....
         // 设置PhoneWindow的WindowManager
         mWindow.setWindowManager(
             	(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
             	mToken, mComponent.flattenToString(),
         	    (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
     }
    
     // Window
     public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
             boolean hardwareAccelerated) {
         mAppToken = appToken;
     }
    

参考

任玉刚 《Android艺术开发探索》

刘望舒《Android进阶解密》

https://www.jianshu.com/p/6afb0c17df43

    原文作者:三木仔
    原文地址: https://blog.csdn.net/qq_15893929/article/details/86649828
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞