转载请注明出处:http://blog.csdn.net/crazy1235/article/details/71568745
绘制流程从哪里开始的?
在 Activity具体是怎么创建的?又是怎么显示出来的? 这篇博文中,讲解了从startActivity() 调用之后,到一个activity的显示过程!
activity界面是在onResume()函数回调之后才显示的!
关键函数就在 ActivityThread 中的 handleResumeActivity() !
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
// ...
// 这里回调了onResume()生命周期函数,返回ActivityClientRecord对象
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// 如果当前activity的window对象没有被添加到windowManager中,并且没有finish自己,也没有启动其他的activity,那么就把window对象添加到wm中!
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
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();
}
}
// 将decorview添加到wm中! (关键代码)
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
// 如果window已经被添加到wm中,但是在resume过程中又启动了别的activity,则此时不让window对象显示!
} else if (!willBeVisible) {
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r, false /* force */);
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
+ r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
r.newConfig = null;
}
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
+ isForward);
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
// mVisibleFromClient 默认为true
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l); // update
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
// 让界面显示出来!实际上就是调用了 decorview.setVisiable(VISIABLE)
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
// ...
} else {
// activity在resume过程中出现异常时,finish这个界面!
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
从上面代码块可以看出,先调用了performResumeActivity(),在这个函数内部毁掉了onResume(),具体分析请查看 onResume()流程分析 。
接着才是将decorview视图添加到windowManager中!
wm.addView(decor, l);
willBeVisiable 标识是否要显示当前的activity
wm是 ViewManager 类型对象!
但是ViewManager是个接口!
ViewManager
先来看看wm这个对象的真正实现者!
ViewManager wm = a.getWindowManager();
vm的创建时通过 getWindowManager() 来的。而a是Activity对象!
在Activity.java中
public WindowManager getWindowManager() {
return mWindowManager;
}
直接返回的是mWindowManager,那么这个对象在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) {
attachBaseContext(context);
// ...
mWindow = new PhoneWindow(this, window);
// ...
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();
// ...
}
attach() 这个函数的调用其实在 ActivityThread类中的 performLaunchActivity() 就已经完成了! 具体流程可以参考:ActivityThread # handleLaunchActivity
在这个attach() 函数中,通过调用PhoneWindow的 getWindowManager() 进行对 mWindowManager 赋值 ~!
再去PhoneWindow.java类中去找一下
public WindowManager getWindowManager() {
return mWindowManager;
}
也是直接获取的对象!但是这个对象哪里来的呢????
如果仔细看attach() 方法,就会发现 mWindow.setWindowManager() 这一行代码!
对应到 PhoneWindow 中
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
// 初始化mWindowManager
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
又跑到了 WindowManagerImpl 类中:
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
OK,至此发现最终是new了一个 WindowManagerImpl 对象!
所以 wm.addView(decor, l); 最终就是调用的 windowManagerImpl.addView(decor, l);
addView()
WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
其实关于window的操作都是同构WindowManagerGlobal来完成的!
具体的分析请参考 Android对Window对象的管理机制分析
直接来看 WindowManagerGloabl.addView()
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// ...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// ...
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) {
// ...
}
}
在这个函数内部创建了 ViewRootImpl 对象!它是连接WindowManager与DecorView的桥梁!
最后调用了 ViewRootImpl.setView() 函数!
来看这个方法体
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
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();
// ...
}
}
}
requestLayout()
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
// ...
}
}
mTraversalRunnable 是一个Runnable对象。mChoreographer.postCallback(Runnable)看起来很像Handler的处理机制对吧!其实Choreographer 内部就是通过Handler来处理的!
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
// ...
performTraversals();
// ...
}
}
终于找到了 performTraversals() 这个函数了!
其实View的绘制流程就是从这个函数开始的!
MeasureSpec
在开始真正的分析绘制流程之前,先来看看 MeasureSpec 这个类 – 测量规格!
MeasureSpec 参与了View的measure过程。
MeasureSpec 是View的一个静态内部类!
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY = 1 << MODE_SHIFT;
public static final int AT_MOST = 2 << MODE_SHIFT;
@IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
@Retention(RetentionPolicy.SOURCE)
public @interface MeasureSpecMode {}
public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size, @MeasureSpecMode int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
@MeasureSpecMode
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
MeasureSpec代表一个32位的int值,高2位表示SpecMode – 测量模式, 低30位表示SpecSize – 测量大小 !
SpecMode与SpecSize组装成MeasureSpec,和,MeasureSpec提取出SpecMode和SpecSize都是通过位运算,非运算,反运算的组合运算得出的!
SpecMode
SpecMode有三种:
UNSPECIFIED
未详细说明的。父容器不对View有限制,一般用于系统内部。自定义控件时基本用不到!
EXACTLY
精确模式。父容器已经确定了子View的大小,所以子View的大小就是SpecSize对应的值。对应于LayoutParams的match_parent和具体的数值这两种方式!
AT_MOST
最多模式。由父容器指定子View的最大宽高,子View的宽高不能超过SpecSize这个值!对应于LayoutParams的wrap_content 方式!
对于顶级decorView来说,MeasureSpec是由窗口尺寸和自身的LayoutParams共同决定!
对于其他普通View来说,MeasureSpec是由父容器的MeasureSpec和自身的LayoutParams共同决定的!
在 performTraversals() 函数里面有这样一段代码:
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
前两行代码就是构建根布局decorView的宽高测量规格。
lp实际上就是我们wm.addView(decor, l) 传进来的WindowManager.LayoutParams 对象!
getRootMeasureSpec()
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
从上面代码可看出DecorView的MeasureSpec的构建过程是根据LayoutParams来进行区分的!
ViewGroup.LayoutParams.MATCH_PARENT
精确模式(EXACTLY),大小就是窗口的大小
ViewGroup.LayoutParams.WRAP_CONTENT
最大模式(AT_MOST),最大不超过窗口的大小
具体数值
精确模式(EXACTLY),大小为rootDimension,也就是WindowManager.LayoutParams中的大小
getChildMeasureSpec()
对于普通的View来说呢?
看下ViewGroup中的 getChildMeasureSpec() 函数,获得子View的测量规格!
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec); // mode
int specSize = MeasureSpec.getSize(spec); // size
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) { // 判断当前ViewGroup的mode
case MeasureSpec.EXACTLY: // exactly
if (childDimension >= 0) { // 具体的数值
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) { // 占满ViewGroup的空间
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) { // 最多不能超过ViewGoup的大小
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// ViewGoup是AT_MOST模式的时候
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// 具体数值
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 子View的大小就是ViewGroup的大小
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 子View的大小最多是ViewGroup的大小
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// ViewGroup大小不确定
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// 具体数值
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 填满ViewGoup的大小
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 最多不超过ViewGroup的大小
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
// 构造测量模式~
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
总结如下:
普通View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定!
当子View的宽/高采用match_parent时,如果父容器的SpecMode是EXACTLY,则子View的SpecMode也是EXACTLY且大小是父容器的剩余空间(除去padding,margining);如果父容器的SpecMode是AT_MOST模式,则子View的SpecMode也是AT_MOST模式且大小不超过父容器的剩余空间大小。
当子View的宽/高采用wrap_content时,不管父容器的SpecMode是EXACTLY还是AT_MOST,子View的SpecMode都是AT_MOST并且大小不能超过父容器的剩余空间!
当子View的宽/高设定了具体的数值,不管父容器的SpecMOde是什么模式,子View的SpecMode都是EXACTLY并且大小就是LayoutParams中设置的大小!
下面就具体来分析下测量 - 布局 - 绘制这三个流程!