Window的分类
系统级Window : z-ordered为 2000-2999
应用层Window : z-ordered为 1-99
子Window : z-ordered为 1000-1999
1
根据下面谷歌的Android层级图可以看出来WindowManager是在framework层掌管Window的
从代码上看WindowManager是一个接口,此接口继承自ViewManager
public interface ViewManager { public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }
@SystemService(Context.WINDOW_SERVICE)public interface WindowManager extends ViewManager { int DOCKED_INVALID = -1; int DOCKED_LEFT = 1; int DOCKED_TOP = 2; int DOCKED_RIGHT = 3; int DOCKED_BOTTOM = 4; final static String INPUT_CONSUMER_PIP = "pip_input_consumer"; final static String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer"; final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer"; public static class BadTokenException extends RuntimeException { public BadTokenException() { } public BadTokenException(String name) { super(name); } } public static class InvalidDisplayException extends RuntimeException { public InvalidDisplayException() { } public InvalidDisplayException(String name) { super(name); } } public Display getDefaultDisplay(); public void removeViewImmediate(View view); public interface KeyboardShortcutsReceiver { void onKeyboardShortcutsReceived(List<KeyboardShortcutGroup> result); } final int TAKE_SCREENSHOT_FULLSCREEN = 1; final int TAKE_SCREENSHOT_SELECTED_REGION = 2; public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array"; public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver, int deviceId); @SystemApi @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public Region getCurrentImeTouchRegion(); public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { ... }
看上面的结构WindowManager内部定义了一些值来定位上下左右的方向,同时又一个LayoutParams
WindowManager的实现者是谁呢?
public final class WindowManagerImpl implements WindowManager { private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); private final Context mContext; private final Window mParentWindow; private IBinder mDefaultToken; public WindowManagerImpl(Context context) { this(context, null); } private WindowManagerImpl(Context context, Window parentWindow) { mContext = context; mParentWindow = parentWindow; } public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow); } public WindowManagerImpl createPresentationWindowManager(Context displayContext) { return new WindowManagerImpl(displayContext, mParentWindow); } public void setDefaultToken(IBinder token) { mDefaultToken = token; } @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); } @Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.updateViewLayout(view, params); } private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) { if (mDefaultToken != null && mParentWindow == null) { if (!(params instanceof WindowManager.LayoutParams)) { throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); } final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; if (wparams.token == null) { wparams.token = mDefaultToken; } } } @Override public void removeView(View view) { mGlobal.removeView(view, false); } @Override public void removeViewImmediate(View view) { mGlobal.removeView(view, true); } @Override public void requestAppKeyboardShortcuts( final KeyboardShortcutsReceiver receiver, int deviceId) { IResultReceiver resultReceiver = new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) throws RemoteException { List<KeyboardShortcutGroup> result = resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY); receiver.onKeyboardShortcutsReceived(result); } }; try { WindowManagerGlobal.getWindowManagerService() .requestAppKeyboardShortcuts(resultReceiver, deviceId); } catch (RemoteException e) { } } @Override public Display getDefaultDisplay() { return mContext.getDisplay(); } @Override public Region getCurrentImeTouchRegion() { try { return WindowManagerGlobal.getWindowManagerService().getCurrentImeTouchRegion(); } catch (RemoteException e) { } return null; } }
实现ViewManager的接口通过WindowManagerGlobal进行代理调用,关于得到Display通过Context代理。
在构造中传递了两个参数一个是Context我们知道是用于Dispalay的得到,另一个是Window对象看名字是父window,但是在addView和updateViewLayout中调用applyDefaultToken时进行判断要求令牌不能为null并且父Window必须是null此时才将传递进来的
ViewGroup.LayoutParams params
转化成WindowManager.LayoutParams
并且给wparams.token赋值令牌。这说明了只有当令牌不为空,没有父window也就是说必须是子Window才有令牌。
由于上面的信息我们引出了WindowManagerGlobal,由下面代码我们可以知道WindowManagerGlobal是单例,也就是说当前进程的内存中只有一份,也变相说明了就算是持有多个WindowManagerImpl也只要通过进程内唯一的WindowManagerGlobal进行add,remove等操作View,可想而知framework层是对View有个统一管理的,现在我们需要看WindowManagerGlobal内部怎么做。
private WindowManagerGlobal() { }public static WindowManagerGlobal getInstance() { synchronized (WindowManagerGlobal.class) { if (sDefaultWindowManager == null) { sDefaultWindowManager = new WindowManagerGlobal(); } return sDefaultWindowManager; } }
我们通过接口进行addView的代码是:
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params);//决定给params.token设没设置令牌 mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); }
通过传入view,params和通过context得到的Display对象的引用以及父窗口的Window对象。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ...//进行参数的判空 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; //如果父Window不为null则进行调整参数 if (parentWindow != null) { parentWindow.adjustLayoutParamsForSubWindow(wparams); } else { // 如果不是父window则从应用的硬件加速设置中对应到视图的硬件加速中 final Context context = view.getContext(); if (context != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } } ViewRootImpl root; View panelParentView = null; synchronized (mLock) { //开始监视系统属性的更改 if (mSystemPropertyUpdater == null) { mSystemPropertyUpdater = new Runnable() { @Override public void run() { synchronized (mLock) { for (int i = mRoots.size() - 1; i >= 0; --i) { mRoots.get(i).loadSystemProperties(); } } } }; SystemProperties.addChangeCallback(mSystemPropertyUpdater); } //从mViews列表中找到对应的view int index = findViewLocked(view, false); if (index >= 0) { //如果消亡的列表中有此View,则调用对应ViewRootImpl的doDie进行销毁通知操作 if (mDyingViews.contains(view)) { mRoots.get(index).doDie(); } else { //同一个View不能添加两次 throw new IllegalStateException("View " + view + " has already been added to the window manager."); } // The previous removeView() had not completed executing. Now it has. } //如果视图级别在1000-1999之间的话,并且令牌等于当前传递进来View的令牌则把对应view找出来设置成panelParentView //说明令牌和级别掌管着子view的父子关系 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); } } } //通过display信息和context得到一个ViewRootImpl对象 root = new ViewRootImpl(view.getContext(), display); //给添加的View设置params view.setLayoutParams(wparams); //将view带来的参数添加到如些列表中 mViews.add(view); mRoots.add(root); mParams.add(wparams); try { //给ViewRootImpl设置view,参数以及父面板view root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { if (index >= 0) { removeViewLocked(index, true); } throw e; } } }
WindowManagerImpl.addView的操作主要是对view的管理,将添加到的view和此view带来的一些参数添加到内部列表中,其中过程中生成ViewRootImpl才是关键。每一个添加的View都对应一个ViewRootImpl对象。
ViewRootImpl.java
public ViewRootImpl(Context context, Display display) { mContext = context; mWindowSession = WindowManagerGlobal.getWindowSession();//得到WMS中对应通信的Session通信Binder类 mDisplay = display; mBasePackageName = context.getBasePackageName();//得到包名 mThread = Thread.currentThread();//得到当前线程 mLocation = new WindowLeaked(null); mLocation.fillInStackTrace(); mWidth = -1; mHeight = -1; mDirty = new Rect(); mTempRect = new Rect(); mVisRect = new Rect(); mWinFrame = new Rect(); mWindow = new W(this);//生成一个W类 mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; mViewVisibility = View.GONE; mTransparentRegion = new Region(); mPreviousTransparentRegion = new Region(); mFirst = true; // true for the first time the view is added mAdded = false; //通过session,W,dispaly等信息起到信息汇总收集总用 mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context); mAccessibilityManager = AccessibilityManager.getInstance(context); mAccessibilityManager.addAccessibilityStateChangeListener(mAccessibilityInteractionConnectionManager, mHandler); mHighContrastTextManager = new HighContrastTextManager(); mAccessibilityManager.addHighTextContrastStateChangeListener(mHighContrastTextManager, mHandler); mViewConfiguration = ViewConfiguration.get(context); mDensity = context.getResources().getDisplayMetrics().densityDpi; mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi; mFallbackEventHandler = new PhoneFallbackEventHandler(context); mChoreographer = Choreographer.getInstance(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); if (!sCompatibilityDone) { sAlwaysAssignFocus = true; sCompatibilityDone = true; } loadSystemProperties(); }
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ... requestLayout(); ... } } }
所以当add一次view的时候就会触发requestLayout方法
@Overridepublic void requestLayout() { //初始为false if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true;//防止重复绘制 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
performTraversals这个才是核心方法
private void performTraversals() { ...... //执行测量操作 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ...... //执行布局操作 performLayout(lp, desiredWindowWidth, desiredWindowHeight); ...... //执行绘制操作 performDraw(); }
对应以下:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { if (mView == null) { return; } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { final View host = mView; host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); ...... }
private void performDraw() { ...... draw(fullRedrawNeeded); ...... }
最后分别抵达用户的onMesure,onLayout,onDraw
最后一个问题,我们从应用层已经知道尾了,我们如何知道头呢?也就是从哪里调用的addView呢?
这个起源对于Activity来说来自于Activity.attach方法生成了PhoneWindow,如果你还想追溯上去,那就了解Activity的创建过程来源于ActivityThread。ActivityThread会调用handleResumeActivity代表要显示这个Activity的界面了。所以attach调用在handleResumeActivity之前就得生成DecorView
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { .................. if (r.window == null && !a.mFinished && willBeVisible) { //获得当前Activity的PhoneWindow对象 r.window = r.activity.getWindow(); //获得当前phoneWindow内部类DecorView对象 View decor = r.window.getDecorView(); //设置窗口顶层视图DecorView可见度 decor.setVisibility(View.INVISIBLE); //得当当前Activity的WindowManagerImpl对象 ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { //标记根布局DecorView已经添加到窗口 a.mWindowAdded = true; //将根布局DecorView添加到当前Activity的窗口上面 wm.addView(decor, l); .....................