一、设计的类
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 libcoreEventLogger.setReporter(new EventLoggingReporter());// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);Process.setArgV0("<pre-initialized>");//初始化LooperLooper.prepareMainLooper();ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(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) {//添加第一个HandlerViewRootImpl.addFirstDrawHandler(new Runnable() {@Overridepublic 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 libcoreDropBox.setReporter(new DropBoxReporter());ViewRootImpl.addConfigCallback(new ComponentCallbacks2() { @Overridepublic 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);}}}}@Overridepublic void onLowMemory() {}@Overridepublic 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 activityWindowManagerGlobal.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) {//获取PhoneWindowr.window = r.activity.getWindow();//获取DecorViewView decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE);//获取WindowManagerViewManager 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是DecorViewview.setLayoutParams(wparams);//添加参数mViews.add(view);mRoots.add(root);mParams.add(wparams);……// do this last because it fires off messages to start doing thingsroot.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.//在下一帧执行回调,即执行 mTraversalRunnablemChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) {scheduleConsumeBatchedInput();}notifyRendererOfFramePending();pokeDrawLockIfNeeded();}
}
// mTraversalRunnable是一个TraversalRunnable内部类
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();TraversalRunnable类:
final class TraversalRunnable implements Runnable { @Overridepublic 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 beint 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 managertry {mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);} catch (RemoteException e) {}}}} ……//设置内部插入元素、设置焦点等……// Remember if we must report the next draw.//第一次绘制时,要通知windowif ((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 againscheduleTraversals();} 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()测量
……(未完待续)