一、设计的类
Activity:与用户交互的基本界面。
Window: 最顶级的视图,所有View都要在add到Window里才能显示。Android中想要使用Window时,应该实例化它的实现类PhoneWindow。
PhoneWindow:这是一个特殊的Window类,每个Activity都有一个PhoneWindow。
WindowManager:顾名思义,这是Window的管理接口,它继承了ViewManager接口的ViewManager里面有addView(),removeView()和updateViewLayout()方法,用于管理View。
WindowManagerImpl:这是WindowManager的实现类,里面实现了addView()等方法。
DecorView:Activity最顶层的View,第一个添加进PhoneWindow的View,就是DecorView。DecorView继承FrameLayout,里面有个垂直线性布局,分成titleBar和content两个部分。在Activity中调用setContentView()方法设置的xml布局,就是添加到content中的。
ViewRootImpl:每个phoneWindow对应一个ViewRootImpl,它控制了View的绘制流程,measure()、layout()和draw的执行顺序,就是在ViewRootImpl中控制的。
二、从main开始
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
/**
* Integrates the framework with Dalvik's sampling profiler.
*/
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
//初始化Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看见,这里面基本都是做了一些系统级的初始化,然后启动looper,利用looper和handler来处理各种消息。这里,thread.attach()做了不少事情,我们进去看看。private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
//添加第一个Handler
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
……
} else {
// Don't set application object here -- if the system crashes,
// we can't display an alert, we just want to die die die.
android.ddm.DdmHandleAppName.setAppName("system_process",
UserHandle.myUserId());
……
}
// add dropbox logging to libcore
DropBox.setReporter(new DropBoxReporter());
ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mResourcesManager) {
// We need to apply this change to the resources
// immediately, because upon returning the view
// hierarchy will be informed about it.
//我们需要立即将这些改变应用于资源,因为在返回时将查看视图层次结构。
if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
mResourcesManager.getConfiguration().getLocales());
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(newConfig)) {
mPendingConfiguration = newConfig;
sendMessage(H.CONFIGURATION_CHANGED, newConfig);
}
}
}
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int level) {
}
});
}这里省略了一些不需要关注的代码,我们可以看见,这个方法里出现了ViewRootImpl,我们看看它都干了些什么。从方法名来看,先是将一个Runnable添加进Handler,然后新建了一个配置回调。我们进入进去看看,实际上是不是这样呢?
//ViewRootImpl类中,这是一个Ruunable列表
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
……//在ActivityThread类attach()中调用的方法
public static void addFirstDrawHandler(Runnable callback) {
synchronized (sFirstDrawHandlers) {
if (!sFirstDrawComplete) {
sFirstDrawHandlers.add(callback);
}
}
}
//这是ViewRootImpl中唯一用到sFirstDrawHandlers的地方
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!surface.isValid()) {
return;
}
if (DEBUG_FPS) {
trackFPS();
}
if (!sFirstDrawComplete) {
synchronized (sFirstDrawHandlers) {
sFirstDrawComplete = true;
final int count = sFirstDrawHandlers.size();
for (int i = 0; i< count; i++) {
//将Runnable推送到消息队列中
mHandler.post(sFirstDrawHandlers.get(i));
}
}
}可以看见,确实如猜测那般,将Runable添加到了消息队列中,在draw()中执行。那Runnable执行的是什么任务呢?
void ensureJitEnabled() {
if (!mJitEnabled) {
mJitEnabled = true;
dalvik.system.VMRuntime.getRuntime().startJitCompilation();
}
}这是调用系统dalvik虚拟机层的方法,启动Jit编译器,将改变的数据传递到底层,然后将View绘制到屏幕上。后面将新建一个ComponentCallbacks添加进ViewRootImpl中,我们进去看看:
//配置回调列表
static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList();
//attch()中调用的方法,将新建的ComponentCallbacks添加进来
public static void addConfigCallback(ComponentCallbacks callback) {
synchronized (sConfigCallbacks) {
sConfigCallbacks.add(callback);
}
}
//改变配置,调用回调的onConfigureationChangerd()方法
void updateConfiguration(Configuration config, boolean force) {
……
synchronized (sConfigCallbacks) {
for (int i=sConfigCallbacks.size()-1; i>=0; i--) {
sConfigCallbacks.get(i).onConfigurationChanged(config);
}
}
……
}
//在这个方法之中调用的updateConfiguration(),这个就是控制view绘制流程的主要方法
private void performTraversals() {
……
updateConfiguration(new Configuration(mPendingConfiguration), !mFirst);
……
}可见,在performTraversals()中,调用了回调的onConfigurationChanged()方法。而在attach()中添加的回调里,onConfigurationChanged()的作用是:“我们需要立即将这些改变应用于资源,因为在返回时将查看视图层次结构”。
由此可见,attch()方法中做的事情,就是为了使数据改变后,能及时将View绘制到屏幕上。
三、handMessager()中的处理
ActvityThread中,在main()里启动了looper,通过H的handleMessage()方法处理各种消息。代码如下:
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
……
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
……
case RESUME_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
SomeArgs args = (SomeArgs) msg.obj;
handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
args.argi3, "RESUME_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break; …… Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
} ……
}H是一个Handler,集中处理各种各样的消息。启动Activity的时候,就是在LAUNCH_ACTIVITY分支下,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,Activity的onCreate()和onStart()生命周期在其中执行
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
//Activity的onResume()方法在其中执行,View也是在其中绘制。
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();
}
}
}记得Activity的生命周期中,都说onCreate()后Activity创建成功,onStart()后就可以看见,onResume()之后可以交互。由此可知,View的绘制应该是在onStart()与onResume()之间,经进入performLaunchActivity()和handleResumeActivity()查看,发现View是在handleResumeActivity()中绘制的。而且,当Activity未被完全遮住的情况下,从其他界面返回Activity,也需要对界面重新绘制,可见将绘制VIew的流程放在handleResumeActivity()里面是合理的。
进入handleResumeActivity()看看:
final void handleResumeActivity(IBinder token,
……
if (r.window == null && !a.mFinished && willBeVisible) {
//获取PhoneWindow
r.window = r.activity.getWindow();
//获取DecorView
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//获取WindowManager
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 && !a.mWindowAdded) {
a.mWindowAdded = true;
//将DecorView添加进Window之中
wm.addView(decor, l);
}
……
}上面代码做的事情很简单,就是获取PhoneWindow,DecorView等的实例,并将DecorView添加进PhoneWindow之中。addView()的具体实现是在WindowManagerImpl类之中。
WindowManagerImp类中l:@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerGlobal类中:public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
……
root = new ViewRootImpl(view.getContext(), display);
//此处的view是DecorView
view.setLayoutParams(wparams);
//添加参数
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
……
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
……
}
ViewRootImpl类中:/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
……
mWindowAttributesChanged = true; //遍历调度,这里面执行绘制的方法
scheduleTraversals();
}
}void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//Posts a callback to run on the next frame.
//在下一帧执行回调,即执行 mTraversalRunnable
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
// mTraversalRunnable是一个TraversalRunnable内部类
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();TraversalRunnable类:
final class TraversalRunnable implements Runnable { @Override
public void run() {
doTraversal();
}
}
ViewRootImpl类中:
void doTraversal() { if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//执行遍历
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}在WindowManagerImpl中将DecorView添加进PhoneWindow的时候,调用了ViewRootImpl的setView方法,启动TraversalRunnable任务,由该任务的doTraversal方法来调用performTraversals()方法,遍历对View进行绘制。经过重重调用,终于找到了绘制View的重头戏了。
四、performTraversals()控制流程
View的绘制为什么会先执行onMeasure(),再执行onLayout()和onDraw(),这必然有一个地方的代码控制了其执行的先后顺序,这个控制的地方,就在performTraversals()之中。
performTraversals()方法比较长,让我们分段慢慢看吧:
private void performTraversals() {
// cache mView since it is used so much below...
//这里的mView就是前面setView()方法里设置的View,即DecorView,先将它缓存起来。
final View host = mView;
//然后是一堆初始化
……
Rect frame = mWinFrame;
//如果是第一次执行,则会初始化宽高、可见性等属性,如果不是第一次,则获取以前的宽高
if (mFirst) {
mFullRedrawNeeded = true;
mLayoutRequested = true;
final Configuration config = mContext.getResources().getConfiguration();
if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
……
// Set the layout direction if it has not been set before (inherit is the default)
if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
host.setLayoutDirection(config.getLayoutDirection());
}
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
//Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
……
//判断是否重新请求布局
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
……
//判断是否改变尺寸
windowShouldResize |= mActivityRelaunched;
//第一次绘制、改变了尺寸、重新布局等,走该分支
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
……
//没有被停止或者请求下一步的绘制
if (!mStopped || mReportNextDraw) {
/**
* Ensure that the touch mode for this window is set, and if it is changing,
* take the appropriate action.
*/
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
//焦点、宽高、内容元素、配置等改变,则需要重新测量
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
//测量方法,这里就是View的mesure流程,会调用OnMeasure方法
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
//如果设置了weight属性,则需要重新测量宽高。
if (lp.horizontalWeight > 0.0f) {
//宽高=设置的宽高数值 + 所占比重的剩余空间
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (measureAgain) {
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
//不是第一次,没有改变配置等情况,走该分支,可能需要移动Window,不做测量
} else {
// Not the first pass and no window/insets/visibility change but the window
// may have moved and we need check that and if so to update the left and right
// in the attach info. We translate only the window frame since on window move
// the window manager tells us only for the new frame but the insets are the
// same and we do not want to translate them more than once.
maybeHandleWindowMove(frame);
}
//重新布局
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
//布局流程,里面会调用View的OnLayout()方法
performLayout(lp, mWidth, mHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
//这里已经知道了viewd的尺寸和位置,然后加上parent的位置,才是真正的位置
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
}
……
//设置内部插入元素、设置焦点等
……
// Remember if we must report the next draw.
//第一次绘制时,要通知window
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
}
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
//绘制没被取消,并且没有新的需要绘制的画面过来,则绘制当前画面,否则重头开始改流程
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
//绘制,里面会调用View的OnDraw()方法
performDraw();
} else {
if (isViewVisible) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
}一个长长的performTraversals()方法从头过了一遍,它做的事情就是对View进行各种花样的判断,最终确定View的状态,然后对View按顺序进行Measure,Layout,和Draw。有时候可能该流程的每个步骤不是唯一的,比如设置了weight会进行两次Measure,有时要可能会出现意外情况导致不会Draw。
View的Measure、Layout、Draw三个流程已经是老生常谈了。下面我这个小学生也学着谈一谈吧。
五、Measure()测量
……(未完待续)