https://www.cnblogs.com/huan89/p/14111360.html
从来都没什么Window,有的只是一个个View树,每个窗口(activity/dialog/popupWindow)都是一个view树(代码中都是叫addView,直到WMS中才叫addWindow),在需要显示时被添加进WMS中,最后通过surfalceFlinger合成后渲染到屏幕中。
每个窗口都对应一个Token,一个应用只对应一个session。
从Window视角看ActivityStart
Activity
简述:首先回顾一下,activity是怎么显示出来的:
- 为activity创建PhoneWindow和WindowManager(WindowManagerImpl)对象
在handleLaunchActivity()被回调的时候,调用WindowManagerGlobal.initialize();
初始化WindoWindowManagerGlobal,之后 Application app = r.packageInfo.makeApplication(false, mInstrumentation);
创建Application(如果还没有创建过Application),然后调用activity.attach()
,这里面 mWindow = new PhoneWindow(this, window, activityConfigCallback);
初始化PhoneWindow并给它设置WindowManager,
1 2 3 4
| mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
|
Window.setWindowManager()中通过WMS创建了WindowManagerImpl(其实就是个一百多行代码的壳)。
从这里可以看到是利用系统服务的windowManager来创建新的windowManagerImpl,因而这个应用所有的WindowManagerImpl都是同个内核windowManager,而创建出来的仅仅是包了个壳。
- setContentView实际上掉的是getWindow()(也就是上面的PhoneWindow)的setContentView(),其中调用PhoneWindow.installDecor(),
- 首先看decorView创建了没有,没有的话创建DecorView
- 把布局加载到DecorView中(LayoutInflater加载预设模板布局,见下)
DecorView是在PhoneWindow中预设好的一个布局,这个布局长这样:
他是一个垂直排列的布局,上面是ActionBar,下面是ContentView,他是一个FrameLayout。我们的Activity布局就加载到ContentView里进行显示。所以Decorview是Activity布局最顶层的viewGroup。
// DecorView创建完成了,但还缺少了最重要的一步:把DecorView作为window添加到屏幕上。
在handleResumeActivity中,执行了最后的 wm.addView(mDecor, getWindow().getAttributes());
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); ... if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
|
直接调用WindowManagerImpl的addView方法来吧decorView添加到屏幕上,至此,我们的Activity界面就会显示在屏幕上了。
进一步需要看WindowManagerImpl.addView之后是怎么将View(即Window)添加入屏幕的
wm.addView(mDecor, getWindow().getAttributes());
其中wm是WindowManagerImpl实例,WindowManagerImpl.addView
其实是通过桥接,调用WindowManagerGlobal的全局单例的方法WindowManagerGlobal.addView
,该方法中会新建一个ViewRootImpl,然后将入参的decorView、新建的ViewRootImp等加入自身维护的mViews、mRoots列表中,同时将DecorView注入ViewRootImplroot.setView(view, wparams, panelParentView)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| WindowMangerGlobal.clss 维护着WMS实例sWindowManagerService和以下列表
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { synchronized (mLock) { ... root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams);
mViews.add(view); mRoots.add(root); mParams.add(wparams);
try { root.setView(view, wparams, panelParentView); } ... } }
|
5. ViewRootImpl.setView()将调用到
1 2 3 4
| res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
|
这个mWindowSession来自于构造器的入参,是由WindowManagerGlobal.getWindowSession中来的,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { ... sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { ... }); } ... } return sWindowSession; } }
|
可以看出,这个session是一个单例,也就是整个应用的所有viewRootImpl的windowSession都是同一个,也就是一个应用只有一个windowSession。
Session.addToDisplay
实际上走的是WMS的addWindow方法,后面的逻辑就交给WMS去处理了,WMS就会创建window,然后结合参数计算window的高度等等,最后使用viewRootImpl进行绘制。
window的添加过程是通过PhoneWindow对应的WindowManagerImpl来添加window,内部会调用WindowManagerGlobal来实现。WindowManagerGlobal会使用viewRootImpl来进行跨进程通信让WMS执行创建window的业务。
每个应用都有一个windowSession,用于负责和WMS的通信,如ApplicationThread与AMS的通信。
Other Window
dialog和popupwindow的层级是10001999,在这个层级都属于子window而不是应用Window(199),子Window需要附属于父Window(Activity,也就是应用Window)才能显示,dialog虽然创建了一个PhoneWindow,但是popupWindow最终也创建了一个Window,只是它不是PhoneWindow而已,popupWindow和dialog的显示都需要依赖父Window的Token,其实两者都需要依赖于Activiy
那么,PopupWindow则是在构造器中时将入参的contentView直接执行 setContentView(contentView);
,然后在showAtLocation()
中调用了preparePopup()
创建它的decorView(PopupDecorView)之后invokePopup()
调用执行mWindowManager.addView(decorView, p);
- 根据参数构建popupDecorView
- 把popupDecorView添加到屏幕上
dismiss()
中调用mWindowManager.removeViewImmediate(decorView);
Dialog
dialog的创建过程Activity比较像:构造器创建PhoneWindow,setContentView初始化DecorView,show时候添加DecorView。
- 构造函数中创建PhoneWindow,设置WindowManger(这里拿的是传入的context,实际上这context只能是actiivty,的WindowManager)
- Dialog.setContentView时掉PhoneWindow.setContentView来初始化DecorView
show()
时候调用了mWindowManager.addView(mDecor, l);
dismiss()
时候调用mWindowManager.removeViewImmediate(mDecor);
总结
- dialog和popupWindow不同,dialog创建了新的PhoneWindow,使用了PhoneWindow的DecorView模板。而popupWindow没有,popupWindow他也对应一个window,因为它也是通过windowManager添加上去的,不属于Activity的view树。
- dialog的显示层级数更高,会直接显示在Activity上面,在dialog后添加的popUpWindow也会显示在dialog下
- dialog的创建流程和activity非常像