Android View的绘制机制
大家好!我 胡汉三 又回来啦!由于之前的一段时间公司的项目比较忙碌,再加上我又全身心的投入到了 PMP 考证队伍中,导致博客停更了好久。
不得不说考取 PMP证书,真的需要付出很大的精力;首先是因为心疼钱,因为考一次就得付出3900大洋,着实心疼啊!幸好付出和收获成了正比,顺利的拿到了 5A,结束了考试之旅。
好了废话说完了,我们接下来就来上干货啦!
View的绘制机制,究竟是怎么回事?
源码啊!源码,你能否一股脑的钻进我的脑子啊!这样我就不用翻你千百遍,一回头你还你我还我了=_=!!
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
...
//初始化WindowManageService 即初始化WMS
WindowManagerGlobal.initialize();
//创建Activity,并执行onCreate生命周期方法
Activity a = performLaunchActivity(r, customIntent);
if (a != null) { //证明Activity被正常启动
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) {
performPauseActivityIfNeeded(r, reason);
...
}
} else {
//若执行出错告诉ActivityManager,结束当前Activity
try {
ActivityManager.getService()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
小结
- 初始化
WindowManagerService
- 生成
Activity
,并执行Activity$onCreate(...)
- 若
Activity
被正常启动 则 执行Activity$onResume(...)
- 若
Activity
未能正常启动 则 结束当前Activity
即通过AMS
执行finishActivity(...)
分块
- [ 1 ]
ActivityThread $performLaunchActivity(...)
是整个Activity
创建的入口- [ 2 ] 在
ActivityThread $handleResumeActivity(...)
方法执行过程中传递给View
类中常量mParent的属性值类型
[ 1 ] ActivityThread $performLaunchActivity(...)
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
//通过类加载的方式创建Activity
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
...
}
try {
...
// [1.1] 通过Instrumentation调取Activity的onCretae
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
...
}
return activity;
}
[ 1.1 ] Instrumentation$callActivityOnCreate
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
//至此进入到`Activity$onCreate(...)`
activity.performCreate(icicle);
postPerformCreate(activity);
}
至此进入到Activity$onCreate(...)
并在onCreate(...)
中调用setContentView(int layoutResID)
进行View的绘制,至此我们才开始正式流程:
[ 1.2 ] Activity$setContentView(int layoutResID)
public void setContentView(@LayoutRes int layoutResID) {
//[1.3] 获取Window对象,调取其setContentView设置视图
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
[ 1.3 ] PhoneWindow$setContentView(int layoutResID)
PhoneWindow.java 由于
Window
是一个抽象类,而PhoneWindow
是它唯一儿子,所谓 父债子还 嘛 !所以找它没错的
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//安装DecorView
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
} else {
//解析Layout XML文件
mLayoutInflater.inflate(layoutResID, mContentParent);
}
//请求视图更新
mContentParent.requestApplyInsets();
...
}
小结
- 若
mContentParent
为Null
,表示当前视图无盛放的容器,即需要安装顶级视图DecorView
- 解析XML布局文件
- 请求视图更新操作
[ 1.4 ] View$requestApplyInsets()
/**
* The parent this view is attached to.
* {@hide}
*
* @see #getParent()
*/
protected ViewParent mParent;
//为mParent变量赋值
void assignParent(ViewParent parent) {
if (mParent == null) {
mParent = parent;
} else if (parent == null) {
mParent = null;
} else {
throw new RuntimeException("view " + this + " being added, but"
+ " it already has a parent");
}
}
/*
* 这是重点:
* 判定mParent是否为Null
* 不为Null时,调取mParent的requestFitSystemWindows()
*/
@Deprecated
public void requestFitSystemWindows() {
if (mParent != null) {
mParent.requestFitSystemWindows();
}
}
/**
* 在PhoneWindow中调取的此方法,进行视图更新
* 此方法无任何操作,而是直接调取了requestFitSystemWindows()
*/
public void requestApplyInsets() {
requestFitSystemWindows();
}
小结
- 在
PhoneWindow
中调取了View$requestApplyInsets()
,由上述代码可知在此方法中没有做任何操作,而是直接调取了View$requestFitSystemWindows()
- 而在
View$requestFitSystemWindows()
方法中它判定了mParent
在不为Null
的情况下,调取了mParent$requestFitSystemWindows()
- 我们发现
assignParent(ViewParent parent)
是为mParent
赋值的地方,而mParent
的类型为ViewParent
,但可惜的是ViewParent
是一个接口,并没有实现代码
public interface ViewParent {}
那这事就郁闷了,无法确定mParent
真实的类型,那么就无法找到 ViewParent$requestFitSystemWindows()
真实实现的地方。那么我们就得再次回归源码了,继续 Read The Fucking Source Code . . . .
[ 2 ] ActivityThread$handleResumeActivity(...)
Activity
被正常创建粗来了,也走完了Activity
的onCreate
生命周期,那么接下来就该走onResume
生命周期了,我来看看在这里我们能不能找到想要的答案~
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
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;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//重点来啦:这里将DecorView添加到WindowManager中
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
...
} else {
...
}
}
[ 2.1 ]WinodwManagerImpl$addView(...)
WindowManager
是一个接口,而他的实现类是WindowManagerImpl
,所以我们就 单刀直入啦!
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
//不做过多论述,调取WindowManagerGlobal$addView(...)
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
[ 2.2 ] WindowManagerGlobal$addView(...)
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
....
//创建ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//重点:
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
...
}
}
}
[ 2.3 ] ViewRootImpl$setView(...)
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
//我们要的就是它,原来是在这里为 View中mParent赋值的,
//为什么是 this 呢? 难道。。。
view.assignParent(this);
...
}
}
}
看看的类描述
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {...}
原来如此,
ViewRootImpl
原来也是ViewParent
接口的实现类之一啊~
那么至此我们就搞清楚View
中mParent
的具体类型了,就是ViewRootImpl
类型!
是时候解答疑惑了,让我回过头看看[ 1.3 ]中的代码
/*
* 这是重点:
* 判定mParent是否为Null
* 不为Null时,调取mParent的requestFitSystemWindows()
*/
@Deprecated
public void requestFitSystemWindows() {
if (mParent != null) {
mParent.requestFitSystemWindows();
}
}
我们已经确认当前的mParent
是类型了,那么我们直接去ViewRootImpl
中寻找这个方法
[ 1.4 ] ViewRootImpl$requestFitSystemWindows()
@Override
public void requestFitSystemWindows() {
//检查线程
checkThread();
mApplyInsetsRequested = true;
//开始遍历view
scheduleTraversals();
}
void scheduleTraversals() {
//当绘制任务执行期间,不允许有其他绘制任务执行
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//向Looper的消息队列发布一个同步障碍
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//重点来了: post一个消息,执行mTraversalRunnable中任务
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
[ 1.5 ] mTraversalRunnable
//它实现了Runnable接口
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;
}
}
}
当我们看到performTraversals()
,我们看到了黎明的曙光。。。没错就是它~~
[ 1.6 ] performTraversals()
private void performTraversals() {
...
//测量
performMeasure();
...
//布局
performLayout();
...
//绘制
perfromDraw
...
}
执行完此方法后,我们就完成了对View
的 测量 ==> 布局 ==> 绘制 ,也就完成了我们的整个流程的剖析!!
........................................................................................................................................................................................................................................................................................................................................................................................................
问题
- 经过上述文章论述,我们知道了
DecorView.mParent
是ViewRootImpl类型
,那么DecorView
儿子的mParent
是谁呢?它儿子的儿子的mParent
又是谁呢?总不能都是ViewRootImpl类型
吧!!
答案肯定是否定的。
mParent
的类型永远同当前View
的父级一个类型。
来源码走一圈,让我们见真知!!
public void addView(View child, int index, LayoutParams params) {
...
//设置新LayoutParams时,addViewInner()将调用child.requestLayout()
//因此,我们之前调用requestLayout()在我们自己,以便孩子的请求
requestLayout();
invalidate(true);
//添加内部View
addViewInner(child, index, params, false);
}
private void addViewInner(View child, int index, LayoutParams params,boolean preventRequestLayout) {
...
// tell our children
if (preventRequestLayout) {
child.assignParent(this);
} else {
child.mParent = this;
}
...
}
通过以上的分析,我们表述已经很简明了,
父View
将自己赋予了子View
的mParent
那么我可以清晰的总结一下:
DecorView
的mParent
的类型是ViewRootImpl
其他View
的mParent
的类型是Ta的父View
,即 Ta的老子!! 哈哈