Window与其管理者的秘密
Posted 姓chen的大键哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Window与其管理者的秘密相关的知识,希望对你有一定的参考价值。
Window,字面意义即窗口,在android中,所有的视图都是通过Window来呈现的,Window是View的直接管理者。
Window是一个抽象类,具体实现是PhoneWindow。对Wndow的管理工作操作都是通过它的Manager–>WindowManager来做的,然而WindowManager也只是走个形式,真正的执行者是WindowManagerService,WindowManager通过提交工单的形式(IPC)交个WindowManagerService执行。
WindowManager管理Window的方式也很简单,常用的只有三种方法–>增删改(crud工程师),即addView(),updateViewLayout(),removeView()。这三个方法定义在ViewManager中,WIndowManager正是继承了ViewManager。
/**
* path: /frameworks/base/core/java/android/view/ViewManager.java
*/
public interface ViewManager
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
接下来看看WindowManager是如何将安排Window安排的明明白白的
本文分析的Android源码版本:android-10.0.0_r40
传送门
WindowManager的管理手段
每个Window对应一个View和ViewRootImpl,Window和View通过ViewRootImpl建立联系,所以Window是以View的形式存在的
管理Window,需要借助WindowManager提供的方法,WindowManager是一个接口,真正的实现类是WindowManagerImpl,WindowManagerImpl中对这三个操作的实现如下:
/**
*path: /frameworks/base/core/java/android/view/WindowManagerImpl.java
*/
public final class WindowManagerImpl implements WindowManager
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
@Override
public void removeView(View view)
mGlobal.removeView(view, false);
从上述的源码中可以看到WindowManagerImpl并没有亲自处理这些操作,而是全部交给了它的秘书(WindowManagerGlobal)来做,WindowManagerImpl这种工作方式被称为桥接模式,WindowManagerGlobal通过单例模式向外提供实例,
/**
* path: /frameworks/base/core/java/android/view/WindowManagerGlobal.java
*/
public final class WindowManagerGlobal
@UnsupportedAppUsage
private static WindowManagerGlobal sDefaultWindowManager;
private WindowManagerGlobal()
//单例模式,对外返回唯一实例
@UnsupportedAppUsage
public static WindowManagerGlobal getInstance()
synchronized (WindowManagerGlobal.class)
if (sDefaultWindowManager == null)
sDefaultWindowManager = new WindowManagerGlobal();
return sDefaultWindowManager;
增加Window的手段(addView())
WindowManagerGlobal 的addView() 方法源码如下:
/**
* path: /frameworks/base/core/java/android/view/WindowManagerGlobal.java
*/
//存储所有Window对应的View
@UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
//存储所有Window对应的ViewRootImpl
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//存储所有Window所对应的布局参数
@UnsupportedAppUsage
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
//存储正在被删除的View对象(调用removeView(),但删除操作还未完成的Window)
private final ArraySet<View> mDyingViews = new ArraySet<View>();
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId)
/* 1.检查参数的合法性,若是子Window还需调整布局参数 */
if (view == null)
throw new IllegalArgumentException("view must not be null");
if (display == null)
throw new IllegalArgumentException("display must not be null");
if (!(params instanceof WindowManager.LayoutParams))
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null)
parentWindow.adjustLayoutParamsForSubWindow(wparams);
else
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0)
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
/* 第一步end */
/********************************************************/
/*2.创建ViewRootImpl,并将View加入到列表中*/
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock)
// Start watching for system property changes.
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);
int index = findViewLocked(view, false);
if (index >= 0)
if (mDyingViews.contains(view))
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
else
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
// The previous removeView() had not completed executing. Now it has.
// If this is a panel window, then find the window it is being
// attached to for future reference.
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);
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
/*第二步end*/
/********************************************************/
/*3.通过ViewRootImpl来更新界面并完成对Window的添加*/
// do this last because it fires off messages to start doing things
try
root.setView(view, wparams, panelParentView, userId);
catch (RuntimeException e)
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0)
removeViewLocked(index, true);
throw e;
/*第三步end*/
/********************************************************/
WindowManagerGlobal 的addView() 方法主要做了这几件事:
- 检查参数的合法性,若是子Window还需调整布局参数
- 创建ViewRootImpl,并将View加入到列表中(列表的类型和作用已标注在源码中)
- 通过ViewRootImpl来更新界面并完成对Window的添加
这一步是通过ViewRootImpl的setView()来实现的,setView()内部通过requestLayout()来完成异步刷新请求(在requestLayout()方法中,scheduleTraversals()是View绘制的入口),之后借助WindowSession的addToDisplayAsUser()方法来完成Window的添加,源码如下:
/**
* path: /frameworks/base/core/java/android/view/ViewRootImpl.java
*/
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
setView(view, attrs, panelParentView, UserHandle.myUserId());
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId)
synchronized (this)
if (mView == null)
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
if (mWindowAttributes.packageName == null)
mWindowAttributes.packageName = mBasePackageName;
mWindowAttributes.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
attrs = mWindowAttributes;
setTag();
if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags
& WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0
&& (attrs.flags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) == 0)
Slog.d(mTag, "setView: FLAG_KEEP_SCREEN_ON changed from true to false!");
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags = attrs.flags;
setAccessibilityFocus(null, null);
if (view instanceof RootViewSurfaceTaker)
mSurfaceHolderCallback =
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null)
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
mSurfaceHolder.addCallback(mSurfaceHolderCallback);
// Compute surface insets required to draw at specified Z value.
// TODO: Use real shadow insets for a constant max Z.
if (!attrs.hasManualSurfaceInsets)
attrs.setSurfaceInsets(view, false /*manual*/, true /*preservePrevious*/);
CompatibilityInfo compatibilityInfo = mDisplay.getDisplayAdjustments().getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
// If the application owns the surface, don't enable hardware acceleration
if (mSurfaceHolder == null)
// While this is supposed to enable only, it can effectively disable
// the acceleration too.
enableHardwareAcceleration(attrs);
final boolean useMTRenderer = MT_RENDERER_AVAILABLE
&& mAttachInfo.mThreadedRenderer != null;
if (mUseMTRenderer != useMTRenderer)
// Shouldn't be resizing, as it's done only in window setup,
// but end just in case.
endDragResizing();
mUseMTRenderer = useMTRenderer;
boolean restore = false;
if (mTranslator != null)
mSurface.setCompatibilityTranslator(mTranslator);
restore = true;
attrs.backup();
mTranslator.translateWindowLayout(attrs);
if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" + attrs);
if (!compatibilityInfo.supportsScreen())
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
mLastInCompatMode = true;
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mAttachInfo.mRootView = view;
mAttachInfo.mScalingRequired = mTranslator != null;
mAttachInfo.mApplicationScale = mTranslator == null ? 1.0f : mTranslator.applicationScale;
if (panelParentView != null)
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
/*完成异步刷新请求*/
/********************************************************/
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
/********************************************************/
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)
inputChannel = new InputChannel();
mForceDecorViewVisibility = (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
/*通过WindowSession来添加Window*/
/********************************************************/
try
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
adjustLayoutParamsForCompatibility(mWindowAttributes);
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
setFrame(mTmpFrame);
catch (RemoteException e)
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
inputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
finally
if (restore)
attrs.restore();
/********************************************************/
if (mTranslator != null)
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
mAttachInfo.mAlwaysConsumeSystemBars =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0;
mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls);
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY)
mAttachInfo.mRootView = null;
mAdded = false;
mFallbackEventHandler.setView(null)以上是关于Window与其管理者的秘密的主要内容,如果未能解决你的问题,请参考以下文章