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);
.....................
共同学习,写下你的评论
评论加载中...
作者其他优质文章
