Window机制探索

接着 Activity 的启动流程, 来探索一下 Window 的机制

Window 的创建

    /**
     * ActivityThread.handleResumeActivity
     */
    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        // 回调了 onResume
        r = performResumeActivity(token, clearHide, reason);
        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;
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 重点来了, 将这个顶层的 DecorView 添加到 Window 中
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }
        }
    }
    /**
     * ActivityThread.performLaunchActivity
     */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ContextImpl appContext = createBaseContextForActivity(r);
        // 忽略 Activity 反射创建的细节
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        // 这个 Attach 非常重要, 现在我们就来分析一下这个 attach 
        activity.attach(..., app, ...);
        // 回调 onCreate
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        ...
        return activity;
    }
    
    /**
     * Activity.attach
     */
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        // 1. 这里创建了一个 Window 实例对象
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();
        // 2. 给这个 Window 实例对象设置 WindowManager
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        // 3. 获取在 <2> 中设置的 WindowManager 对象
        mWindowManager = mWindow.getWindowManager();
        mWindow.setColorMode(info.colorMode);
    }
    
    /**
     * Window.setWindowManager
     */
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        // 2.1 这个 wm, 是应用层的 WindowManager, 是进程间单例的
        // SystemServiceRegistry 在中的 static{...} 代码块中注册了这些应用层的服务
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        // 2.2 可以看到这里又调用了一个 createLocalWindowManager 
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
    
    
    /**
     * WindowManagerImpl.createLocalWindowManager
     */
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        // 2.2.1 
        // WindowManagerImpl 是 WindowManager 的实现类
        // 可见创建了一个新的 WindowManager 对象返回回去
        // 也就是说每个 Activity 直接中通过 Activity.getWindowManager() 获取到的 WindowManager 对象都是不一样的
        return new WindowManagerImpl(mContext, parentWindow);
    }
复制代码

这里我们是接着Activity启动流程去分析的, 可见在 performLaunchActivity 在反射创建了 Activity 实例对象之后、在回调 onCreate 之前, 调用了 attach 方法绑定相关参数进行了 Window 初始画相关操作:

  1. 创建实际类型变量 PhoneWindow 给 mWindow 赋值
  2. 让 mWindow 绑定 WindowManager
    • 传入了一个系统级的 WindowManager 对象 wm:
      (WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
    • 通过 wm 创建了一个本地的 WindowManager 对象:
      ((WindowManagerImpl)wm).createLocalWindowManager(this);
    • 将这个本地的 WindowManager 对象与 Activity 绑定:
      mWindowManager = mWindow.getWindowManager();
  3. Activty.attach() 方法也解释了为什么在不同的 Activity 中通过 Activity.getWindowManager() 获取到的 WindowManager 对象都是不一样的原因

之后的在 onCreate() 中的 setContentView()中的细节, 这里就不做分析了

Window添加View的过程

    /**
     * WindowManagerImpl.addView
     */
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    
    /**
     * WindowManagerGlobal.addView
     * private final ArrayList<View> mViews = new ArrayList<View>();
     * private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
     * private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
     */
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        // 1. 对参数做一些验证
        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;
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            // 2. 从缓存中查找这个 View 是否已经添加进当前 Window 中了
            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.");
                }
            }
            // 3. 判断是否是 SubWindow, 若是子 window 则找出它所依赖的父 Window
            // 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);
                    }
                }
            }
            // 4. 构建 ViewRootImpl 对象
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            // 5. 将三个参数同时放入对应的集合维护, 他们的 index 都是一样的
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                // 6. 调用了 ViewRootImpl 的 setView 方法
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
复制代码

好看到这里我们就来总结一下了, WindowManager 的 addView 具体做了些什么

  1. 从 WindowManager 的实现类 WindowManagerImpl.addView 方法入手, 它调用了 WindowManagerGlobal 的 addView 方法
  2. WindowManagerGlobal.addView 具体做了如下的事情:
    • 对传入的参数做一些校验, 判断是否符合要求
    • 通过 findViewLocked 方法从缓存(mViews)中查找 View , 若存在则判断是否已经被 WindowManager 添加
    • 通过传入的 WindowManager.LayoutParmas 对象 wparams, 来判断 Window 的类型是否为 SubWindow
      • 若为 SubWindow 类型则需要找到其所依附的父 Window, 找到后赋给 panelParentView
    • 构建 ViewRootImpl 对象
    • 将校验后的参数加各自对应的集合中去缓存
    • 调用了 ViewRootImpl 的 setView 方法 可见 WindowManager 的 addView 分析到这里并没有看到任何添加进 Window 的代码
      我们接着往下看 ViewRootImpl 这个类到底做了哪些事情它是如何将 View 设置进 Window 的

ViewRootImpl

    /**
     * ViewRootImpl.Constructor()
     */
    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        // Point1
        mWindowSession = WindowManagerGlobal.getWindowSession();
        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();
        // Point2 
        mWindow = new W(this);
        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;
        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();
    }
复制代码

好, 可以看到 ViewRootImpl 对象在创建的过程中出现了两个值得关注的点

  • mWindowSession = WindowManagerGlobal.getWindowSession();
  • mWindow = new W(this);
先关注点1: mWindowSession 实例的创建
    /**
     * WindowManagerGlobal.getWindowSession
     */
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    // 1. 获取远程 WindowManagerService 的实例
                    IWindowManager windowManager = getWindowManagerService();
                    // 2. 通过这个实例获取一个远程的会议对象: WindowSession 
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
    
    /**
     * WindowManagerGlobal.getWindowManagerService
     */
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                // 1.1 ServiceManager.getService("window"), 这个获取到的就是 WindowManagerService.Stub(远程 IBinder)对象
                // 1.2 注意这个 ServiceManager.getService 与 context.getSystemService(...) 方法的不同
                // ServiceManager.getService 跨进程获取服务
                // context.getSystemService(...) 获取当前进程的服务如 Context.WINDOW_SERVICE
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }
    
    /**
     * WindowManagerService.openSession
     */
    @Override
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
        if (client == null) throw new IllegalArgumentException("null client");
        if (inputContext == null) throw new IllegalArgumentException("null inputContext");
        // 2.1 创建了一个 Session 对象并且返回回去, 就这么简单...
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }
复制代码

可以看到 WindowSession 的创建主要分为两步

  1. 获取远程 WindowManagerService 实例
  2. 通过 WindowManagerService 实例来创建一个 Session 对象
    (即 IWindowSession 的实现者对象, 这个命名比较有意思, 按理 Android 源码的套路实现类应该为 WindowSessionService 才对)
关注点2: mWindow 实例的创建
   /**
    * ViewRootImpl.W 
    * <p>
    * 为 ViewRootImpl 的内部类
    */
   static class W extends IWindow.Stub {
        // 可以看到其内部关联了一个 mViewRootImpl 对象和一个 mWindowSession
        private final WeakReference<ViewRootImpl> mViewAncestor;
        private final IWindowSession mWindowSession;
        
        W(ViewRootImpl viewAncestor) {
            mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
            mWindowSession = viewAncestor.mWindowSession;
    }
复制代码

Wow!!!
可见真正可以让 WindowManagerService 添加的 Window 其实就是 ViewRootImpl.W 这个 IWindow.Stub 的 Binder 实例对象
这是一个在当前进程创建的 Binder 对象!
想想之前的 Window 和其实现类 PhoneWindow, 那只是Window机制最顶层的表现

开始分析 setView方法
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                // 1. 给参数赋值
                mAttachInfo.mDisplayState = mDisplay.getState();
                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
                mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
                mFallbackEventHandler.setView(view);
                mWindowAttributes.copyFrom(attrs);
                if (mWindowAttributes.packageName == null) {
                    mWindowAttributes.packageName = mBasePackageName;
                }
                attrs = mWindowAttributes;
                mClientWindowLayoutFlags = attrs.flags;
                setAccessibilityFocus(null, null);
                mSoftInputMode = attrs.softInputMode;
                mWindowAttributesChanged = true;
                mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
                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; */
                // 2. 在添加进 Window 之前先执行一次 ViewRootImpl.requestLayout 方法, 确保在接收系统事件之前已经完成了布局的 layout
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    // 3. 调用了 Session 的 addToDisplay 方法, 将 View 添加并显示
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } 
                ...
            }
        }
    }
    
    /**
     * ViewRootImpl.requestLayout
     */
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            // 2.1 调用了 ViewRootImpl.scheduleTraversals
            scheduleTraversals();
        }
    }
    
    /**
     * ViewRootImpl.scheduleTraversals
     */
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 2.2 关注这行代码, 执行了一个 Runnable
            // final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
           ...
        }
    }
    
    /**
     * ViewRootImpl.TraversalRunnable
     */
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            // 2.3 调用了 ViewRootImpl.doTraversal 
            doTraversal();
        }
    }
   
    /**
     * ViewRootImpl.doTraversal
     */
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            // 2.4 这个一直苦苦追寻的方法终于出现了, View 绘制的三大流程的发起方法!!!!!
            performTraversals();
        }
    }
    
    /**
     * Session.addToDisplay
     */
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        // 3.1 这里终于看到了执行 Window 添加的方法
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
    
    /**
     * WindowManagerService.addWindow
     */
    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        int[] appOp = new int[1];
        // 检查添加权限, 若高层级的 Window 则需要在 Manifest 中声明
        int res = mPolicy.checkAddPermission(attrs, appOp);
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }
        boolean reportNewConfig = false;
        WindowState parentWindow = null;
        long origId;
        final int callingUid = Binder.getCallingUid();
        final int type = attrs.type;
        // 根据mWindowMap 中的 key 返回不同的 返回码, 这里就不往下分析了
        ...
    }
    
复制代码

ViewRootImpl.setView 这个方法真是让人兴奋, 我们平时从大牛博客上观看到的方法, 终于被我们在这里找了出来

  1. 给相关参数赋值, 不阐述了
  2. 调用了 ViewRootImpl.requestLayout 方法最终会走到 performTraversals 中执行 View 绘制的三大流程, 这里不去阐述具体的细节
  3. 调用了 Session 的 addToDisplay 方法, 最终调用到 WindowManagerService.addWindow 将 W(IWindow) 添加并展示
至此, 对 Android 中整个 Window 的作用和机制就有一个大概的了解了, 画图分析一下

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值