是在onResume之后
- public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
- String reason) {
- ......
- // TODO Push resumeArgs into the activity for consideration
- final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); // ⭐
- ......
-
- final Activity a = r.activity;
-
- ......
- 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;
- l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
- l.softInputMode |= forwardBit;
- if (r.mPreserveWindow) {
- a.mWindowAdded = true;
- r.mPreserveWindow = false;
- // Normally the ViewRoot sets up callbacks with the Activity
- // in addView->ViewRootImpl#setView. If we are instead reusing
- // the decor view we have to notify the view root that the
- // callbacks may have changed.
- ViewRootImpl impl = decor.getViewRootImpl();
- if (impl != null) {
- impl.notifyChildRebuilt();
- }
- }
- if (a.mVisibleFromClient) {
- if (!a.mWindowAdded) {
- a.mWindowAdded = true;
- 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);
- }
- }
-
- // If the window has already been added, but during resume
- // we started another activity, then don't yet make the
- // window visible.
- } else if (!willBeVisible) {
- if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
- r.hideForNow = true;
- }
- }
从上面贴出的代码,我们看到handleResumeActivity会调用performResumeActivity这个方法:
- public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
- String reason) {
- ......
- r.activity.performResume(r.startsNotResumed, reason);
- ......
- return r;
- }
然后,调用Activity的PerformResume方法:
- final void performResume(boolean followedByPause, String reason) {
- ......
- // mResumed is set by the instrumentation
- mInstrumentation.callActivityOnResume(this);
- .....
- }
PerformResume方法调用mInstrumentation.callActivityOnResume(this)方法
- public void callActivityOnResume(Activity activity) {
- activity.mResumed = true;
- activity.onResume();
-
- if (mActivityMonitors != null) {
- synchronized (mSync) {
- final int N = mActivityMonitors.size();
- for (int i=0; i
- final ActivityMonitor am = mActivityMonitors.get(i);
- am.match(activity, activity, activity.getIntent());
- }
- }
- }
- }
callActivityOnResume方法中调用了activity的onResume方法,到此handleResumeActivity方法是如何调用到Activity的omResume方法说清楚了,上面提到了View是在onResume方法后绘制在手机上的,那具体是在哪里,我们继续分析handleResumeActivity的第二个重点代码
4.handleResumeActivity的第二个重点代码
- if (r.window == null && !a.mFinished && willBeVisible) {
- r.window = r.activity.getWindow();
- View decor = r.window.getDecorView();//获取setContentView方法创建的DecorVieww
- decor.setVisibility(View.INVISIBLE);
- ViewManager wm = a.getWindowManager(); //a是Activity,通过调用getWindoManager方
- //法获取WindowManager,ViewManager是一个接口,WindowManager实现了ViewManager这个接口
- WindowManager.LayoutParams l = r.window.getAttributes(); // 获取window的LayoutParams
- a.mDecor = decor;
- ......
- if (a.mVisibleFromClient) {
- if (!a.mWindowAdded) { // 一个Activity中只会添加一次⭐
- a.mWindowAdded = true;
- 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);
- }
- }
-
- // If the window has already been added, but during resume
- // we started another activity, then don't yet make the
- // window visible.
- }
可以看到先通过Activity的getWindowManager方法获取了WindowManager,WindowManager是一个接口它的实现类是WindowManagerImpl,所以wm.addView(decor,l)实际上是执行的WindowManagerImpl的addView方法,通过这个方法的名字也可以猜到是把DecorView和PhoneWindow联系起来的,我们也知道了是在handelResumeActivity方法中把window和View联系起来的。
在上篇setContentView方法详解一文中我们知道Window是在Activity的attach方法中new出来的,
同样在Activity的attach方法中对WindowManager做的处理:
- 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, IBinder assistToken) {
-
-
- 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());
- }
- mWindowManager = mWindow.getWindowManager();
-
-
- }
-
-
- public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
- boolean hardwareAccelerated) {
- mAppToken = appToken;
- mAppName = appName;
- mHardwareAccelerated = hardwareAccelerated;
- if (wm == null) {
- wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- }
- mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
- }
-
- public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
- return new WindowManagerImpl(mContext, parentWindow); //可以看到WindowManager的实现类是WindowManagerImpl
- }
5.分析WindowManagerImpl的addView方法
- public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
- applyDefaultToken(params);
- mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
- mContext.getUserId());
- }
WindowManagerImpl中的对Window的操作,都是通过WindowManagerGlobal来完成的,这个WindowManagerGlobal是在创建WindowManagerImpl对象的时候创建:
- public final class WindowManagerImpl implements WindowManager {
-
- private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
-
- }
所以WindowManagerImpl的addView方法实际是调用的WindowManagerGlobal的addView的方法:
- @UnsupportedAppUsage
- private final ArrayList
mViews = new ArrayList(); - @UnsupportedAppUsage
- private final ArrayList
mRoots = new ArrayList(); - @UnsupportedAppUsage
- private final ArrayList
mParams = - new ArrayList
(); -
- public void addView(View view, ViewGroup.LayoutParams params,
- Display display, Window parentWindow, int userId) {
- ......
-
- final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
- ......
-
- ViewRootImpl root;
-
- 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 {
- root.setView(view, wparams, panelParentView, userId); //⭐
- } catch (RuntimeException e) {
- // BadTokenException or InvalidDisplayException, clean up.
- if (index >= 0) {
- removeViewLocked(index, true);
- }
- throw e;
- }
- }
- }
一个Activity中wm.addView方法只会执行一次,ViewRootImpl是在addView方法中new出来的,所以一个Activity中只会有一个ViewRootImpl,在WindowManagerGlobal中维护着三个ArrayList集合,mViews是用来存储DecorView,mRoots是用来存储ViewRootImpl,mParams是用来存储window的LayoutParams,WindowManagerGlobal是一个单例,它是一个进程所有的窗口的管理类,WindowManagerGlobal通过ViewRootImpl是和WMS交互
所以我们知道了,一个Activity对应一个Window(在Activity的attach方法中创建的PhoneWindo),一个Window对应一个WindowManagerImpl对象(一个window对应一个windowManager的实例,windowManager的实现类就是WindowManagerImpl),一个WindowManagerImpl对应一个ViewRootImpl,ViewRootImpl是和WMS交互来管理自己的窗口,WindowManagerGlobal是一个单例,它是一个进程所有的窗口的管理类,WindowManagerGlobal通过ViewRootImpl是和WMS交互的,View的绘制、事件分发都由它来控制
6.分析ViewRootImpl的构造方法
- public ViewRootImpl(Context context, Display display, IWindowSession session,
- boolean useSfChoreographer) {
- mContext = context;
- mWindowSession = session; // 从wMS中创建的一个session对象,可以当成WMS的一个代理
- ......
- mThread = Thread.currentThread(); // ⭐这个mThread后面要用到
- ......
- mDirty = new Rect();
- ......
-
- mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
- context);// 用来保存当前窗口信息
- ......
- mChoreographer = useSfChoreographer // 编舞者
- ? Choreographer.getSfInstance() : Choreographer.getInstance();
- ......
- }
7.分析ViewRootImpl的setView方法
- public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
- int userId) {
- synchronized (this) {
- if (mView == null) {
- ......
- requestLayout(); // ⭐真正开始绘制流程,单独分析
- ......
- res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
- getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
- mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
- mAttachInfo.mDisplayCutout, inputChannel,
- mTempInsets, mTempControls); // 将窗口添加到WMS上面
- ......
- mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
- Looper.myLooper()); // 接收事件
- ......
- view.assignParent(this); // view是DecorView,这个是把DecorView的父亲设置成当前的ViewRootImpl,
- //DecorView已经是View树的顶层,现在在上面加了一层,ViewRootImpl变成了View树的顶层了
- }
-
- }
- }
总结:到此其实是View树的根DecorView是如何添加到Window上的流程,首先我们知道了是在ActivityThread的handleResumeActivity方法中且在Activity的onResume方法后添加的,从本质上来说DecorView其实并没有真正的像View那样Draw在Window上,而是通过WindowManagerImpl最终调用WindowManagerGlobal的addView方法,在这个方法中创建了一个VIewRootImpl作为DecorView的父亲,并且来管理DecorVIew(也就包括View树)的绘制、事件处理等,所以phoneWIndow通过WindowManager来管理ViewRootImpl,ViewRootImpl和WMS打交道来管理View和事件的处理。
8.分析ViewRootImpl的requestLayout方法,真正的View绘制流程
- public void requestLayout() {
- if (!mHandlingLayoutInLayoutRequest) {
- checkThread(); // ⭐
- mLayoutRequested = true;
- scheduleTraversals(); // ⭐
- }
- }
先分析checkThread:
- void checkThread() {
- if (mThread != Thread.currentThread()) {
- throw new CalledFromWrongThreadException(
- "Only the original thread that created a view hierarchy can touch its views.");
- }
- }
mThread上面提到了,在ViewRootImpl的构造方法中获取的当前线程,ViewRootImpl默认是在主线程创建的,所以我们对UI的操作都必须在主线程,这样能够保证操作UI时的线程安全,这个才是为什么一定要在主线程绘制UI的本质,换句话说,如果在子线程创建的ViewRootImpl那对UI的操作必须在这个子线程进行,总之ViewRootImpl在哪个线程创建就只能在哪个线程更新UI
9.分析ViewRootImpl的ScheduleTraversals方法
- void scheduleTraversals() {
- if (!mTraversalScheduled) {
- mTraversalScheduled = true;
- mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//handler机制的异步消息屏障开启
- mChoreographer.postCallback(
- Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); notifyRendererOfFramePending();
- pokeDrawLockIfNeeded();
- }
- }
mTraversalRunable是要执行的任务:
- final class TraversalRunnable implements Runnable {
- @Override
- public void run() {
- doTraversal();
- }
- }
- final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
-
-
- void doTraversal() {
- if (mTraversalScheduled) {
- mTraversalScheduled = false;
- mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);//移除异步消息屏障
-
- if (mProfile) {
- Debug.startMethodTracing("ViewAncestor");
- }
-
- performTraversals(); //开始绘制,⭐下面重点分析
-
- if (mProfile) {
- Debug.stopMethodTracing();
- mProfile = false;
- }
- }
- }
10.分析ViewRootImpl中的performTraversals
- private void performTraversals() {
- ......
- windowSizeMayChange |= measureHierarchy(host, lp,
- mView.getContext().getResources(),
- desiredWindowWidth, desiredWindowHeight);
- ......
- performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
- ......
- performLayout(lp, mWidth, mHeight);
- ......
- performDraw();
- }