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与其管理者的秘密的主要内容,如果未能解决你的问题,请参考以下文章

幂等的秘密

JSON语法

洛谷——P1746 离开中山路

山路综合性能测试,高合HiPhi X实力出击

本地应用程序中的秘密管理

如何使用 cloudbuild 将秘密管理器秘密传递给 app.yaml 中的应用引擎环境变量