源码:Android11
执行主线注释标识:// ####
省略代码标识 // ...
PS:不定期更新内容
场景1:Activity绘制流程
// activity中执行resume方法的源码位置
ActivityThread.java
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
// #### 执行activity的onResume方法 1->A
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
// #### 这里获取PhoneWindow
r.window = r.activity.getWindow();
// #### 这里获取DecorView
View decor = r.window.getDecorView();
// #### 这里wm是WindowManagerGlobal 1->B
wm.addView(decor, l);
}
public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
String reason) {
// #### 执行activity的onResume方法 1->A
r.activity.performResume(r.startsNotResumed, reason);
}
Activity.java
final void performResume(boolean followedByPause, String reason) {
// #### 执行activity的onResume方法 结束1->A
mInstrumentation.callActivityOnResume(this);
}
WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
// #### 构建ViewRootImpl 1->B->A
root = new ViewRootImpl(view.getContext(), display);
// #### 添加DecorView 1->B
mViews.add(view);
// #### 添加ViewRootImpl 1->B
mRoots.add(root);
// #### 添加LayoutParams 1->B
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
// #### 最后把View添加到ViewRootImpl 1->B
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
ViewRootImpl.java
public ViewRootImpl(Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
// #### 记录当前线程作为后面判断是否在UI线程刷新的依据 结束1->B->A
mThread = Thread.currentThread();
// #### 记录当前窗口的数据 1->B->A
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
// #### 请求遍历 1->B
requestLayout();
// #### 将窗口PhoneWindow添加到WMS 1->B
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
}
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// #### 这里会检查当前刷新UI的线程是否与初始化ViewRootImpl的线程一致,也就是分析1->B->A流程的关联上
checkThread();
mLayoutRequested = true;
// #### 请求刷新UI 1->B
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// #### 向Choreographer发送刷新UI的请求,最终执行mTraversalRunnable 1->B
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
// #### 刷新UI 1->B
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
// #### 刷新UI 1->B
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
private void performTraversals() {
// #### 刷新UI 结束1->B
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
场景2:View测量流程
ViewRootImpl.java
// 根据上面的绘制流程
private void performTraversals() {
// #### 这里会先进行最多三次的预测量 2->A
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
// #### 进行遍历测量 2->B
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
int childWidthMeasureSpec;
int childHeightMeasureSpec;
boolean windowSizeMayChange = false;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag,
"Measuring " + host + " in display " + desiredWindowWidth
+ "x" + desiredWindowHeight + "...");
boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
// On large screens, we don't want to allow dialogs to just
// stretch to fill the entire width of the screen to display
// one line of text. First try doing the layout at a smaller
// size to see if it will fit.
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize
+ ", desiredWindowWidth=" + desiredWindowWidth);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
// #### 第一次进行系统默认尺寸的预测量是否满足 2->A
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight()
+ ") from width spec: " + MeasureSpec.toString(childWidthMeasureSpec)
+ " and height spec: " + MeasureSpec.toString(childHeightMeasureSpec));
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
// #### 第二次进行大一倍尺寸的预测量是否满足 2->A
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(mTag, "Good!");
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
// #### 第三次进行最大尺寸的预测量是否满足 2->A
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after measure");
host.debug();
}
// #### 返回三次预测量是否满足的判断 接受2->A
return windowSizeMayChange;
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
// #### 执行View的onMeasure 2->B
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
// #### 执行View的onMeasure 结束2->B
onMeasure(widthMeasureSpec, heightMeasureSpec);
// #### 根据以下抛出的异常知道,自定义view或者viewgroup重写onMeasure方法必须调用setMeasuredDimension方法 2->B
if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
throw new IllegalStateException("View with id " + getId() + ": "
+ getClass().getName() + "#onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
}
}
场景3:子线程更新UI
// 根据场景2分析知道,如果要在子线程更新UI的主要问题是绕过1->B->A流程的mThread的判断,所以可以从两方面入手,一个是创建ViewRootImpl前触发刷新UI,一个是子线程自己创建ViewRootImpl
// 从场景1、2我们知道初始化Activity如何渲染UI,现在再看下View自己如何触发刷新UI的流程
View.java
public void invalidate(boolean invalidateCache) {
// #### 3->A
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
// #### 3->A
p.invalidateChild(this, damage);
}
ViewGroup.java
public final void invalidateChild(View child, final Rect dirty) {
// #### 3->A
parent = parent.invalidateChildInParent(location, dirty);
}
ViewRootImpl.java
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
// #### 这里又出现了判断刷新UI是否一直的方法 3->A->A
checkThread();
// #### 结束3->A
invalidate();
}
void checkThread() {
// #### 所以要再子线程刷新UI的方法就是绕过这个mThread的初始化或者子线程就是mThread 结束3->A->A
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}