美文网首页
WindowManager源码分析小结

WindowManager源码分析小结

作者: Dane_404 | 来源:发表于2019-03-19 23:20 被阅读0次

    研究源码的目的,我个人理解是这样的:懂得底层工作原理,可以更好更快的定位BUG,写项目更得心应手,比如,知道了ViewRootImpl什么时候创建,所以为什么在onCreate中View.getMeasure会是0,知道了ViewRootImpl检查线程的时机,知道了为什么子线程中更新UI为什么不一定会报错等等,最重要的是,我们改BUG,定位BUG更有自信,从而可以去分析解剖别人的框架,一步一步,你也可以写出好框架,当然,不要忘了还要有设计模式的思想起看别人的代码。所以,参考了前辈的分析,自己过一遍,记录下自己理解分析的过程,不过有个前提是,会先懂Binder机制,这一点对看Android源码很重要。

    分析之前,先有个概念:

    就是每个Window都对应着一个View和一个ViewRootImpl,Window和View是通过ViewRootImpl来建立联系,所以一个Activity会有一个Window,而最终Window的请求添加View的操作会交给WindowManagerService去处理,WindowManagerService存在于系统进程,不是我们应该的进程,所以这是一个跨进程的过程,通过Binder通信。

    我们通常通过getSystemService拿到WindowManager,然后通过WindowManager的addView方法就可以将一个View添加到屏幕上,所以就先从addView入手:

    点进去你发现不是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);
        }
    

    可以看到ViewManager 定义了Window的添加、更新和操作,我相信你就会去想WindowManager实现了ViewManager ,找WindowManager,没错,确实WindowManager实现了ViewManager,但是你会发现WindowManager也是一个接口,没做什么:

      public interface WindowManager extends ViewManager
    

    这时候我们是不是想这找WindowManager的实现类,没错确实是这样,按照套路很多时候都是后面加个Impl,也就是WindowManagerImpl,那么全局搜索下WindowManagerImpl,果然就是它了:

      @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);
        }
    
        @Override
        public void removeView(View view) {
            mGlobal.removeView(view, false);
        }
    

    可以看到,交给了一个成员mGlobal去处理,先看下这个mGlobal:

      private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    

    可以看到交给了WindowManagerGlobal 去处理了,那么点进去看下WindowManagerGlobal 的addView,跟着源码和注释看下去:

      private final ArrayList<View> mViews = new ArrayList<View>();  //存放View
      private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); //存放ViewRootImpl
      private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();  //存放WindowManager.LayoutParams
      private final ArraySet<View> mDyingViews = new ArraySet<View>(); //存放将要被删除的View
      -----------------------------------上面是WindowManagerGlobal 的成员-----------------------------
      public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
            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)) {   //判断,必须是WindowManager.LayoutParams
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }
    
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (parentWindow != null) {    //parentWindow 一般就是PhoneWindow
                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;
                }
            }
    
            ViewRootImpl root;    //可以看到,会在下面在创建ViewRootImpl 
            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);  //查询View有没已经添加了,就是通过上面存放View的集合查找
                if (index >= 0) {   没有添加过会返回-1,返回是集合的索引值
                    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);  //创建ViewRootImpl
    
                view.setLayoutParams(wparams);  
    
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
            }
    
            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);  //将View和ViewRootImpl关联
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                synchronized (mLock) {
                    final int index = findViewLocked(view, false);
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                }
                throw e;
            }
        }
    

    可以看到,最终由ViewRootImpl的setView处理,那么看下setView:

      public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
                    mView = view;
                    ---------------------省略部分代码-------------------
                    // 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();     //这一步最终会检查线程,走View的流程
                    if ((mWindowAttributes.inputFeatures
                            & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                        mInputChannel = new InputChannel();
                    }
                    mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                            & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                    try {
                        mOrigWindowType = mWindowAttributes.type;
                        mAttachInfo.mRecomputeGlobalAttributes = true;
                        collectViewAttributes();     
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),  //这里是Binder过程,下面分析
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
                    } catch (RemoteException e) {
                        mAdded = false;
                        mView = null;
                        mAttachInfo.mRootView = null;
                        mInputChannel = null;
                        mFallbackEventHandler.setView(null);
                        unscheduleTraversals();
                        setAccessibilityFocus(null, null);
                        throw new RuntimeException("Adding window failed", e);
                    } finally {
                        if (restore) {
                            attrs.restore();
                        }
                    }
                   ---------------------省略部分代码-------------------   
                }
        }
     requestLayout:
    
      @Override
      public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();    //检查线程,就是不能在子线程更新UI
            mLayoutRequested = true;
            scheduleTraversals();    //会调用performTraversals,完成三大流程
        }
      }
    

    requestLayout执行完, mWindowSession.addToDisplay的完成添加过程,看下mWindowSession的定义,它在ViewRootImpl的构造中被赋值:

       final IWindowSession mWindowSession;
       mWindowSession = WindowManagerGlobal.getWindowSession();
    

    那么看下 WindowManagerGlobal.getWindowSession():

      public static IWindowSession getWindowSession() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowSession == null) {
                    try {
                        InputMethodManager imm = InputMethodManager.getInstance();
                        IWindowManager windowManager = getWindowManagerService(); 
                        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;
            }
        }
    

    上面的 IWindowManager windowManager = getWindowManagerService(),这时候就应该想到系统进程的WindowManagerService,没错,先看下getWindowManagerService:

      public static IWindowManager getWindowManagerService() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowManagerService == null) {
                    sWindowManagerService = IWindowManager.Stub.asInterface( //明显是AIDL,返回了WindowManagerService
                            ServiceManager.getService("window"));
                    try {
                        sWindowManagerService = getWindowManagerService();
                        ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
                return sWindowManagerService;
            }
        }
    

    上面的进一步说下,Android源码中有个IWindowManager.aidl文件,很平时使用AIDL同样的原理,看下WindowManagerService的继承关系就明白了:

      public class WindowManagerService extends IWindowManager.Stub
    

    所以说,mWindowSession 是通过AIDL跨进程调用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");
        Session session = new Session(this, callback, client, inputContext);  //this,是WindowManagerService 
        return session;
      }
    

    看这就大概明白,mWindowSession 真正的实现是Session,最终所有工作交给WindowManagerService :

      @Override
      public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {    //mService,就是WindowManagerService 
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
      }
    

    至于WindowManagerService怎么去添加就不深入研究,知道了这一个原理工程,知道了View出现在WindowManagerService是通过WindowManager的addView,WindowManagerService是系统进程,那么就可以去分析Activity启动过程了。

    相关文章

      网友评论

          本文标题:WindowManager源码分析小结

          本文链接:https://www.haomeiwen.com/subject/xaoomqtx.html