Android面试相关Android技术知识Android知识

Window机制探索

2018-01-16  本文已影响78人  SharryChoo

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

Window 的创建

    /**
     * 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的过程

    /**
     * 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);
                }
            }
        }
    }

    /**
     * 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 对象在创建的过程中出现了两个值得关注的点

先关注点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方法
    /**
     * ViewRootImpl.setView
     * 这个方法通过 WindowMangaerGlobal 调用, 负责将一个 View 添加到 Window 中
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                // 这个 mView 就是 Window 中最顶层的 View
                mView = view;
                // 1. 将相关参数写入 mAttachInfo 中
                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;// 这个 Winodw 的根 View 即设置进来的 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 方法, 将新的 Window 添加进 WMS 进行管理并展示
                    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 的作用和机制就有一个大概的了解了, 画图分析一下

Window架构图.png
上一篇 下一篇

猜你喜欢

热点阅读