Activity界面绘制流程

Activity界面绘制流程

2023年7月15日发(作者:)

Activity界⾯绘制流程在Activity启动流程中,执⾏⽣命周期中的Create流程,在onCreate()⽅法中通过调⽤setContentView()⽅法,完成view的创建。前⾯源码的分析得出这⼀步仅仅是将布局创建,并没有显⽰出来。这篇⽂章继续分析,创建后的布局是如何显⽰出来的。我们知道在执⾏完Create流程后,接下来会执⾏Resume流程,这⼀步会调⽤ActivityThread中的handleResumeActivity()⽅法。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); ...

if ( == null && !hed && willBeVisible) { = dow(); View decor = orView(); ibility(BLE); ViewManager wm = dowManager(); Params l = ributes(); = decor; = _BASE_APPLICATION; putMode |= forwardBit; ... if (leFromClient) { if (!wAdded) { wAdded = true; w(decor, l); } else { ... } } ... } else if (!willBeVisible) { ... } ... if (!hed && willBeVisible && != null && !rNow) { ... leFromServer = true; mNumVisibleActivities++; if (leFromClient) { sible(); } } ...}⾸先会调⽤performResumeActivity()⽅法,这个⽅法最终会调⽤Activity中的onResume⽅法。接着调⽤w(decor, l)⽅法,第⼀个参数decor是上⾯通过orView()获得的,是⼀个DecorView,就是我们视图的根布局。这⾥调⽤的是vm的addView()⽅法,省略了⼀部分代码,最后得到这个vm其实是⼀个WindowManagerImpl类的实例。在WindowManagerImpl类中找到addView()⽅法。public void addView(@NonNull View view, @NonNull Params params) { applyDefaultToken(params); w(view, params, play(), mParentWindow);}调⽤WindowManagerGlobal中的addView⽅法。public void addView(View view, Params params, Display display, Window parentWindow) { ... ViewRootImpl root; View panelParentView = null; synchronized (mLock) { ... root = new ViewRootImpl(text(), display); outParams(wparams); (view); (root); (wparams); // do this last because it fires off messages to start doing things try { w(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } }}⾸先创建⼀个ViewRootImpl对象,然后调⽤setView⽅法。注意⼀下其中的第⼀个参数,就是上⾯在调⽤addView()⽅法时,传⼊的⼀个DecorView。进到ViewRootImpl的setView⽅法中。public void setView(View view, Params attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view;... mSoftInputMode = putMode; mWindowAttributesChanged = true; mWindowAttributesChangesFlag = HING_CHANGED; iew = view; ngRequired = mTranslator != null; cationScale = mTranslator == null ? 1.0f : ationScale; if (panelParentView != null) { ParentWindowToken = licationWindowToken(); } mAdded = true; int res; /* = _OKAY; */ // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); ... try { ... } catch (RemoteException e) { ... } finally { if (restore) { e(); } } ... } }}mView = view,将DecorView赋值给了mView,接着调⽤了requestLayout()⽅法。public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); }}⾸先调⽤checkThread()⽅法。void checkThread() { if (mThread != tThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); }}这就是⼀直说的只能在UI线程更新界⾯的原因,如果当前线程不是主线程,会直接抛出异常。之后会调⽤scheduleTraversals()⽅法。final TraversalRunnable mTraversalRunnable = new TraversalRunnable();void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = per().getQueue().postSyncBarrier(); llback( CK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); }}llback执⾏了mTraversalRunnable这个Runnable。final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); }}在run()⽅法中调⽤了doTraversal()⽅法。void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; per().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { ethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { thodTracing(); mProfile = false; } }}调⽤performTraversals()⽅法。这个⽅法很长,我们需要知道在这个⽅法中先后调⽤了performMeasure、performLayout、performDraw三个⽅法。private void performTraversals() { performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); performLayout(lp, mWidth, mHeight); performDraw();}我们都知道每⼀个View的绘制流程都是measure、layout、draw三步,⽽且是按着以上顺序依次执⾏,原因就是在这⾥。⾸先调⽤performMeasure()⽅法。private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { if (mView == null) { return; } egin(_TAG_VIEW, "measure"); try { e(childWidthMeasureSpec, childHeightMeasureSpec); } finally { nd(_TAG_VIEW); }}调⽤mView的measure⽅法,这⾥的mView是前⾯提到的DecorView。但是看了代码会发现DecoView中没有重写View类的measure⽅法,所以我们看⼀下View中的measure⽅法。public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... if (forceLayout || needsLayout) { ... int cacheIndex = forceLayout ? -1 : fKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = t(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } ... } ...}调⽤了onMeasure⽅法,我们发现onMeasure()在DecorView中有被重写。现在知道了这⾥调⽤onMeasure⽅法开始绘制第⼀个View,是从根布局DecorView开始的。看⼀下DecorView的onMeasure⽅法。protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); final boolean isPortrait = getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT; final int widthMode = getMode(widthMeasureSpec); final int heightMode = getMode(heightMeasureSpec); boolean fixedWidth = false; mApplyFloatingHorizontalInsets = false; if (widthMode == AT_MOST) { final TypedValue tvw = isPortrait ? WidthMinor : WidthMajor; if (tvw != null && != _NULL) { final int w; if ( == _DIMENSION) { w = (int) ension(metrics); } else if ( == _FRACTION) { w = (int) ction(ixels, ixels); } else { w = 0; } if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w); final int widthSize = e(widthMeasureSpec); if (w > 0) { widthMeasureSpec = asureSpec( (w, widthSize), EXACTLY); fixedWidth = true; } else { widthMeasureSpec = asureSpec( widthSize - - , AT_MOST); mApplyFloatingHorizontalInsets = true; } } } ... ure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); boolean measure = false; widthMeasureSpec = asureSpec(width, EXACTLY); ... // TODO: Support height? if (measure) { ure(widthMeasureSpec, heightMeasureSpec); }}在这个⽅法中发现有⼀⾏ure(widthMeasureSpec, heightMeasureSpec)⽅法。因为DecorView继承⾃FrameLayout,进到FrameLayout的onMeasure()⽅法。protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); ... for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || ibility() != GONE) { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); ... } } ... setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); ...}通过for循环遍历⼦View,调⽤measureChildWithMargins()⽅法。protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) outParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + rgin + argin + widthUsed, ); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + gin + Margin + heightUsed, ); e(childWidthMeasureSpec, childHeightMeasureSpec);}⾸先计算出⼦View的MeasureSpec,然后调⽤View的measure()⽅法,在上⾯分析DecorView时,我们知道了接着会在measure()⽅法中调⽤⼦View⾃⼰重写的onMeasure⽅法。⽽我们知道DecorView的⾥⾯⼀层是⼀个LinearLayout,进到LinearLayout的onMeasure()⽅法。protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mOrientation == VERTICAL) { measureVertical(widthMeasureSpec, heightMeasureSpec); } else { measureHorizontal(widthMeasureSpec, heightMeasureSpec); }}这个LinearLayout的orientation是VERTICAL,所以调⽤measureVertical⽅法。同样的,遍历⼦View,调⽤measureChildBeforeLayout()⽅法。void measureChildBeforeLayout(View child, int childIndex, int widthMeasureSpec, int totalWidth, int heightMeasureSpec, int totalHeight) { measureChildWithMargins(child, widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight);}和FrameLayout同理,调⽤了ViewGroup的measureChildWithMargins⽅法。接下来,就像前⾯的DecorView和刚刚LinearLayout的measure⼀样,通过View的measure⽅法,调⽤各⼦View⾃⼰重写的onMeasure⽅法完成布局的测量,⽽且这个测量过程是由外向内的。当调⽤performMeasure()⽅法,所有的布局都依次测量完成后,才会顺序执⾏performLayout()、performDraw()⽅法,依次完成layout和draw过程。以上就是当启动⼀个Activity时,界⾯是如何加载及绘制的。并且通过源码分析了为什么界⾯的绘制流程是measure、layout、draw依次完成的。

发布者:admin,转转请注明出处:http://www.yc00.com/news/1689429482a246813.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信