在子线程中不能更新UI的前提是不触发 checkThread ,逐步委托给mParent检查线程
onCreate加载contentView 进行draw
onStart onResume 可能也没有完成测量流程
setContentView:
public abstract void setContentView(@LayoutRes int resId);
Activity 是由ActivityThread类中的handleLaunchActivity创建的,
Activity 的 onStart 由 handleStartActivity调用,onResume 由 handleResumeActivity调用
Activity的具体创建实现:
final Activity a = performLaunchActivity(r, customIntent);
performLaunchActivity:
activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent);
newActivity:
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { String pkg = intent != null && intent.getComponent() != null ? intent.getComponent().getPackageName() : null; return getFactory(pkg).instantiateActivity(cl, className, intent); }
instantiateActivity:
public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className, @Nullable Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return (Activity) cl.loadClass(className).newInstance(); }
通过反射创建 classLoad.newInstance
创建出Activity后 Activity会调用attach方法
activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback, r.assistToken);
attach:
@UnsupportedAppUsage 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, ActivityConfigCallback activityConfigCallback, IBinder assistToken) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(mWindowControllerCallback); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); } mUiThread = Thread.currentThread(); mMainThread = aThread; mInstrumentation = instr; mToken = token; mAssistToken = assistToken; mIdent = ident; mApplication = application; mIntent = intent; mReferrer = referrer; mComponent = intent.getComponent(); mActivityInfo = info; mTitle = title; mParent = parent; mEmbeddedID = id; mLastNonConfigurationInstances = lastNonConfigurationInstances; if (voiceInteractor != null) { if (lastNonConfigurationInstances != null) { mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor; } else { mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this, Looper.myLooper()); } } 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(); mCurrentConfig = config; mWindow.setColorMode(info.colorMode); mWindow.setPreferMinimalPostProcessing( (info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0); setAutofillOptions(application.getAutofillOptions()); setContentCaptureOptions(application.getContentCaptureOptions()); }
然后调用onCreate
if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); }
然后调用callActivityOnCreate
public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) { prePerformCreate(activity); activity.performCreate(icicle, persistentState); postPerformCreate(activity); }
performCreate:
final void performCreate(Bundle icicle) { performCreate(icicle, null); }
performCreate:
@UnsupportedAppUsage final void performCreate(Bundle icicle, PersistableBundle persistentState) { dispatchActivityPreCreated(icicle); mCanEnterPictureInPicture = true; // initialize mIsInMultiWindowMode and mIsInPictureInPictureMode before onCreate final int windowingMode = getResources().getConfiguration().windowConfiguration .getWindowingMode(); mIsInMultiWindowMode = inMultiWindowMode(windowingMode); mIsInPictureInPictureMode = windowingMode == WINDOWING_MODE_PINNED; restoreHasCurrentPermissionRequest(icicle); if (persistentState != null) { onCreate(icicle, persistentState); } else { onCreate(icicle); } EventLogTags.writeWmOnCreateCalled(mIdent, getComponentName().getClassName(), "performCreate"); mActivityTransitionState.readState(icicle); mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); dispatchActivityPostCreated(icicle); }
postPerformCreate:
private void postPerformCreate(Activity activity) { if (mActivityMonitors != null) { synchronized (mSync) { final int N = mActivityMonitors.size(); for (int i=0; i<N; i++) { final ActivityMonitor am = mActivityMonitors.get(i); am.match(activity, activity, activity.getIntent()); } } } }
setContentView 通过Window 也就是PhoneWindow连接
activity 通过attch关联Window(PhoneWindow),PhoneWindow通过setContentView关联View
getWindow().setContentView(view);
PhoneWindow通过Decor进行关联View (DecorView)
getLocalFeature 创建feature,在setContentView前调用 requestWindowFeature
PhoneWindow
int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { layoutResource = R.layout.screen_swipe_dismiss; setCloseOnSwipeEnabled(true); } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_title_icons; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = R.layout.screen_progress; // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_custom_title; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { // If no other features and not embedded, only need a title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar); } else { layoutResource = R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); }
contentView 先初始化 screen_simple.xml
screen_simple.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
mDecor.startChanging(); mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
onResourcesLoaded
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { if (mBackdropFrameRenderer != null) { loadBackgroundDrawablesIfNeeded(); mBackdropFrameRenderer.onResourcesLoaded( this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), getCurrentColor(mNavigationColorViewState)); } mDecorCaptionView = createDecorCaptionView(inflater); final View root = inflater.inflate(layoutResource, null); //根View if (mDecorCaptionView != null) { if (mDecorCaptionView.getParent() == null) { addView(mDecorCaptionView, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } mDecorCaptionView.addView(root, new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT)); } else { // Put it below the color views. addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //添加到docorView } mContentRoot = (ViewGroup) root; initializeElevation(); }
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
@Nullable public <T extends View> T findViewById(@IdRes int id) { return getDecorView().findViewById(id); }
setContentView
@Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }
关系图 由下往上调用
DecorView parent 是 ViewRootImpl实现
而子线程更新UI 会出发checkThread
判断mThread != Thread.currentThread
mThread:
public ViewRootImpl(Context context, Display display) { mContext = context; mWindowSession = WindowManagerGlobal.getWindowSession(); mDisplay = display; mBasePackageName = context.getBasePackageName(); mThread = Thread.currentThread();
}
handlResumeActivity
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, 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; // TODO Push resumeArgs into the activity for consideration final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
performResumeActivity
public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest, String reason) { final ActivityClientRecord r = mActivities.get(token); if (localLOGV) { Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished); } if (r == null || r.activity.mFinished) { return null; } if (r.getLifecycleState() == ON_RESUME) { if (!finalStateRequest) { final RuntimeException e = new IllegalStateException( "Trying to resume activity which is already resumed"); Slog.e(TAG, e.getMessage(), e); Slog.e(TAG, r.getStateString()); // TODO(lifecycler): A double resume request is possible when an activity // receives two consequent transactions with relaunch requests and "resumed" // final state requests and the second relaunch is omitted. We still try to // handle two resume requests for the final state. For cases other than this // one, we don't expect it to happen. } return null; } if (finalStateRequest) { r.hideForNow = false; r.activity.mStartedActivity = false; } try { r.activity.onStateNotSaved(); r.activity.mFragments.noteStateNotSaved(); checkAndBlockForNetworkAccess(); if (r.pendingIntents != null) { deliverNewIntents(r, r.pendingIntents); r.pendingIntents = null; } if (r.pendingResults != null) { deliverResults(r, r.pendingResults, reason); r.pendingResults = null; } r.activity.performResume(r.startsNotResumed, reason); r.state = null; r.persistentState = null; r.setState(ON_RESUME); reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming"); } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException("Unable to resume activity " + r.intent.getComponent().toShortString() + ": " + e.toString(), e); } } return r; }
performResume
final void performResume(boolean followedByPause, String reason) { dispatchActivityPreResumed(); performRestart(true /* start */, reason); mFragments.execPendingActions(); mLastNonConfigurationInstances = null; if (mAutoFillResetNeeded) { // When Activity is destroyed in paused state, and relaunch activity, there will be // extra onResume and onPause event, ignore the first onResume and onPause. // see ActivityThread.handleRelaunchActivity() mAutoFillIgnoreFirstResumePause = followedByPause; if (mAutoFillIgnoreFirstResumePause && DEBUG_LIFECYCLE) { Slog.v(TAG, "autofill will ignore first pause when relaunching " + this); } } mCalled = false; // mResumed is set by the instrumentation mInstrumentation.callActivityOnResume(this); EventLogTags.writeWmOnResumeCalled(mIdent, getComponentName().getClassName(), reason); if (!mCalled) { throw new SuperNotCalledException( "Activity " + mComponent.toShortString() + " did not call through to super.onResume()"); } // invisible activities must be finished before onResume() completes if (!mVisibleFromClient && !mFinished) { Log.w(TAG, "An activity without a UI must call finish() before onResume() completes"); if (getApplicationInfo().targetSdkVersion > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) { throw new IllegalStateException( "Activity " + mComponent.toShortString() + " did not call finish() prior to onResume() completing"); } } // Now really resume, and install the current status bar and menu. mCalled = false; mFragments.dispatchResume(); mFragments.execPendingActions(); onPostResume(); if (!mCalled) { throw new SuperNotCalledException( "Activity " + mComponent.toShortString() + " did not call through to super.onPostResume()"); } dispatchActivityPostResumed(); }
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;
WindowManager: 是一个接口 继承viewManager
public interface WindowManager extends ViewManager {}
每个WindowManager内部都有一个WindowMangerImpl对象 然后分发
ViewRootImpl:
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
@UnsupportedAppUsage void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
postCallback //异步操作
doTraversal
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
performTraversals
private void performTraversals() { // cache mView since it is used so much below... final View host = mView; if (DBG) { System.out.println("======================================"); System.out.println("performTraversals"); host.debug(); } if (host == null || !mAdded) return; mIsInTraversal = true; mWillDrawSoon = true; boolean windowSizeMayChange = false; boolean surfaceChanged = false; WindowManager.LayoutParams lp = mWindowAttributes; int desiredWindowWidth; int desiredWindowHeight; final int viewVisibility = getHostVisibility(); final boolean viewVisibilityChanged = !mFirst
.......................
if (mFirst) { mFullRedrawNeeded = true; mLayoutRequested = true;
}
view的post:
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); }
取attachInfo的handler
handler从创建AttachInfo开始创建
AttachInfo(IWindowSession session, IWindow window, Display display, ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer, Context context) { mSession = session; mWindow = window; mWindowToken = window.asBinder(); mDisplay = display; mViewRootImpl = viewRootImpl; mHandler = handler; //handler mRootCallbacks = effectPlayer; mTreeObserver = new ViewTreeObserver(context); } }
ViewRootImpl
final ViewRootHandler mHandler = new ViewRootHandler(); //ViewoRootImpl
// Ask host how big it wants to be windowSizeMayChange |= measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight); }
测量
measureHierarchy
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); 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); 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); 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(); } return windowSizeMayChange; }
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 measure
performMeasure
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { if (mView == null) { return; } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
然后DecorView 会进行measure测量,层层调用下层View 完成测量
relayoutWindow window的大小由WMS 进行调整的
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException {
把Window从初始值改为固定值
然后执行
if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) { if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: " + mPendingMergedConfiguration.getMergedConfiguration()); performConfigurationChange(mPendingMergedConfiguration, !mFirst, INVALID_DISPLAY /* same display */); updatedConfiguration = true; //updatedConfiguration }
第二次测量(ViewRootImpl)
if (!mStopped || mReportNextDraw) { 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); if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth=" + mWidth + " measuredWidth=" + host.getMeasuredWidth() + " mHeight=" + mHeight + " measuredHeight=" + host.getMeasuredHeight() + " coveredInsetsChanged=" + contentInsetsChanged); // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //DView测量
第一次测量performMeasure确定Window尺寸
第二次测量View和Window尺寸
绘制
if (!cancelDraw) { if (mPendingTransitions != null && mPendingTransitions.size() > 0) { for (int i = 0; i < mPendingTransitions.size(); ++i) { mPendingTransitions.get(i).startChangingAnimations(); } mPendingTransitions.clear(); } 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(); } }
performDraw(ViewRootImpl)
ViewRootImpl.draw
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) 判断是否开启硬件加速 软件绘制or硬件绘制
ViewRootImpl.draw() -> mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);//硬件绘制
软件绘制(DecorView draw)
如果子线程更新UI不触发requestLayout 则不进行报错 也就是不执行checkThread
mThread有ViewRootImpl 创建
ViewRootImpl由WindowMannagerGlobal中addView创建
可以再子线程中创建View 创建前需要Looper.prepare / 创建handler
然后再Loop.loop
thread { Looper.prepare() val button = Button(this) windowManager.addView(button,WindowManager.LayoutParams()) button.setOnClickListener { Toast.makeText(this,"button",Toast.LENGTH_LONG).show() } Looper.loop() }
TextView 如果固定宽高,则不会触发requestLayout 然后调用ViewGroup的invalidateChild
ViewGroup会判断是否支持/开启了硬件加速,如果开启 就不进行重回 则执行 onDescendantIncalidated(child,child) 则不会检查线程