美文网首页
Activity窗口机制源码解析下(8.0)

Activity窗口机制源码解析下(8.0)

作者: Android_Jian | 来源:发表于2018-09-22 22:03 被阅读11次

这个小节我们接着ActivityThread 类中的handleLaunchActivity方法继续分析,接下来就到了3 处的handleResumeActivity方法,我们跟进去看下(为方便分析,代码有所删减):

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
      
        //1.完成 Activity onResume生命周期函数回调
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            //获取到当前activity对象
            final Activity a = r.activity;

            if (r.window == null && !a.mFinished && willBeVisible) {
                //获取到当前activity对应的phoneWindow对象,并赋值给r.window
                r.window = r.activity.getWindow();
                //获取到当前activity对应的decorview对象
                View decor = r.window.getDecorView();
                //将decorview暂时设置为不可见,注:decorview本质为FrameLayout
                decor.setVisibility(View.INVISIBLE);
                //获得ViewManager实例,实质为WindowManagerImpl对象
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
              
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;

                        //2.调用WindowManagerImpl对象的addView方法,将decorview与Window进行绑定,底层通过IPC机制
                        wm.addView(decor, l);
                    } else {
                        // The activity will get a callback for this {@link LayoutParams} change
                        // earlier. However, at that time the decor will not be set (this is set
                        // in this method), so no action will be taken. This call ensures the
                        // callback occurs with the decor set.
                        a.onWindowAttributesChanged(l);
                    }
                }

            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }

            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
              
                if (r.activity.mVisibleFromClient) {
                    //3.将decorview设置为可见
                    r.activity.makeVisible();
                }
            }

         ...

        }
    }

上述代码中重要的部分已经做了标注,1 处 performResumeActivity方法,我们在分析Activity启动流程的时候有讲过,该方法中完成了 Activity onResume生命周期函数的回调,在这里就不赘述了。我们看下 2 处 wm.addView方法,wm实质为WindowManagerImpl对象,所以就来到了WindowManagerImpl的addView方法,我们跟进去看下:

    #WindowManagerImpl
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

可以看到,WindowManagerImpl的addView方法直接调用了mGlobal.addView方法。mGlobal是什么呢?我们看下它的定义:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

可以看到mGlobal实质为WindowManagerGlobal实例对象,同时我们也看到mGlobal是通过单例的模式来进行获取的。我们跟进去WindowManagerGlobal的addView方法去看下:

    #WindowManagerGlobal
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
         
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            
            //1.调用到findViewLocked方法,判断当前decorview是否已经被添加过
            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.
            }
              
            //2.创建ViewRootImpl对象
            root = new ViewRootImpl(view.getContext(), display);

            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 {

                //3.调用 ViewRootImpl 对象的 setView方法
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

注意在该方法中参数 view 为当前decorview对象,parentWindow 为当前 phonewindow 对象,具体细节赋值操作,在这里就不展开分析了,有兴趣的可以跟进源码看下。在分析 1 处findViewLocked方法之前,我们先来看下 WindowManagerGlobal中几个比较重要的成员变量:

    //1.
    private final ArrayList<View> mViews = new ArrayList<View>();
    //2.
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    //3.
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    //4.
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

我们刚才也看到,WindowManagerGlobal对象是通过单例的方式来获取的,所以我们的应用程序中只存在一个WindowManagerGlobal单例对象。在上面的声明中,mViews存储的是所有Window所对应的View,mRoots存储的是所有ViewRootImpl实例对象,mParams存储的是所有Window所对应的布局参数,而mDyingViews中存储的是将要被删除的view对象。
好了,我们接着之前的分析,首先我们看下 1 处的 findViewLocked方法,注意 1 处调用findViewLocked方法所传的第二个参数为false:

    private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
        if (required && index < 0) {
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;
    }

我们上面提到,mViews中存储的是所有phonewindow所对应的view对象,可以看到方法中调用到mViews.indexOf方法,用来判断当前decorview是否已经被添加过,由于我们传入的required参数值为false,所以findViewLocked接着直接将index返回掉,如果index的值大于等于0,就说明当前decorview已经被添加过,如果index的值为-1就说明当前decorview未被添加过。我们回到addView方法中可以看到,后续操作对index的值进行了判断,当index的值大于等于0时,表明当前decorview已经被添加过,接着进一步调用mDyingViews.contains(view)方法,如果该方法return true的话,会调用mRoots.get(index).doDie();方法,将已经添加的decorview移除掉,否则直接抛出异常。

好了,我们接着看下 2 处创建ViewRootImpl对象,在这里你可能会说,一个对象的创建有什么好看的,不就是一个 new 关键字吗,其实在这里不然,里面暗藏玄机,我们跟进去看下ViewRootImpl对象的构造方法就知道了:

    public ViewRootImpl(Context context, Display display) {
       
        //重点!!!
        mWindowSession = WindowManagerGlobal.getWindowSession();
        
        ...

    }

可以看到mWindowSession就是在构造方法中进行赋值的,我们先看下mWindowSession的类型:

final IWindowSession mWindowSession;

有没有似曾相识的感觉,对的,IWindowSession就是AIDL类型的接口,decorview被WindowManager正式添加到Window中底层是通过IPC机制来实现的,其中客户端进程对应的就是mWindowSession,而SystemServer进程中对应的Binder实现就是Session类。
好了,我们看到mWindowSession的赋值是通过调用WindowManagerGlobal.getWindowSession();方法来实现的,我们跟进去看下:

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

方法中首先调用到getWindowManagerService方法,获取到IWindowManager对象,等等,IWindowManager?该不会也是一个AIDL接口吧?对的,没毛病,其实IWindowManager在这里代表Binder连接池对应的AIDL接口,SystemServer进程中对应的Binder实现类为 WindowManagerService 。我们可以看到 2 处通过调用windowManager.openSession方法来获取到sWindowSession实例对象,其实windowManager.openSession方法就涉及到了IPC操作,远程调用到WindowManagerService的openSession方法。我们先分析下 1 处IWindowManager对象的获取,可以看到它是通过调用getWindowManagerService方法来实现的,我们自然是跟进去看下:

    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

实际上在这里调用getWindowManagerService方法,sWindowManagerService已经进行过赋值操作了,方法中会直接返回sWindowManagerService。在这里你可能会疑惑,sWindowManagerService的赋值操作是什么时候进行的呢?我们就来揭开神秘的面纱,上个小节的handleLaunchActivity少年你可还记得?好,那handleLaunchActivity方法中 1 处的调用代码 WindowManagerGlobal.initialize(); 你可还记得?sWindowManagerService的赋值操作就是这行代码进行操作的,怎么你不信,我们回到WindowManagerGlobal.initialize()方法中去看下:

    public static void initialize() {
        getWindowManagerService();
    }

可以看到 initialize 方法中直接调用到getWindowManagerService方法,对 sWindowManagerService 完成了赋值操作,有没有豁然开朗柳暗花明的感觉哈哈。
好了我们接着回到getWindowSession 方法的 2 处,刚我们提到过, 2 处windowManager.openSession方法实际上远程调用到WindowManagerService的openSession方法,我们跟进去看下:

    #WindowManagerService
    @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);
        return session;
    }

可以看到WindowManagerService的openSession方法中直接 new 了一个Session对象,然后 return掉。根据我们的分析,Session为IWindowSession
AIDL接口在SystemServer进程中对应的Binder实现类,到底是不是这样子呢,我们看下Session类的定义:

    public class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {

果然如此哈哈。

好了,ViewRootImpl对象的创建操作,这里我们就分析完毕了,总结一句话就是ViewRootImpl对象创建的时候,在它的构造方法中获取到了IWindowSession 实例对象,为后续decorview被WindowManager正式添加到Window中这一IPC操作做准备。我们接着往下看,3 处调用到 ViewRootImpl 对象的 setView 方法,我们跟进去看下,方法中的代码量有点大,我做了删减操作:

     #ViewRootImpl
     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {

                //1.重点,view测量绘制操作的源头就是这里
                requestLayout();
              
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();

                    //2.远程调用到 SystemServer进程中 Session类 的addToDisplay方法,完成decorview被WindowManager正式添加到Window操作
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            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);
                } 
  
                ...

        }
    }

首先我们先看下 1 处的requestLayout方法,代码中标注了该方法就是view测量绘制的源头。你也许会说,view的测量绘制不是由ViewRootImpl类的 performTraversals 方法开始的吗?不知道你有没有想过 performTraversals 方法是怎么被调用的呢?其实就是由 requestLayout 方法调用到的,不信我们跟进去代码看下:

    #ViewRootImpl
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

可以看到 requestLayout 方法中首先调用到 checkThread方法,根据方法名字我们就可以猜到,这个方法是用来检验当前线程是否为UI线程,我们跟进去看下:

    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

果然如我们所料,相信方法中的异常大家在刚刚学习Android知识的时候肯定遇到过,当我们在工作线程中进行更新UI操作时,就会抛出该异常。其实我们在对控件进行UI操作时,底层都会调用到requestLayout方法,重新进行测量绘制操作。好了,我们回到requestLayout方法接着往下看,可以看到方法中调用到scheduleTraversals方法,跟进去看下:

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

            //重点
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

scheduleTraversals方法中调用到 mChoreographer.postCallback方法,关于mChoreographer定义以及postCallback的细节由于篇幅原因这里就不深入分析了,有兴趣的可以翻阅源码看下,postCallback方法中传入的第二个参数 mTraversalRunnable 实质为 Runnable对象,我们看下它的定义:

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

final class TraversalRunnable implements Runnable {

所谓的异步绘制操作就是从这里开始的,还等什么,接下来肯定是看下 TraversalRunnable 的 run 方法啦:

   final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

run方法中直接调用了doTraversal 方法,我们接着跟:

   #ViewRootImpl
   void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            //重点!!!
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

在 doTraversal 方法中我们终于找到了 performTraversals 方法的调用。performTraversals方法中是如何进行测量绘制操作的,在这里就不展开分析了,后续笔者会专门开一小节来进行分析。

好了好了,我们回到 ViewRootImpl 的 setView 方法中接着往下看,requestLayout方法调用完毕后就来到 2 处,mWindowSession.addToDisplay方法通过IPC机制远程调用到 SystemServer进程中 Session类 的addToDisplay方法,我们跟进去看下:

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

可以看到,Session 类的 addToDisplay 方法直接调用到 mService.addWindow 方法,我们看下 mService 的定义:

    final WindowManagerService mService;

mService其实就是 WindowManagerService 实例对象,所以说 Window的添加操作最终还是通过 WindowManagerService 的addWindow方法来实现的。后续代码我们就不再分析了。

Window添加完毕后,最后我们回到 handleResumeActivity 方法的 3 处,调用到 r.activity.makeVisible();方法,将decorview设置为可见,我们跟进去Activity的makeVisible方法看下:

    #Activity
    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        //重点
        mDecor.setVisibility(View.VISIBLE);
    }

很简单,Activity的 makeVisible方法中直接将 mDecor设置为VISIBLE,这样我们就能看到Activity 界面了。

到这里,Activity窗口机制源码解析这一部分就告一段落了,欢迎大家一起交流。

相关文章

网友评论

      本文标题:Activity窗口机制源码解析下(8.0)

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