Android高级UI系列(2)-DecorView

上一章我们讲解了我们的视图是如何显示到界面上的,这一章我们来讲讲View是如何添加到window上的,换句话说就是我们的DecorView是如何添加到Window上的。

同样,我们先带着疑问:
我们的DecorView是怎么添加到Window上的呢?

这个问题我们要先从Activity的启动开始讲起。我们这里从Java层面开始,不考虑其他层面。

Activity的启动时通过ActivityThread来进行发起的。它会调用自己的
handleLaunchActivity()方法,

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }

        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);

        if (localLOGV) Slog.v(
            TAG, "Handling launch of " + r);

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

            if (!r.activity.mFinished && r.startsNotResumed) {
                // The activity manager actually wants this one to start out paused, because it
                // needs to be visible but isn't in the foreground. We accomplish this by going
                // through the normal startup (because activities expect to go through onResume()
                // the first time they run, before their window is displayed), and then pausing it.
                // However, in this case we do -not- need to do the full pause cycle (of freezing
                // and such) because the activity manager assumes it can just retain the current
                // state it has.
                performPauseActivityIfNeeded(r, reason);

                // We need to keep around the original state, in case we need to be created again.
                // But we only do this for pre-Honeycomb apps, which always save their state when
                // pausing, so we can not have them save their state when restarting from a paused
                // state. For HC and later, we want to (and can) let the state be saved as the
                // normal part of stopping the activity.
                if (r.isPreHoneycomb()) {
                    r.state = oldState;
                }
            }
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }复制代码

在这个方法中它首先会调用performLaunchActivity()。好, 我们看这个方法:

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         //省略部分代码
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
                    ...
                    } catch (Exception e) {
            ...
        }

        try {
            ...
                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);

               ...
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
                if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onPostCreate()");
                    }
                }
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }复制代码

首先,它会通过反射获取Activity,然后他会调用我们Activity的attach()方法,而我们的PhoneWindow是在Activity的attach()方法中初始化的,大家可以自己查看源码。因此,初始化的过程都是在Activity中发起调用的。初始化完成之后, 它会调用callActivityOnCreate(),这里就是会调用activity的,也就是说我们Activity的声明周期,执行过程,都是在我们ActivityThread中一次执行的,大家可以自己往后看,生命周期次序都有。

看完了performLaunchActivity ()方法,我们回到handleLaunchActivity (),接着往下看,它会执行handleResumeActivity(),我们看方法名,就可以猜到它肯定会去调用resume方法吧。

它的流程大概就是handleResumeActivity()->performResumeActivity()->activity.performResume()

  final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
            ...
            //第一步            
        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);

        ...
        //第二步
          r.window = r.activity.getWindow();
          View decor = r.window.getDecorView();
          ViewManager wm = a.getWindowManager();
          WindowManager.LayoutParams l = r.window.getAttributes();
          //第三步
          wm.addView(decor, l);  
          ...
            }复制代码

好,我们继续看handleResumeActivity (),他会去拿window,decor,还有WindowManager,然后把我们Decor添加到我们WindowManager里面去。那这个windowManager是什么呢,我们点进去一看是个接口,那我们要找它的实现类,它是实现类是WindowManagerImpl。所以我们要看它的addView()方法。

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

@Override
    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;
            ...
            root = new ViewRootImpl(view.getContext(), display);

             mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            ...
             root.setView(view, wparams, panelParentView);
            }复制代码

它首先创建一个ViewRootImpl,然后把我们的Decor,viewrootImp,和一些参数都保存到了这个Global类中,也就是说,我们的WindowManagerGlobal是用来管理ViewRoot和DecorView的。接下来他会把我们的DecorView设置给ViewRoot,这就是这篇文章最关键的一个方法了。我们点进去看ViewRoot的setView()方法:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//将我们的DecorView赋值给mView,因此下面的mView就是DecorView
              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();
         ...
         //发起了跨进程消息
          res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
          ...
          //给DecorView设置parent,我们view调用requsetLayout的时候,其实调用的都是viewrootImp的requestLayout
          view.assignParent(this);
}复制代码

首先会将我们的DecorView赋值给mView,因此下面的mView就是DecorView,这个mView这里没用到,下面的UI遍历会用到。我们先来看viewRoot的requestLayout()中的scheduleTraversals();

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


  void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }复制代码

这里开启了一个runnable,我们点进去看

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

而doTraversal()中有一个方法performTraversals(),这个相信只要是看过UI绘制的博客的人应该都很熟悉这个方法吧,这个方法就是真正执行整个UI绘制的遍历过程。

private void performTraversals() {
        ...
// cache mView since it is used so much below...
        final View host = mView;
        ...
 // Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ...
         performLayout(lp, mWidth, mHeight);
        ...
         performDraw();

}复制代码

这篇文章不仅仅讲了DecorView是如何添加到Window的,还梳理了一遍View的绘制发起点,虽然篇幅较短,但也费了些心思。

下一篇章我们真正开始DecorView的绘制流程讲解。下篇文章计划将结合《Android开发艺术探索》和其他相关博客,并加上个人的理解,总结出一篇希望能帮助大家理解UI绘制流程的文章,尽请期待!

由于个人水平有限,文章中可能会出现错误,如果你觉得哪一部分有错误,或者发现了错别字等内容,欢迎在评论区告诉我。

望共勉。

参考

腾讯课堂

本文为猫舍原创作品,转载请注明原文出处: imlerry.com/2017/05/11/…

转载于:https://juejin.im/post/591424e3128fe1005ca1f436

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值