前言
首先看一个Android界面的布局层次结构,最直观的看一下:
我们能清晰看到,这个界面分成了3部分:顶部状态栏(statusbar)、底部导航栏(navigationbar)、应用界面。
题外话:
查看布局的层次结构,工具或途径可以参考下面的。
Android Studio:Tools->Layout Inspector->选择要查看的进程;
SDK Tools:tools/hierarchyviewer.bat。 不过最新推荐用tools/monitor.bat代替单独的hierarchyviewer.bat;hierarchyviewer.bat在工程目录下也存在prebuilts/devtools/tools
该篇主要介绍Activity中窗口的创建过程 以及 添加到WMS的过程。
第二部分 综述总结,先将上述两个过程的内容做了比较简洁的总结。
第三部分 Activity窗口添加过程,跟踪源码详细 讲述了 Activity窗口的创建过程 以及 添加到WMS过程。
由于第三部分 跟踪源码,这个过程比较长,涉及比较多,相对枯燥。所以把总结先放到了第二部分。这样,如果了解过源码或这个过程的,可以只看第二部分。没了解过的,也可以通过第二部分有个大概了解,再查看第三部分,若遇到不太清楚的部分,可以再回到第二部分,对比理解。
该篇也是基于Android10的源码。
若有不对或不足,欢迎指点。
综述总结
前言已经介绍了为什么将总结放在了前面。下面具体看下。
第二部分,主要介绍了下面几个内容:
- Window类型:窗口类型介绍
- 几个重要类:窗口创建添加到WMS过程中常见的一些类,了解下他们之间的关系
- Activity创建窗口添加到WMS综述:简单总结了下 Activity的创建过程 和 添加到WMS过程
- Activity中的一些结构示意图:整个过程,Activity中关联的一些类/结构的 关系,理解这个个人觉得很有必要
- Token传递到WMS:Token是很重要的参数,参与整个过程。这里将该篇涉及的过程中的 Token的传递过程单独总结了下
Window类型
//WindowManager.javapublic static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {public static final int FIRST_APPLICATION_WINDOW = 1;public static final int LAST_APPLICATION_WINDOW = 99;public static final int FIRST_SUB_WINDOW = 1000;public static final int LAST_SUB_WINDOW = 1999;public static final int FIRST_SYSTEM_WINDOW = 2000;public static final int LAST_SYSTEM_WINDOW = 2999;//状态栏public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;//搜索栏public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;//来电显示public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//警告窗口,常见如:低电量警告public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//锁屏public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;//toastpublic static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;//显示在所有窗口之上,覆盖//15b0来电优先,即使锁屏状态下public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;//输入法窗口public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;//壁纸public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;}
应用窗口(1 ~ 99): FIRST_APPLICATION_WINDOW ~ LAST_APPLICATION_WINDOW。对应一个Activity,token需设置成Activity的token。 如:Activity。
子窗口(1000 ~ 1999): FIRST_SUB_WINDOW ~ LAST_SUB_WINDOW。必须要有一个父窗口,token需设置成父窗口的token。 如:PopupWindow,依附于Activity。
系统窗口(2000 ~ 2999): FIRST_SYSTEM_WINDOW ~ LAST_SYSTEM_WINDOW。系统级的 不需要对应Activity 也不需要有父窗口,应用进程一般没有权限创建,只有系统进程可以创建。如:上面列出了部分常见的系统窗口,状态栏、来电、toast、输入法等等。
几个重要类
下面几个类是后续经常看到的,这里主要看下他们直接的继承关系,后面看到比较容易理解。
public abstract class Window {}public class PhoneWindow extends Window implements MenuBuilder.Callback {}public interface WindowManagerPolicy extends WindowManagerPolicyConstants {}public class PhoneWindowManager implements WindowManagerPolicy {}public interface ViewManager {public void addView(View view, ViewGroup.LayoutParams params);public void updateViewLayout(View view, ViewGroup.LayoutParams params);public void removeView(View view);}public interface WindowManager extends ViewManager {}public final class WindowManagerImpl implements WindowManager {}/** A window in the window manager. */class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {}
- Window是一个抽象类,Activity、Toast、Dialog等都是靠Window来呈现。
PhoneWindow是Window的具体实现类(几乎是唯一实现类)。 - WindowManager是个接口,继承接口 ViewManager(ViewManager定义了3个操作:增加、更新、移除)。
WindowManagerImpl是WindowManager的实现类。
不过查看WindowManagerImpl中 关于ViewManager的3个操作可以看出,这3个实现 最终是交由WindowManagerGlobal完成的。 - WindowState维护着窗口的所有信息。WMS通过WindowState对窗口进行管理、保存状态等。
Activity创建窗口添加到WMS综述
这是跟踪的代码过程,这里汇总下 方便后续查看理解。 红色是比较主要的几个节点方法。
//attach()-performLaunchActivity()--activity.attach()//创建了PhoneWindow(mWindow)。mWindowManager保存的是 从mWindow处获取的 setWindowManager()创建的WindowManagerImpl---mWindow.setWindowManager()//PhoneWindow内部 创建了WindowManagerImpl(mWindowManager),并保存了appToken、appName。//onCreate()-setContentView()--installDecor()---generateDecor()//创建了DecorView(mDecor)---generateLayout()//将activity的布局作为子视图(ViewGroup)添加到mDecor中//onResume()-r.activity.makeVisible()//--wm.addView(mDecor, ...)//wm即mWindowManager(WindowManagerImpl对象)---WindowManagerGlobal.addView()//创建了ViewRootImpl。addView的view是mDecor,ViewRootImpl中创建了mWindow(这里是一个IBinder,而非attach()中创建的)----ViewRootImpl.setView()//openSession()创建了Session(IWindowSession的代理类),view也是mDecor。mDecor传入到ViewRootImpl的mView-----Session.addToDisplay()//通过Session进入system_server进程------mService.addWindow()//进入WMS,执行addWindow()添加窗口
attach阶段:
一个Activity 创建了一个PhoneWindow对象 ,PhoneWindow通过setWindowManager() 创建了WindowManagerImpl 。
即Activity 对应一个PhoneWindow,并得到了一个WindowManager(WindowManagerImpl,Window创建的)。
onCreate阶段:
创建了DecorView ,并将 activity的布局添加到DecorView中 。
onResume阶段:
创建了ViewRootImpl,通过setView()最终由Session进入system_server进程。最终执行addWindow添加窗口到WMS。
Activity中的一些结构示意图
下面是我学习总结中 根据理解画的,方便自己查看时一眼可得。
(若有什么不对,多谢指点)
public final class WindowManagerImpl implements WindowManager {private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();}public final class WindowManagerGlobal {private static IWindowManager sWindowManagerService;//WMS客户端,private static IWindowSession sWindowSession;//Sessionprivate final ArrayList<View> mViews = new ArrayList<View>();private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(ad0);private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();}
- 一个Activity对应了一个PhoneWindow对象。即 每个Activity对应一个Window (具体实现类是PhoneWindow)。
- 一个PhoneWindow 持有一个 DecorView 实例, DecorView实际是一个FrameLayout,它是Activity中所有View的根(最顶层的View)。
- 一个PhoneWindow有一个WindowManagerImpl。WindowManagerImpl持有一个单例WindowManagerGlobal。
Token传递到WMS
Activity启动时 AMS会为其创建一个ActivityRecord。可以参考:AMS之应用的第一次启动过程。
下面先看下ActivityRecord中关于token的几处代码:
final class ActivityRecord extends ConfigurationContainer {final IApplicationToken.Stub appToken; // window manager token// TODO: Remove after unificationAppWindowToken mAppWindowToken;ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,...) {appToken = new Token(this, _intent);}void createAppWindowToken() {mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,...);}static class Token extends IApplicationToken.Stub {Token(ActivityRecord activity, Intent intent) {weakActivity = new WeakReference<>(activity);name = intent.getComponent().flattenToShortString();}}}
ActivityRecord中的成员变量 appToken ,这个很重要,后续很多地方会一直涉及到。
ActivityRecord中有个 appToken ,其是一个IBinder(内部类Token继承了IApplicationToken接口)。Token内部持有Activity的弱引用。
在ActivityRecord中会通过createAppWindow()创建并保存 AppWindowToken对象 到mAppWindowToken。
mAppWindowToken:这个appToken会被封装在其中。路径:ActivityStack.startActivityLocked()->ActivityRecord.createAppWindowToken()。AppWindowToken是WindowToken子类。WindowToken可以标志一个窗口。
这个appToken,会在Activity.attach()中作为参数传递到Activity。
Activity保存到mToken。
然后通过 Activity.attach()->mWindow.setWindowManager() 传入到Window(PhoneWindow)中。
Window保存到mAppToken。
WindowManagerGlobal.addView()->Window.adjustLayoutParamsForSubWindow()保存到WindowManager.LayoutParams中的token变量中。
最后WindowManage3638r.LayoutParams(其中token即ActivityRecord中的appToken)作为参数传入ViewRootImpl.setView()。
ViewRootImpl中mWindowAttributes拷贝了WindowManager.LayoutParams,作为参数通过Session.addToDisplay()传入WMS中,进行后续操作。
这是整个添加窗口(到addWindow())过程 appToken参与的过程及传递过程。
appToken如何参与窗口的添加,这个在 “第三部分的2.8:mService.addWindow()” 注释中能大致看到,比较详细。
Activity窗口添加过程
这里主要介绍 Activity对应Window的创建 以及 Window添加到WMS的过程。
Activity窗口创建
在 AMS之应用的第一次启动过程 中,从点击应用图标到activity创建并执行onCreate()。 下面部分是后面部分的截取,不清楚可以参考下那篇文章。
1.1:handleLaunchActivity()
这里从handleLaunchActivity()开始。
//ActivityThread.java@Overridepublic Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {...WindowManagerGlobal.initialize();final Activity a = performLaunchActivity(r, customIntent);...}private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...try {java.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);if (activity != null) {Window window = null;...//attach(),注意这个r.token。参考1.2activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.configCallback,r.assistToken);if (r.isPersistable()) {//callActivityOnCreate() 最终执行到的activity的onCreate()方法。//参考1.4mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {mInstrumentation.callActivityOnCreate(activity, r.state);}}}...return activity;}
WindowManagerGlobal.initialize();
是获取WMS的IBinder代理类,用于与WMS通信。这里不列出代码了。
接着要看的是
activity.attach()
。注意作为参数传入attach()的 r.token 是个IBinder,来自ActivityClientRecord,简单看标识了一个Activity。
1.2:activity.attach()
//Activity.javafinal void attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, ...) {...//创建PhoneWindowmWindow = new PhoneWindow(this, window, activityConfigCallback);//创建PhoneWindow//设置软键盘if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {mWindow.setSoftInputMode(info.softInputMode);}//token保存到mToken。mToken = token;...//mToken传入到Window,参考1.3mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//mWindowManager即setWindowManager()中创建的WindowManagerImpl。mWindowManager = mWindow.getWindowManager();...}
首先创建了Activityd对应的Window,是PhoneWindow-Window的实现类。 接着看
mWindow.setWindowManager()
。
1.3:mWindow.setWindowManager()
//Window.javapublic void setWindowManager(WindowManager wm, IBinder appToken, String appName,boolean hardwareAccelerated) {//ActivityClientRecord.tokenmAppToken = appToken;mAppName = appName;mHardwareAccelerated = hardwareAccelerated;if (wm == null) {wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);}//创建了WindowManagerImpl,注意WindowManagerImpl中mParentWindow是this,非空mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);}
这里创建了WindowManagerImpl对象,即WindowManager的实现类。并保存了appToken、appName、mWindowManager。
通过setWindowManager(),即为Window(或者PhoneWindow)设置创建了WindowManager(WindowManagerImpl)。
1.4:setContentView()
mInstrumentation.callActivityOnCreate()
最终有调用到Activity的onCreate()。
自定义Activity,设置布局都执行了
setContentView()
,下面直接来看下这个方法。
//Activity.javapublic void setContentView(@LayoutRes int layoutResID) {getWindow().setContentView(layoutResID);//initWindowDecorActionBar();}public Window getWindow() {return mWindow;//即PhoneWindow对象}//PhoneWindow.java// This is the top-level view of the window, containing the window decor.private DecorView mDecor;@Overridepublic void setContentView(int layoutResID) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window// decor, when theme attributes and the like are crystalized. Do not check the feature// before this happens.if (mContentParent == null) {installDecor();//} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());transitionTo(newScene);} else {mLayoutInflater.inflate(layoutResID, mContentParent);}...}private void installDecor() {mForceDecorInstall = false;if (mDecor == null) {//生成DecorView,参考1.5mDecor = generateDecor(-1);...} else {mDecor.setWindow(this);}if (mContentParent == null) {//布局添加到DecorView,参考1.5mContentParent = generateLayout(mDecor);// Set up decor part of UI to ignore fitsSystemWindows if appropriate.mDecor.makeOptionalFitsSystemWindows();final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);if (decorContentParent != null) {} else {mTitleView = findViewById(R.id.title);}...}}
这里主要关注下两个方法:
mDecor = generateDecor(-1);
和
mContentParent = generateLayout(mDecor);
。
先看下他们的相关代码:
1.5:generateDecor()和generateLayout()
protected DecorView generateDecor(int featureId) {// System process doesn\'t have application context and in that case we need to directly use// the context we have. Otherwise we want the application context, so we don\'t cling to the// activity.Context context;...return new DecorView(context, featureId, this, getAttributes());//}public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {}protected ViewGroup generateLayout(DecorView decor) {...mDecor.startChanging();mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);...mDecor.finishChanging();return contentParent;}//DecorView.javavoid onResourcesLoaded(LayoutInflater inflater, int layoutResource) {...mDecorCaptionView = createDecorCaptionView(inflater);final View root = inflater.inflate(layoutResource, null);if (mDecorCaptionView != null) {if (mDecorCaptionView.getParent() == null) {addView(mDecorCaptionView,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}mDecorCaptionView.addView(root,new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));} else {// Put it below the color views.addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}mContentRoot = (ViewGroup) root;initializeElevation();}
通过
generateDecor()
创建了一个DecorView。DecorView实际是一个FrameLayout。
然后通过
generateLayout()
,最终将activity的布局作为子视图(ViewGroup)添加到DecorView中。
上面可以看到,activity生成到执行onCreate(),这个过程,activity生成了关联的PhoneWindow,然后创建了WindowManagerImpl、DecorView。
下面看下Window添加到WMS的过程,看这些创建的对象之前如何联系 形成的一开始介绍的结构示意图。
Window添加到WMS过程
在 AMS之应用的第一次启动过程 中主要讲到onCreate,其实onResume也在其中在,这里不详细解释了,再次列出相关代码:
//ActivityStackSupervisor.java:boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,boolean andResume, boolean checkConfig) throws RemoteException {...try {...try {// Create activity launch transaction.final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.appToken);final DisplayContent dc = r.getDisplay().mDisplayContent;clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),...// Set desired final state.final ActivityLifecycleItem lifecycleItem;if (andResume) {lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());} else {lifecycleItem = PauseActivityItem.obtain();}clientTransaction.setLifecycleStateRequest(lifecycleItem);// Schedule transaction.mService.getLifecycleManager().scheduleTransaction(clientTransaction);...}}...return true;}
通过 LaunchActivityItem 关联 最终执行结果是创建了应用的Activity 并 执行了attach()和onCreate()。 andResume为true(传入的参数为true,可以参考那篇这段代码往前看), 通过 ResumeActivityItem 关联 最终执行到的是 ActivityThread.handleResumeActivity()。
这里从ActivityThread.handleResumeActivity()来看。
2.1:ActivityThread.handleResumeActivity()
//ActivityThread.java@Overridepublic void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {...// TODO Push resumeArgs into the activity for consideration//执行onStart()->onResume()。参考2.2final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, 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;l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;...}...// The window is now visible if it has been added, we are not// simply finishing, and we are not starting another activity.if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {...r.activity.mVisibleFromServer = true;mNumVisibleActivities++;if (r.activity.mVisibleFromClient) {//参考2.3r.activity.makeVisible();}}...}
注意
View decor = r.window.getDecorView();
获取了DecorView对象,最后通过
a.mDecor = decor;
将DecorView赋到了Activity中。
这里关注两个:
performResumeActivity()
r.activity.makeVisible();
2.2:performResumeActivity()
//ActivityThread.java@VisibleForTestingpublic ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,String reason) {final ActivityClientRecord r = mActivities.get(token);...try {r.activity.onStateNotSaved();r.activity.mFragments.noteStateNotSaved();...r.activity.performResume(r.startsNotResumed, reason);r.state = null;r.persistentState = null;r.setState(ON_RESUME);reportTopResumedActivityChad0anged(r, r.isTopResumedActivity, \"topWhenResuming\");}return r;}//Activity.javafinal void performResume(boolean followedByPause, String reason) {performRestart(true /* start */, reason);mInstrumentation.callActivityOnResume(this);}final void performRestart(boolean start, String reason) {mInstrumentation.callActivityOnRestart(this);}//Instrumentation.javapublic void callActivityOnRestart(Activity activity) {activity.onRestart();}public void callActivityOnResume(Activity activity) {activity.mResumed = true;activity.onResume();...}
performResumeActivity()中
r.activity.performResume()
回调Activity的performResume()方法。最终执行了Activity的onResume()方法。
performResume()在执行onResume前,调用了
performRestart()
,最终调用的是activity的onStart()。这里可以看出 onStart()执行在onResume()之前。
2.3:r.activity.makeVisible()
//Activity.javavoid makeVisible() {if (!mWindowAdded) {ViewManager wm = getWindowManager();wm.addView(mDecor, getWindow().getAttributes());mWindowAdded = true;}mDecor.setVisibility(View.VISIBLE);}//WindowManagerImpl.javapublic final class WindowManagerImpl implements WindowManager {@UnsupportedAppUsageprivate final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyDefaultToken(params);//mParentWindow即创建WindowManagerImpl是 传入的。参考2.4mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);}}
这里的getWindowManager()获取到的是前面讲到的 attach()时通过setWindowManager()创建的WindowManagerImpl对象。
前面也讲过,addView()等3个操作定义实现 最终在WindowManagerGlobal中,这里就可以看到。
2.4:WindowManagerGlobal.addView()
//WindowManagerGlobal@UnsupportedAppUsageprivate final ArrayList<View> mViews = new ArrayList<View>();@UnsupportedAppUsageprivate final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {...final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;if (parentWindow != null) {//调整Window参数,这个过程将token设置其中了//参考2.4.1parentWindow.adjustLayoutParamsForSubWinad8dow(wparams);}...ViewRootImpl root;View panelParentView = null;synchronized (mLock) {...root = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);try {//将view及相关参数设置到ViewRootImpl中。ViewRootImpl会向WMS添加新窗口、申请Surface及绘制工作等。//参考2.6root.setView(view, wparams, panelParentView);}...}}//ViewRootImpl.java@UnsupportedAppUsagefinal IWindowSession mWindowSession;public ViewRootImpl(Context context, Display display) {mContext = context;//创建了Session(),参考2.5mWindowSession = WindowManagerGlobal.getWindowSession();mDisplay = display;mBasePackageName = context.getBasePackageName();mThread = Thread.currentThread();mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;//这里的mWindow不是前面Activity中的PhoneWindow,它是W extends IWindow.StubmWindow = new W(this);mViewVisibility = View.GONE;//创建AttachInfomAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,context);...}static class W extends IWindow.Stub {...}//ViewRootImpl.javapublic final class ViewRootImpl implements ViewParent,//View.javafinal static class AttachInfo {AttachInfo(IWindowSession session, IWindow window, Display display,ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer,Context context) {mSession = session;mWindow = window;mWindowToken = window.asBinder();mDisplay = display;mViewRootImpl = viewRootImpl;mHandler = handler;mRootCallbacks = effectPlayer;mTreeObserver = new ViewTreeObserver(context);}}
这里主要看到,创建了ViewRootImpl对象。这个类实现了View与WindowManager之间必要的协议。
注意创建中的
mWindow = new W(this);
,这个W继承IWindow.Stub。
创建ViewRootImpl对象时 创建了一个
mAttachInfo = View.AttachInfo()
, AttachInfo是一系列绑定信息。mWindowSession、mWindow作为参数传入。AttachInfo创建时注意
mWindowToken = window.asBinder();
。
mWindowSession在后续2.5/2.6/2.7中讲到,它是Session对象,它是IWindowSession的代理类,通过他可以与WMS通信的binder接口。
mWindow这里是W对象,它是IWindow.Stub,通过new创建,后续能看到会传入WMS,它是WMS回调应用(与应用通信)的binder接口。mWindowToken,也就是W的IBinder对象,也是WMS与应用通信的接口。
创建ViewRootImpl对象后,WindowManagerGlobal将View、ViewRootImpl、LayoutParams保存到相应的ArrayList中。前面也讲到过,WindowManagerGlobal是单例的,应用进程中只有一个。最后通过root.setView()将View(这里是DecorView)传入到ViewRootImpl中。
2.4.1:adjustLayoutParamsForSubWindow()
前面看到mAppToken是从Activity的传入的。
这里mAppToken被设置到WindowManager.LayoutParams里,后面可以看到最终传入到WMS参与处理。
//Window.javavoid adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {CharSequence curTitle = wp.getTitle();//子窗口,该篇中是应用窗口,所以不走这,也了解下。if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {if (wp.token == null) {View decor = peekDecorView();if (decor != null) {wp.token = decor.getWindowToken();}}...//系统窗口,也不走这} else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {...//应用窗口,该篇走这} else {if (wp.token == null) {//设置到了WindowManager.LayoutParams中wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;}...}...}
2.4.2:AttachInfo在其中了解下
ViewRootImpl与各个View。通过下面的过程,AttachInfo绑定信息被设置到各个View中了,即各个View能够获取到各种相关信息。
2.6执行到ViewRootImpl.setView()后,参考过程:setView()->requestLayout()->scheduleTraversals()->mTraversalRunnable->doTraversal()->performTraversals()->host.dispatchAttachedToWindow(mAttachInfo, 0)->View.dispatchAttachedToWindow()->ViewGroup.dispatchAttachedToWindow()。
属同个ViewGroup的 AttachInfo是一样的。
//ViewGroup.java@Override@UnsupportedAppUsagevoid dispatchAttachedToWindow(AttachInfo info, int visibility) {mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;super.dispatchAttachedToWindow(info, visibility);mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;final int count = mChildrenCount;final View[] children = mChildren;for (int i = 0; i &ad8lt; count; i++) {final View child = children[i];child.dispatchAttachedToWindow(info,combineVisibility(visibility, child.getVisibility()));}final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();for (int i = 0; i < transientCount; ++i) {View view = mTransientViews.get(i);view.dispatchAttachedToWindow(info,combineVisibility(visibility, view.getVisibility()));}}}
上述过程 performTraversals() 大致了解下:从上而下遍历视图树,每个View绘制自己,ViewGroup通知子View进行绘制。测量performMeasure() 执行布局performLayout() 绘制performDraw()。
Android绘制 重要的部分就在这里,需要了解的可以仔细研究下这个方法(performTraversals()),这里不作关注。
2.5:WindowManagerGlobal.getWindowSession()
// WindowManagerGlobal.java@UnsupportedAppUsagepublic static IWindowSession getWindowSession() {synchronized (WindowManagerGlobal.class) {if (sWindowSession == null) {try {InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();IWindowManager windowManager = getWindowManagerService();//创建Session对象sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {@Overridepublic void onAnimatorScaleChanged(float scale) {ValueAnimator.setDurationScale(scale);}});} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}return sWindowSession;}}//WindowManagerService.java@Overridepublic IWindowSession openSession(IWindowSessionCallback callback) {return new Session(this, callback);}
获取Sessiond对象,如果没有则通过
windowManager.openSession()
创建。Session是IWindowSession的代理类,然后返回给ViewRootImpl中的mWindowSession。
2.6:ViewRootImpl.setView()
//ViewRootImpl.javapublic void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {synchronized (this) {if (mView == null) {mView = view;mWindowAttributes.copyFrom(attrs);...// Schedule the first layout -before- adding to the windowad8// manager, to make sure we do the relayout before receiving// any other events from the system.requestLayout();//TODOtry {mOrigWindowType = mWindowAttributes.type;mAttachInfo.mRecomputeGlobalAttributes = true;collectViewAttributes();//参考2.7,进入system_server进程res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,mTempInsets);setFrame(mTmpFrame);}...}}}
res = mWindowSession.addToDisplay()
:mWindowSession是上面返回的创建的Session,
mWindowSession.addToDisplay()
即通过binder进入system_server进程,执行的Session.addToDisplay()。
mView即DecorView。
这里的mWindow是2.4中讲到的,是 W 继承IWindow.Stub。这是一个IBinder对象,在应用进程创建ViewRootImpl时被创建。
这里
mWindowSession.addToDisplay()
往后可以看到被传入到WMS。
2.7:Session.addToDisplay()
//Session.javaclass Session extends IWindowSession.Stub implements IBinder.DeathRecipient {final WindowManagerService mService;public Session(WindowManagerService service, IWindowSessionCallback callback) {mService = service;...}@Overridepublic 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,InsetsState outInsetsState) {//参考2.8return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,outInsetsState);}}
进入WMS,添加Window。
2.8:mService.addWindow()
终于到最后WMS.addWindow(),这里完成窗口添加。可以仔细看下下面源码及注释,这个方法即使缩减了很多还是比较长,需要耐心。
//WindowManagerService.javapublic int addWindow(Session session, IWindow client, int seq,LayoutParams attrs,15b0int viewVisibility, int displayId, Rect outFrame,Rect outContentInsets, Rect outStableInsets, Rect outOutsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState) {int[] appOp = new int[1];//检查权限,无权限不能添加窗口int res = mPolicy.checkAddPermission(attrs, appOp);if (res != WindowManagerGlobal.ADD_OKAY) {return res;}boolean reportNewConfig = false;WindowState parentWindow = null;...final int type = attrs.type;synchronized (mGlobalLock) {...//获取窗口要添加到的DisplayContent。即显示在哪个屏幕上final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);if (displayContent == null) {Slog.w(TAG_WM, \"Attempted to add window to a display that does not exist: \"+ displayId + \". Aborting.\");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}if (!displayContent.hasAccess(session.mUid)) {Slog.w(TAG_WM, \"Attempted to add window to a display for which the application \"+ \"does not have access: \" + displayId + \". Aborting.\");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}if (mWindowMap.containsKey(client.asBinder())) {Slog.w(TAG_WM, \"Window \" + client + \" is already added\");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}//添加子窗口,父窗口必须存在。if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {parentWindow = windowForClientLocked(null, attrs.token, false);if (parentWindow == null) {Slog.w(TAG_WM, \"Attempted to add window with token that is not a window: \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}//这里可以看出WMS要求 窗口的层级 最多为两层if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {Slog.w(TAG_WM, \"Attempted to add window with token that is a sub-window: \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}}if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {Slog.w(TAG_WM, \"Attempted to add private presentation window to a non-private display. Aborting.\");return WindowManagerGlobal.ADD_PERMISSION_DENIED;}AppWindowToken atoken = null;final boolean hasParent = parentWindow != null;//获取WindowToken,对于子窗口使用父窗口的token。//通过attrs.token从mTokenMap取出:private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();//关于Activity窗口:WindowToken,前面讲过ActivityRecord 创建时会创建AppWindowToken,这个过程中appToken和AppWindowToken被保存到mTokenMap中WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);// If this is a child window, we want to apply the same type checking rules as the// parent window type.final int rootType = hasParent ? parentWindow.mAttrs.type : type;boolean addToastWindowRequiresToken = false;//以下是WindowToken和窗口之间的关系if (token == null) {//以下窗口类型,WindowToken不能为空if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {Slog.w(TAG_WM, \"Attempted to add application window with unknown token \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (rootType == TYPE_INPUT_METHOD) {Slog.w(TAG_WM, \"Attempted to add input method window with unknown token \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (rootType == TYPE_VOICE_INTERACTION) {Slog.w(TAG_WM, \"Attempted to add voice interaction window with unknown token \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (rootType == TYPE_WALLPAPER) {Slog.w(TAG_WM, \"Attempted to add wallpaper window with unknown token \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (rootType == TYPE_DREAM) {Slog.w(TAG_WM, \"Attempted to add Dream window with unknown token \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (rootType == TYPE_QS_DIALOG) {Slog.w(TAG_WM, \"Attempted to add QS dialog window with unknown token \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {Slog.w(TAG_WM, \"Attempted to add Accessibility overlay window with unknownad0token \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (type == TYPE_TOAST) {// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,parentWindow)) {Slog.w(TAG_WM, \"Attempted to add a toast window with unknown token \"+ attrs.token + \". Aborting.\");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}}final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();final boolean isRoundedCornerOverlay =(attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;//token为空,除上述窗口类型,其他是允许的。此时新建WindowTokentoken = new WindowToken(this, binder, type, false, displayContent,session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);//token不为空,且是应用窗口类型,token需要时AppWindowToken类型} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {//判断token是否是AppWindowToken类型//前面知道,Activity创建的是AppWindowToken,即获得的atoken非空atoken = token.asAppWindowToken();if (atoken == null) {Slog.w(TAG_WM, \"Attempted to add window with non-application token \"+ token + \". Aborting.\");return WindowManagerGlobal.ADD_NOT_APP_TOKEN;} else if (atoken.removed) {Slog.w(TAG_WM, \"Attempted to add window with exiting application token \"+ token + \". Aborting.\");return WindowManagerGlobal.ADD_APP_EXITING;} else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {Slog.w(TAG_WM, \"Attempted to add starting window to token with already existing\"+ \" starting window\");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}//其他各种窗口类型处理} else if (rootType == TYPE_INPUT_METHOD) {...}...//WindowState维护了所有窗口的信息,它是WMS实际管理的“窗口”//它与Z-Order密切相关(多个Window层叠布局),其属性mLayer 越大,窗口越靠前。final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], seq, attrs, viewV15b0isibility, session.mUid,session.mCanAddInternalSystemWindow);if (win.mDeathRecipient == null) {// Client has apparently died, so there is no reason to// continue.Slog.w(TAG_WM, \"Adding window client \" + client.asBinder()+ \" that is dead, aborting.\");return WindowManagerGlobal.ADD_APP_EXITING;}...origId = Binder.clearCallingIdentity();//创建SurfaceSession,实现与SurfaceFlinger通信。参考2.9 简单说明下win.attach();//将WindowState对象加入到mWindowMap中mWindowMap.put(client.asBinder(), win);win.initAppOpsState();...final AppWindowToken aToken = token.asAppWindowToken();win.mToken.addWindow(win);win.applyAdjustForImeIfNeeded();...}...return res;}
WMS通过WindowState对窗口进行管理、保存状态等。
添加窗口都需要指明其WindowToken;同时窗口需指明其DisplayContent 以确定显示到哪个屏幕设备。
具体请看上面注释,比较详细了。
看完,大致能明白窗口类型、WindowToken在窗口添加中的作用。了解到token的作用。
比如:若添加的时子窗口,则必须有父窗口,且窗口的层级最多为两层。WindowToken为null,可以明显看出那些情况不允许添加。添加的窗口时应用窗口时,WindowToken要是AppWindowToken。等等。
addWindow()暂时就说这些。
这个添加后的结果,通过res 最终反回到了 2.6:ViewRootImpl.setView() 中,根据结果 继续处理。
2.9:win.attach()
为什么
win.attach()
是创建与SurfaceFlinger通信的?简单了解下。
跟踪下去是创建了SurfaceSession对象,这个创建进入native,最终创建了一个与SurfaceFlinger通信的 SurfaceComposerClient。 因此,可以与SurfaceFlinger进行通信。
ViewRootImpl创建时 就创建的mSurface,mSurface是ViewRootImpl的成员变量,此时创建的Surface什么都没有,最后通过relayoutWindow()进入WMS 一步步向SurfaceFlinger发出请求。
这时几处相关代码。
//WindowState.javavoid attach() {if (localLOGV) Slog.v(TAG, \"Attaching \" + this + \" token=\" + mToken);mSession.windowAddedLocked(mAttrs.packageName);}//Session.javavoid windowAddedLocked(String packageName) {mPackageName = packageName;mRelayoutTag = \"relayoutWindow: \" + mPackageName;if (mSurfaceSession == null) {mSurfaceSession = new SurfaceSession();...}mNumWindow++;}//ViewRootImpl.java// These can be accessed by any thread, must be protected with a lock.// Surface can never be reassigned or cleared (use Surface.clear()).@UnsupportedAppUsagepublic final Surface mSurface = new Surface();private final SurfaceControl mSurfaceControl = new SurfaceControl();
这里只说到添加窗口到WMS,关于窗口添加后的处理、界面的计算显示更新等等,以后再总结。