展開說說:Android頁面繪制流程源碼解析

說到Android系統View的繪制流程大家一定知道是分為測量(Measure)、布局(Layout)和繪制(Draw)三個階段這篇文章主要聊一聊在這三個步驟之前的源碼執行流程,頁面啟動后是怎樣通過代碼執行這三個方法

之前看過startActivity啟動一個Activity最終都會經過ActivityThread類來調用Activity的生命周期。所以我們就從ActivityThread中調用onResume的performResumeActivity方法開始。

1、ActivityThread類的performResumeActivity方法

首先通過performResumeActivity調用Activity的onResume方法,之前在“四大組件”專欄中分析過這個方法。

final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

繼續向下看:

ViewManager wm = a.getWindowManager();

此處省略好多行。。。。

wm.addView(decor,l)

這里第一個參數就是DecorView的實例,執行順序在performResumeActivity后面.

補充一下,此處的wm類型是ViewManager,ViewManager是個接口

/** Interface to let you add and remove child views to an Activity. To get an instance* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.*/
public interface ViewManager
{/*** Assign the passed LayoutParams to the passed View and add the view to the window.* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming* errors, such as adding a second view to a window without removing the first view.* <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a* secondary {@link Display} and the specified display can't be found* (see {@link android.app.Presentation}).* @param view The view to be added to this window.* @param params The LayoutParams to assign to view.*/public void addView(View view, ViewGroup.LayoutParams params);public void updateViewLayout(View view, ViewGroup.LayoutParams params);public void removeView(View view);
}

WindowManager接口繼承ViewManager,順著可以找到WindowManager的實現類WindowManagerImpl。

2、來到WindowManagerImpl

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyDefaultToken(params);mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());
}

3、進入WindowManagerGlobal類

addView方法最底下:

root = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);mViews.add(view);
mRoots.add(root);
mParams.add(wparams);// do this last because it fires off messages to start doing things
try {root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {// BadTokenException or InvalidDisplayException, clean up.if (index >= 0) {removeViewLocked(index, true);}throw e;
}

這里創建ViewRootImpl并且會調用ViewRootImpl的setView方法,這里傳入的view就是上面wm.addView(decor,l)時傳入的decorView他也會一直透傳到ViewRootImpl的setView方法

4、ViewRootImpl的setView方法

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {synchronized (this) {if (mView == null) {mView = view;

下面省略好多行。。。。

上面的view就是從ActivityThread傳遞過來的DecorView實例,因此mView 就是DecorView,add方法下面滑油三個比較重要的點:requestLayout()mInputEventReceiver以及view.assignParent(this)

4.1?requestLayout調用scheduleTraversals

@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);notifyRendererOfFramePending();pokeDrawLockIfNeeded();}
}

4.2 進入Choreographer類

上面mChoreographer.postCallBack, 這里會調用Choreographer類的postCallBack - postCallBackDelayed - postCallbackDelayedInternal - scheduleFrameLocked

@UnsupportedAppUsage
@TestApi
public void postCallback(int callbackType, Runnable action, Object token) {postCallbackDelayed(callbackType, action, token, 0);
}@UnsupportedAppUsage
@TestApi
public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) {if (action == null) {throw new IllegalArgumentException("action must not be null");}if (callbackType < 0 || callbackType > CALLBACK_LAST) {throw new IllegalArgumentException("callbackType is invalid");}postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) {if (DEBUG_FRAMES) {Log.d(TAG, "PostCallback: type=" + callbackType+ ", action=" + action + ", token=" + token+ ", delayMillis=" + delayMillis);}synchronized (mLock) {final long now = SystemClock.uptimeMillis();final long dueTime = now + delayMillis;mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);if (dueTime <= now) {scheduleFrameLocked(now);} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);msg.arg1 = callbackType;msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, dueTime);}}
}private void scheduleFrameLocked(long now) {if (!mFrameScheduled) {mFrameScheduled = true;if (USE_VSYNC) {if (DEBUG_FRAMES) {Log.d(TAG, "Scheduling next frame on vsync.");}// If running on the Looper thread, then schedule the vsync immediately,// otherwise post a message to schedule the vsync from the UI thread// as soon as possible.if (isRunningOnLooperThreadLocked()) {scheduleVsyncLocked();} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);msg.setAsynchronous(true);mHandler.sendMessageAtFrontOfQueue(msg);}} else {final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);if (DEBUG_FRAMES) {Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");}Message msg = mHandler.obtainMessage(MSG_DO_FRAME);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextFrameTime);}}
}

上面scheduleFrameLocked方法通過handler發了MSG_DO_FRAME的msg消息。

private final class FrameHandler extends Handler {public FrameHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_DO_FRAME:doFrame(System.nanoTime(), 0);break;case MSG_DO_SCHEDULE_VSYNC:doScheduleVsync();break;case MSG_DO_SCHEDULE_CALLBACK:doScheduleCallback(msg.arg1);break;}}
}

?handleMessage中doFrame方法調用?doCallbacks,然后調用CallbackRecord

類的run方法 c.run(frameTimeNanos);

5、進入CallbackRecord

private static final class CallbackRecord {public CallbackRecord next;public long dueTime;public Object action; // Runnable or FrameCallbackpublic Object token;@UnsupportedAppUsagepublic void run(long frameTimeNanos) {if (token == FRAME_CALLBACK_TOKEN) {((FrameCallback)action).doFrame(frameTimeNanos);} else {((Runnable)action).run();}}
}

通過((Runnable)action).run()看到是又回到了4.1中????????mChoreographer.postCallback(
????????????????Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);可以看到是返回了ViewRootImpl的TraversalRunnable的實例mTraversalRunnable

6、返回ViewRootImpl?

((Runnable)action).run(); ?又調回到ViewRootImpl的TraversalRunnable的run方法調

final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

?doTraversal方法調用performTraversals()方法

performTraversals()方法,measure測量、layout布局、draw繪制,都是在這個方法開啟的
??預測量

windowSizeMayChange |= measureHierarchy(host, lp, res,
????????desiredWindowWidth, desiredWindowHeight);

布局窗口
?relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

控件樹測量

?performMeasure(childWidthMeasureSpec, childHeightMeasureSpec)

布局控件
?performLayout(lp,mWidth,mHeight) ?

繪制控件
?performDraw()

以measure控件樹測量為例看看怎樣調用到View的measure方法:

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(int widthMeasureSpec, int heightMeasureSpec)方法,因此執行頂級父類View的measure方法:

onMeasure(widthMeasureSpec, heightMeasureSpec);

View的measure方法會調用onMeasure,DecorView中有此方法因此來到DecorView:

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

這里super又會調用父類FrameLayout中的onMeasure方法,方法很長只截取部分:

FrameLayout根據它的MeasureSpec調用measureChildWithMargin方法來對每一個子View進行測量

for (int i = 0; i < count; i++) {final View child = getChildAt(i);if (mMeasureAllChildren || child.getVisibility() != GONE) {measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);final LayoutParams lp = (LayoutParams) child.getLayoutParams();maxWidth = Math.max(maxWidth,child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);maxHeight = Math.max(maxHeight,child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);childState = combineMeasuredStates(childState, child.getMeasuredState());if (measureMatchParentChildren) {if (lp.width == LayoutParams.MATCH_PARENT ||lp.height == LayoutParams.MATCH_PARENT) {mMatchParentChildren.add(child);}}}
}

DecorView委托父類對所有子view完成測量。

7、ViewRootImpl的setView方法

再說剩下的兩個重要的點,后面再細致的單獨分析:
?????res=mWindowSession.addToDisplayAsUser(),與requestLayout同在setView方法,負責將窗口添加到WMS上,主要通過mWindowSession與WMS進行系統通信
?????mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper()),與requestLayout同在setView方法,View事件輸入的接收器,接收來自于系統的事件
?????view.assignParent(this),與requestLayout同在setView方法,assignParent方法在View類實現,將ViewRootImpl自己設置為DecorView的parent,所以ViewTree上的Root就是ViewRootImpl。

才疏學淺,如有錯誤,歡迎指正,多謝。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/45105.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45105.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45105.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C語言丟失精度 如何實現高精度計算

&#xff08;1&#xff09;int 類型舉例 int &#xff1a;占4個字節&#xff0c;也就是32位&#xff0c;及最大值是2^32-11024*1024*1024*4-14294967295 以上說法錯誤&#xff0c;因為Int是有符號類型整數&#xff0c;所以最高位是符號位&#xff0c;及int的最大值應該是2^31…

【Java】鏈表的頭插法和尾插法

頭插法 頭插法就是在已有的節點的前面插入新節點 如何實現 &#xff08;1&#xff09;先定義一個節點類ListNode&#xff0c;里面有value值和地址 public class ListNode {int value;ListNode next;public ListNode(int value){this.value value;}Overridepublic String t…

開發指南046-機構樹控件

為了簡化編程&#xff0c;平臺封裝了很多前端組件。機構樹就是常用的組件之一。 基本用法&#xff1a; import QlmOrgTree from /qlmcomponents/tree/QlmOrgTree <QlmOrgTree></QlmOrgTree> 功能&#xff1a; 根據權限和控制參數顯示機構樹。機構樹數據來源于核…

讓我們一起來看看這些強大的中國汽車品牌如何勇攀巔峰!

咱們中國的汽車品牌&#xff0c;就是這么牛&#xff01;你知道嗎&#xff1f;他們已經悄悄崛起&#xff0c;一步步向著更廣闊的海外市場進軍了。盡管這個過程可能有點坎坷&#xff0c;但是“勇敢”始終是他們前行的動力&#xff0c;推動著他們不斷向前&#xff0c;打造屬于我們…

AGI 之 【Hugging Face】 的【文本摘要】的 [評估PEGASUS ] / [ 微調PEGASUS ] / [生成對話摘要] 的簡單整理

AGI 之 【Hugging Face】 的【文本摘要】的 [評估PEGASUS ] / [ 微調PEGASUS ] / [生成對話摘要] 的簡單整理 目錄 AGI 之 【Hugging Face】 的【文本摘要】的 [評估PEGASUS ] / [ 微調PEGASUS ] / [生成對話摘要] 的簡單整理 一、簡單介紹 二、文本摘要 三、在CNN/Daily…

秋招突擊——7/9——MySQL索引的使用

文章目錄 引言正文B站網課索引基礎創建索引如何在一個表中查看索引為字符串建立索引全文索引復合索引復合索引中的排序問題索引失效的情況使用索引進行排序覆蓋索引維護索引 數據庫基礎——文檔資料學習整理創建索引刪除索引創建唯一索引索引提示復合索引聚集索引索引基數字符串…

C#基于任務的異步模式(TAP)

1、C#異步模式分類 基于任務的異步模式&#xff08;TAP&#xff09; 基于事件的異步模式&#xff08;EAP&#xff09;和異步編程模型模式&#xff08;APM&#xff09; 2、基于任務的異步模式&#xff08;TAP&#xff09; 基于任務的異步模式&#xff08;TAP&#xff09;用單個方…

從零手寫實現 nginx-28-error pages 指令

前言 大家好&#xff0c;我是老馬。很高興遇到你。 我們為 java 開發者實現了 java 版本的 nginx https://github.com/houbb/nginx4j 如果你想知道 servlet 如何處理的&#xff0c;可以參考我的另一個項目&#xff1a; 手寫從零實現簡易版 tomcat minicat 手寫 nginx 系列 …

夾子音轉換器matlab

操作過程點擊此處觀看 上段時間補習了一下傅里葉變化的知識&#xff0c;突發奇想可以根據此做一款聲音轉換器&#xff0c;使用工科神器Matlab進行完成&#xff0c;并且開發了可操作界面如下圖所示&#xff1a; 功能實現與描述 軟件中可以實現聲音的錄制、回放、文件的保存與…

【C++】動態內存分配(關于構造與析構函數的調用)動態數組類 動態創建多維數組 知識點+代碼學習記錄

一.動態內存分配相關知識點 1.堆和棧內存&#xff1a; 堆內存&#xff1a;動態分配的內存位于堆中&#xff0c;它不受作用域限制&#xff0c;由程序員控制其生命周期。 棧內存&#xff1a;局部變量和函數參數等自動分配的內存位于棧中&#xff0c;由編譯器自動管理。 2.new…

性能測試(2)

jmeter參數化 loadrunner Jmeter IP欺騙&#xff0c;也稱為IP欺詐&#xff0c;是指通過偽裝、篡改IP地址的方式&#xff0c;進行網絡攻擊或欺騙行為。這種行為可能會導致網絡安全問題&#xff0c;包括身份盜竊、數據泄露、DDoS攻擊等。為了保護自己的網絡安全&#xff0c;用戶…

MySQL-表的約束

文章目錄 一、空屬性二、默認值三、zerofill四、列描述五、主鍵刪除主鍵追加主鍵復合主鍵根據主鍵快速索引 六、自增長last_insert_id() 七、唯一鍵八、外鍵class表&#xff08;主表&#xff09;student表&#xff08;從表&#xff09; 一、空屬性 之前我們將表的機構&#xff…

單例模式java

并發的有序性會導致返回空指針 class A{private static volatil A a;private A(){}public A getInstance(){if(anull){synchronized(A.class){if(anull)anew A();}}return a; }anew A();這條語句&#xff0c;他需要&#xff1a;開辟空間&#xff0c;初始化空間&#xff0c;指針…

python熱門面試題三

面試題1&#xff1a;Python中的列表推導式是什么&#xff1f;請給出一個例子。 回答&#xff1a; 列表推導式&#xff08;List Comprehension&#xff09;是Python中一種非常強大且簡潔的構建列表的工具。它允許你通過一個表達式來創建一個新的列表&#xff0c;這個表達式定義…

無顯卡也可以玩大模型-MiniGPT-4篇

MiniGPT-4學習 1.Colab上部署MiniGPT-42.論文摘要3.模型架構4.模型訓練5.總結 1.Colab上部署MiniGPT-4 代碼&#xff1a; https://github.com/Czi24/Awesome-MLLM-LLM-Colab/blob/master/MLLM/MiniGPT-4-colab/MiniGPT-4.md 2.論文摘要 最近的 GPT-4 展示了非凡的多模式能力&…

【設計模式之美】【建造型】工廠模式:通過面向接口編程思路,串起業務流程

文章目錄 一. 簡單工廠&#xff08;Simple Factory&#xff09;第一種簡單工廠&#xff1a;面向接口編程與工廠類&#xff1a;劃分功能職責第二種&#xff1a;單例簡單工廠&#xff1a;節省內存和對象創建的時間 二. 工廠方法&#xff08;Factory Method&#xff09;&#xff1…

文心一言:如何備考軟考架構師

提問&#xff1a;軟考高級:架構師指導&#xff0c;有哪些考點、重點考點有哪些&#xff1f;需要如何備考&#xff1f;案例分析題如何做&#xff1f;論文如何寫&#xff1f; 文心一言回答1700字如下&#xff1a; 針對軟考高級架構師的考試&#xff0c;以下是對考點、重點考點、備…

DiffusionModel-Transformer知識回顧

論文鏈接&#xff1a; Attention Is All You Need CNN vs RNN vs Transformer CNN&#xff08;卷積神經網絡&#xff09; 特點&#xff1a; 局部連接&#xff1a;每個卷積神經元只與輸入數據的局部區域連接&#xff0c;這有助于捕捉局部特征。權重共享&#xff1a;卷積核的權重…

Android筆記:Constructor (xxx) has been changed after generation.

遇到此報錯時&#xff0c;onstructor (xxx) has been changed after generation.是因為修改了實體類字段后什么都不修改的話就會報這個錯 這條信息是關于代碼生成和代碼變更的警告。當你使用某些工具&#xff08;如注解處理器、代碼生成庫等&#xff09;來自動生成代碼時&…

運行在Linux上的程序越來越慢的排查思路

1、通過free -h 排查內存使用情況&#xff0c;是否內存滿了 2、通過df -h 排查磁盤的使用情況&#xff0c;磁盤是否沒有空間了 3、檢查系統資源配置情況&#xff0c;比如使用ulimit -a檢查當前會話的資源限制&#xff0c;如最大文件數、打開文件描述符數等&#xff0c;看是否…