上一章我们讲解了我们的视图是如何显示到界面上的,这一章我们来讲讲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绘制流程的文章,尽请期待!
由于个人水平有限,文章中可能会出现错误,如果你觉得哪一部分有错误,或者发现了错别字等内容,欢迎在评论区告诉我。
望共勉。
参考
腾讯课堂
本文为猫舍原创作品,转载请注明原文出处: http://imlerry.com/2017/05/11/03-Android%E9%AB%98%E7%BA%A7UI%E7%B3%BB%E5%88%97%EF%BC%882%EF%BC%89-DecorView/。