前一段时间写过两篇关于View的文章 Activity中的Window的setContentView 和 遇见LayoutInflater&Factory 。分析了Activity设置页面布局到页面View元素进行布局到底经历了一个怎么样的过程?
为什么接下来继续写这篇文章呢?是因为我在掘金上看到一篇子线程更新View的文章之后,发现自己对View还不是很了,以这个问题为方向看了View相关的源码。发现网络上有些文章对于ViewRootImpl的分析还是有些问题或者疑惑的,所以自己整理过的知识点分享给大家,希望能对大家有帮助。(源码cm12.1
)
最开始学习View的时候最先分析的是它的布局(LinearLayout、FrameLayout、TableLayout、RelativeLayout、AbsoluteLayout),然后是它的三大方法(measure、layout、draw)。
ViewGroup.java
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec)
protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec)
protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed)
public void draw(Canvas canvas)
protected void onDraw(Canvas canvas)
protected void dispatchDraw(Canvas canvas)(View,ViewGroup)
protected boolean drawChild(Canvas canvas, View child, long drawingTime) (ViewGroup)
ViewTree.jpg
View的解析和生成之前在下边的这两篇文章中已经讲述
View如何在页面进行展示的,View树是如何生成的。 Activity中的Window的setContentView View对象的生成,属性值的初始化。 遇见LayoutInflater&Factory
在这两篇文章中用到了一些Android中相关的类:
The top of a view hierarchy, implementing the needed protocol between View and the WindowManager. This is for the most part an internal implementation detail of {@link WindowManagerGlobal}.
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代码省略**********/
}
ViewRootImpl是View中的最高层级,属于所有View的根(但ViewRootImpl不是View,只是实现了ViewParent接口
),实现了View和WindowManager之间的通信协议,实现的具体细节在WindowManagerGlobal这个类当中。
ViewManager.png
public interface ViewManager{
//添加View
public void addView(View view, ViewGroup.LayoutParams params);
//更新View
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
//移除View
public void removeView(View view);
}
上面提到了WindowManager
对View
的添加、更新、删除操作,那么联系这两者之间的Window
呢?
Activity中的Window的setContentView 阅读这篇文章我们知道Activity中有Window对象,一个Window对象对应着一个View(
DecorView
),ViewRootImpl
就是对这个View进行操作的。
我们知道界面所有的元素都是有View构成的,界面上的每一个像素点也都是由View绘制的。Window只是一个抽象的概念,把界面抽象为一个窗口对象,也可以抽象为一个View。
在了解ViewRootImpl与其他类的关系之前,我们先看一副图和一段代码:
ViewRootImpl.jpg
public final class WindowManagerGlobal {
/*******部分代码省略**********/
//所有Window对象中的View
private final ArrayList<View> mViews = new ArrayList<View>();
//所有Window对象中的View所对应的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//所有Window对象中的View所对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
/*******部分代码省略**********/
}
WindowManagerGlobal
在其内部存储着ViewRootImpl
和View
实例的映射关系(顺序存储)。WindowManager
继承与ViewManger
,从ViewManager
这个类名来看就是用来对View
类进行管理的,从ViewManager
接口中的添加、更新、删除View的方法也可以看出来WindowManager
对View
的管理。public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
/*******部分代码省略**********/
}
WindowManagerImpl
为WindowManager
的实现类。WindowManagerImpl
内部方法实现都是由代理类WindowManagerGlobal
完成,而WindowManagerGlobal
是一个单例,也就是一个进程中只有一个WindowManagerGlobal
对象服务于所有页面的View。在Activity的onResume之后,当前Activity的Window对象中的View会被添加在WindowManager中。
public final class ActivityThread {
/*******部分代码省略**********/
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
/*******部分代码省略**********/
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
/*******部分代码省略**********/
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;
//window的类型:一个应用窗口类型(所有的应用窗口类型都展现在最顶部)。
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//将decor添加在WindowManager中
wm.addView(decor, l);
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
/*******部分代码省略**********/
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
}
}
创建Window所对应的ViewRootImpl,并将Window所对应的View、ViewRootImpl、LayoutParams顺序添加在WindowManager中。
public final class WindowManagerGlobal {
/*******部分代码省略**********/
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//检查参数是否合法
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent and we're running on L or above (or in the
// system context), assume we want hardware acceleration.
final Context context = view.getContext();
if (context != null
&& context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
//声明ViwRootImpl
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
//创建ViwRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//将Window所对应的View、ViewRootImpl、LayoutParams顺序添加在WindowManager中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try {
//把将Window所对应的View设置给创建的ViewRootImpl
//通过ViewRootImpl来更新界面并完成Window的添加过程。
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
}
ViewRootImpl绑定Window所对应的View,并对该View进行测量、布局、绘制等。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代码省略**********/
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
//ViewRootImpl成员变量view进行复制,以后操作的都是mView。
mView = view;
/*******部分代码省略**********/
//Window在添加完之前先进行一次布局,确保以后能再接受系统其它事件之后重新布局。
//对View完成异步刷新,执行View的绘制方法。
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//将该Window添加到屏幕。
//mWindowSession实现了IWindowSession接口,它是Session的客户端Binder对象.
//addToDisplay是一次AIDL的跨进程通信,通知WindowManagerService添加IWindow
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
/*******部分代码省略**********/
//设置当前View的mParent
view.assignParent(this);
/*******部分代码省略**********/
}
}
}
}
对View的操作包括文章最开始讲述的测量、布局、绘制,其过程主要是在ViewRootImpl的performTraversals方法中。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代码省略**********/
//请求对界面进行布局
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
//校验所在线程,mThread是在ViewRootImpl初始化的时候执行mThread = Thread.currentThread()进行赋值的,也就是初始化ViewRootImpl所在的线程。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
//安排任务
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
//做任务
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
try {
//执行任务
performTraversals();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
//执行任务(分别调用mView的measure、layout、draw)
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
/*******部分代码省略**********/
//想要展示窗口的宽高
int desiredWindowWidth;
int desiredWindowHeight;
//开始进行布局准备
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null) {
/*******部分代码省略**********/
if (!mStopped) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
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;
/*******部分代码省略**********/
if (measureAgain) {
//View的测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
} else {
/*******部分代码省略**********/
}
final boolean didLayout = layoutRequested /*&& !mStopped*/ ;
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
//View的布局
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
/*******部分代码省略**********/
}
/*******部分代码省略**********/
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
/*******部分代码省略**********/
//View的绘制
performDraw();
}
} else {
if (viewVisibility == View.VISIBLE) {
// 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;
}
}
ViewRootImpl调用performMeasure执行Window对应的View的测量。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代码省略**********/
//View的测量
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//mView在Activity中为DecorView(FrameLayout)
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
ViewRootImpl调用performLayout执行Window对应的View的布局。
在这期间可能View会自己触发布布局请求,所以在此过程会在此调用ViewRootImpl的requestLayout重新进行测量、布局、绘制。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代码省略**********/
//请求对ViewRootImpl进行布局
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
//View的布局
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
/*******部分代码省略**********/
final View host = mView;
/*******部分代码省略**********/
try {
//调用View的Layout方法进行布局
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
//在ViewRootImpl进行布局的期间,Window内的View自己进行requestLayout
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
// requestLayout() was called during layout.
// If no layout-request flags are set on the requesting views, there is no problem.
// If some requests are still pending, then we need to clear those flags and do
// a full request/measure/layout pass to handle this situation.
ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
false);
if (validLayoutRequesters != null) {
// Set this flag to indicate that any further requests are happening during
// the second pass, which may result in posting those requests to the next
// frame instead
mHandlingLayoutInLayoutRequest = true;
// Process fresh layout requests, then measure and layout
int numValidRequests = validLayoutRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = validLayoutRequesters.get(i);
Log.w("View", "requestLayout() improperly called by " + view +
" during layout: running second layout pass");
//请求对该View布局,最终回调到ViewRootImpl的requestLayout进行重新测量、布局、绘制
view.requestLayout();
}
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mHandlingLayoutInLayoutRequest = false;
// Check the valid requests again, this time without checking/clearing the
// layout flags, since requests happening during the second pass get noop'd
validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
if (validLayoutRequesters != null) {
final ArrayList<View> finalRequesters = validLayoutRequesters;
// Post second-pass requests to the next frame
getRunQueue().post(new Runnable() {
@Override
public void run() {
int numValidRequests = finalRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = finalRequesters.get(i);
/*******部分代码省略**********/
//请求对该View布局,最终回调到ViewRootImpl的requestLayout进行重新测量、布局、绘制
view.requestLayout();
}
}
});
}
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
}
ViewRootImpl调用performDraw执行Window对应的View的布局。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代码省略**********/
//View的绘制
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
/*******部分代码省略**********/
}
//进行绘制
private void draw(boolean fullRedrawNeeded) {
/*******部分代码省略**********/
//View上添加的Observer进行绘制事件的分发
mAttachInfo.mTreeObserver.dispatchOnDraw();
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
/*******部分代码省略**********/
//调用Window对应的ViewRootImpl的invalidate方法
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
/*******部分代码省略**********/
//绘制Window
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
/**
* @return true if drawing was successful, false if an error occurred
*/
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
/*******部分代码省略**********/
try {
/*******部分代码省略**********/
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
//View绘制
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
/*******部分代码省略**********/
}
return true;
}
}
异步线程中到底是否能对视图进行更新呢?我们先看看TextView.setText
方法的代码执行流程图。
setText.png
从上图看在页面进行视图更新的时候会触发 checkThread
,校验当前线程是否是 ViewRootImpl
被创建时所在的线程。而 ViewRootImpl
的创建是在 Activity
的 onResume
生命周期之后。
需要注意的是不是所有的 TextView.setText
都会触发 checkThread
。 比如 TextView.setText
之后对当前 TextView
的 layout
不会进行任何改变,那么这次的 TextView.setText
就可以在异步线程执行。
ViewRootImpl.checkThread.png
操作UI的时候只有这些方法被调用的时候才会执行 ViewRootImpl.checkThread
,进行任务执行线程的校验。
//异常信息,提示当前执行任务的线程与创建ViewRootImpl的线程不一样~!
new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
我们知道了异步线程不建议操作UI的最终原因是:
发生了对任务执行线程的校验,而且当前执行任务的线程与创建
ViewRootImpl
的线程不一样;
所以只要避免上面的执行逻辑,还是可以在异步线程操作UI的。
ViewRootImpl.checkThread
的情况下再异步线程执行UI操作。Activity.onResume
之前在异步线程进行视图更新,因为这个时候不会发生线程校验。(PS:我们知道 Activity
所绑定的 ViewRootImpl
的初始化是在主线程中,所以我们一般不会在非主线程进行 UI操作
。)ViewRootImpl
同时在该线程进行视图更新(eg: Dialog
异步线程的展现,具体参考:Dialog、Toast的Window和ViewRootImpl)。PS:我们学习Android源码并通过了解其内部实现,我们可以通过技术手段去掉、增加或修改部分代码逻辑。以期望能做出更好的产品、做更细节的优化。但是想这种界面绘制只能发生在主线程规则我们还是必须要遵守的。两个线程不能同时draw,否则屏幕会花;不能同时写一块内存,否则内存会花;不能同时写一份文件,否则文件会花。同一时刻只有一个线程可以做ui,那么当两个线程互斥几率较大时,或者保证互斥的代码复杂时,选择其中一个长期持有其他发消息就是典型的解决方案。所以普遍的要求ui只能单线程。
文章前面内容都是站在Activity的角度来进行代码解析的,因此我们不再对Dialog和Toast与Activity做具体分析,主要来看看它们与Activity有什么不同之处。详见:Dialog、Toast的Window和ViewRootImpl。
通过对ViewRootImpl的更细节的分析,我们再看自定义View的布局时的一些方法会更加清楚(知其然且知其所以然)。同时也解开了Android中异步线程更新View
的谜底。