View工作原理

171 阅读6分钟

1.概述

上一篇文章分析了App的启动流程,方法执行到了handleLaunchActivity这里,后续又通过performLaunchActivity方法完成了Activity的创建并执行onCreate方法,相应的还会执行到handleResumeActivity进而执行到performResumeActivity来执行Activity的onResume方法。其实在ActivityThread中有一整套对应Activity生命周期的处理方法。 从handleXXXActivityperformXXXActivity,Activity的生命周期处理流程之后会单独用一篇文章来分析。这里仅分析在Activity创建成功之后界面是如何工作起来的。整体的工作流程可以大体分为几个部分:
1)创建Window与DecorView,将Window与Activity建立关联。
2)WindowManager通过ViewRootImpl来管理DecorView的测量、布局、绘制。
3)ViewRootImpl向主线程的消息队列里发送消息屏障确保View的工作流程优先执行,并且将调度交给Choreographer。
4)Choreographer等待Vsync刷新信号到来执行ViewRootImpl的调度任务。
5)ViewRootImpl移除主线程消息队列的同步屏障并开始View的测量、布局、绘制。

2.执行过程

整体代码的执行过程涉及Activity、Window、DecorView的创建以及WindowManager的获取,还包括ViewRootImpl和Choreographer的应用。

2.1 创建Activity与Window

创建Activity的入口是ActivityThread的handleLaunchActivity方法,进而会调用performLaunchActivity方法完成创建。

ActivityThread#handleLaunchActivity
public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {
    ....
    //初始化WindowManagerGlobal
    WindowManagerGlobal.initialize();
    final Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        //创建Activity成功
        //设置Configuration
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        ....
    } else {
        //任何原因导致Activity创建失败都需要调用AMS来关闭掉
        try {
            ActivityManager.getService()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
    return a;
}
ActivityThread#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    //创建Context
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        //借助Instrumentation通过反射创建Activity
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        ....
    } catch (Exception e) 
    try {
        //获取Application对象
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        if (activity != null) {
            ....
            //关键代码,执行Activity的attach
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);
            ....
            //执行Activity的onCreate方法
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            ....
        }
        //设置生命周期状态为ON_CREATE
        r.setState(ON_CREATE);
        //添加到activities集合中
        mActivities.put(r.token, r);
        ....
    return activity;
}

到这里Activity就已经创建成功了,并且执行了onCreate方法,这里有一段关键代码是activity.attach。

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) {
    //绑定baseContext    
    attachBaseContext(context);
    mFragments.attachHost(null /*parent*/);
    //创建Window,实现类是PhoneWindow
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    //设置activity为window的callback
    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);
    }
    ....
    省略一些参数的赋值
    ....
    //给Window设置WindowManager,本质上这里通过(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)方法获取的是WindowManagerImpl
    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());
    }
    //获取Window的WindowManager
    mWindowManager = mWindow.getWindowManager();
    ....
}
//上文context.getSystemService(Context.WINDOW_SERVICE)方法最终其实会调用到这里
SystemServiceRegistry#registerService
registerService(Context.WINDOW_SERVICE, WindowManager.class,
        new CachedServiceFetcher<WindowManager>() {
    @Override
    public WindowManager createService(ContextImpl ctx) {
        return new WindowManagerImpl(ctx);//返回的是WindowManagerImpl
    }});

attach方法进行了Window(PhoneWindow)的创建,并且将Activity传给Window作为callback,最后给Window设置WindowManager。

2.2 创建DecorView

View的工作流程是在Activity可见(onResume)之后开始的,ActivityThread中的 handleResumeActivity是onResume方法的根源入口。

ActivityThread#handleResumeActivity
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    ....
    //调用performResumeActivity方法处理生命周期状态,执行onResume方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    ....
    final Activity a = r.activity;
    ....
    if (r.window == null && !a.mFinished && willBeVisible) {
        //获取Window
        r.window = r.activity.getWindow();
        //关键代码,获取DecorView,调用的是PhoneWindow的getDecorView方法
        View decor = r.window.getDecorView();
        //DecorView设置不可见
        decor.setVisibility(View.INVISIBLE);
        //获取WindowManager
        ViewManager wm = a.getWindowManager();
        //获取Window的LayoutParams
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        l.softInputMode |= forwardBit;
        if (r.mPreserveWindow) {
            //布局变更的时候调用
            a.mWindowAdded = true;
            r.mPreserveWindow = false;
            ViewRootImpl impl = decor.getViewRootImpl();
            if (impl != null) {
                impl.notifyChildRebuilt();
            }
        }
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                //关键代码,WindowManager的addView方法
                wm.addView(decor, l);
            } else {
               ....
            }
        }
    } 
    ....
    省略一些Activity的处理逻辑
    ....
}

这里有两处关键代码,一处是r.window.getDecorView获取DecorView。

PhoneWindow#getDecorView
public final View getDecorView() {
    //如果DecorView没有初始化则进行初始化,其实大部分情况下这里已经初始化了,因为在调用
      setContentView方法时会进行初始化,onCreate时候大部分情况下会进行setContentView
    if (mDecor == null || mForceDecorInstall) {
        //初始化DecorView
        installDecor();
    }
    //返回DecorView
    return mDecor;
}

DecorView是PhoneWindow绑定的根View,继承于FrameLayout。我们平时通过setContentView设置的View其实是添加到了DecorView的id为com.android.internal.R.id.content的子View中了。 另一处关键是wm.addView,将DecorView添加到WindowManager中,是调用WindowManager的addView方法,本质上是调用WindowManager实现类WindowManagerImpl的addView方法。WindowManager是上文Activity的attach方法中设置的。

2.3 ViewRootImpl处理DecorView

ViewRootImpl相当于DecorView的大管家,全权处理View的相关工作的调度,包括测量、布局、绘制的控制,还包括点击事件的收发。这里只关注测量、布局、绘制的流程。

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

WindowManagerGlobal#addView
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    ....
    ViewRootImpl root; //关键类ViewRootImpl
    View panelParentView = null;
    synchronized (mLock) {
        ....
        //创建ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        //设置LayoutParams
        view.setLayoutParams(wparams);
        //添加到DecorView的集合中
        mViews.add(view);
        //添加到ViewRootImpl集合中
        mRoots.add(root);
        //添加到LayoutParams集合中
        mParams.add(wparams);
        try {
            //关键代码,调用ViewRootImpl的setView
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
         ....
        }
    }
}

WindowManagerGlobal将DecorView交给了ViewRootImpl的setView方法。setView方法代码行数比较多,这里只看最关键的那一行requestLayout()。

ViewRootImpl#setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
            ....
            requestLayout(); //触发布局流程
           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();
               //添加View到WindowManagerService中来显示到屏幕上
               res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                     getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                     mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                     mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
}

        }
    }
}
ViewRootImpl#requestLayout
public void requestLayout() {
    //判断标识位 
    if (!mHandlingLayoutInLayoutRequest) {
        //检查线程
        checkThread();
        mLayoutRequested = true;
        //调度任务
        scheduleTraversals();
    }
}
ViewRootImpl#scheduleTraversals
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true; //设置标识位
        //通过主线程Handler发送屏障消息
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //通过Choreographer发送mTraversalRunnable
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
ViewRootImpl#TraversalRunnable
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();//执行doTraversal方法
    }
}
ViewRootImpl#doTraversal
void doTraversal() {
    if (mTraversalScheduled) {
        //设置标识位
        mTraversalScheduled = false;
        //移除屏障消息
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        //调用performTraversals方法进行measure、layout、draw流程
        performTraversals();
        ....
    }
}
ViewRootImpl#performTraversals
private void performTraversals() {
    //调用到performMeasure进行测量
    measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight);
    //进行布局
    performLayout(lp, mWidth, mHeight);
    //进行绘制
    performDraw();
}

ViewRootImpl通过requestLayout方法触发View的工作流程(测量、布局、绘制),然后向主线程MessageQueue发送一条屏障消息,进而确保View的工作流程优先被执行,这里涉及到了Handler的同步屏障机制。然后将任务交给Choreographer来处理执行调度,收到调度回调后移除屏障消息,随后进行测量、布局、绘制流程。经历了测量、布局、绘制流程就能看到界面上显示的内容了,具体到View层的onMeasure、onLayout、onDraw流程这里就不描述了,简单总结为jsonchao大神的这一张图。 image.png

2.4 Choreographer的应用

ViewRootImpl将Traversal任务交给了Choreographer来调度。Choreographer通过接收Vsync信号来控制View的绘制流程。Vsync信号和Choreographer的内容这里不做描述了,感兴趣的可以看一下这篇文章Android屏幕刷新机制—VSync、Choreographer 全面理解

注:以上分析基于API Level 28

3.总结

View的工作流程整体可归纳为几个步骤:
1)Activity通过attach绑定PhoneWindow
2)PhoneWindow绑定DecorView
3)WindowManager通过ViewRootImpl管理DecorView的工作流程
4)ViewRootImpl将DecorView的测量、布局、绘制流程的执行交给Choreographer来响应
5)Choreographer接收刷新信号来控制ViewRootImpl执行View的工作流程。