Activity、Window、DecorView的關系

目錄

一、Activity、Window、DecorView的層級關系如下圖所示:

1、Activity

2、Window

3、DecorView? ?

二、DecorView初始化相關源碼??

三、DecorView顯示時機


前言: 不同的Android版本有差異,以下基于Android 11進行講解。

一、Activity、Window、DecorView的層級關系如下圖所示:

? ? ? ? 從上圖可以直觀的看到Activity、Window以及DecorView之間的關系,Activity持有window(Phone Window)、而在window里管理著DecorView,我們一般平時開發對Activity樣式的修改,實際上是對DecorView的修改、系統中提供著幾種默認DecorView樣式。?

?1、Activity

對于應用的開發,我們通常操做Activity來創建我們想要的視圖界面,但實際上對視圖的控制并不是Activity,而是Activity持有的Window。每一個Activity包含一個PhoneWindow,PhoneWindow是window的子類。

如上圖所示,在Activity類中的attach方法中創建了PhoneWindow實例。

2、Window

? ? ? ? Window是視圖界面真正的管理器。Window是一個抽象類,具體的實現在PhoneWindow類,PhoneWindow類繼承于Window抽象類。PhoneWindow類該持有DecorView的實例。PhoneWindow類通過DecorView來加載布局xml文件。

?如上圖所示,在PhoneWindow中給全局變量mDecor負值。mDecor為DecorView實例。

3、DecorView? ?

?根據以上源碼截圖我們發現DecorView是FrameLayout類的子類。

Android原生提供了幾種樣式給DecorView。如下這幾種xml布局都是原生提供的:

其中我們看看screen_title.xml布局代碼:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:fitsSystemWindows="true"><!-- Popout bar for action modes --><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" /><FrameLayoutandroid:layout_width="match_parent" android:layout_height="?android:attr/windowTitleSize"style="?android:attr/windowTitleBackgroundStyle"><TextView android:id="@android:id/title" style="?android:attr/windowTitleStyle"android:background="@null"android:fadingEdge="horizontal"android:gravity="center_vertical"android:layout_width="match_parent"android:layout_height="match_parent" /></FrameLayout><FrameLayout android:id="@android:id/content"android:layout_width="match_parent" android:layout_height="0dip"android:layout_weight="1"android:foregroundGravity="fill_horizontal|top"android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

在該布局里,DecorView內部包含一個LinearLayout,在這個LinearLayout里面有上下三個部分,上面是個ViewStub,根據Theme樣式設置ActionBar等。中間的是TitleView,有的xml布局沒有這一部分,例如screen_simple.xml中就沒有這一部分。最下面的是ContentViews,這是最重要的一部分,我們開發應用時在oncreate()函數中調用的setContentView()加載方法,其實就是將其加載到這個ContentViews里。

? ? 如上圖所示,phonewindow中通過DecorView的實例mDecor.onResourcesLoaded()方法將該布局加載到DecorView.

二、DecorView初始化相關源碼??

接下來我們看看從Activity 到 PhoneWindow再到DecorView的初始化以及布局加載源碼:

Activity的完整啟動流程在這里不再細說,直接從ActivityThread.java的handleLaunchActivity()方法開始講解。代碼如下:

\frameworks\base\core\java\android\app\ActivityThread.java

@Overridepublic Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {.................// Hint the GraphicsEnvironment that an activity is launching on the process.GraphicsEnvironment.hintActivityLaunch();final Activity a = performLaunchActivity(r, customIntent);//(1).................return a;}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {..................appContext.getResources().addLoaders(app.getResources().getLoaders().toArray(new ResourcesLoader[0]));appContext.setOuterContext(activity);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); //(2)
..................}

如上代碼,在Activity的啟動過程中,其中在ActivityThread.java中的與PhoneWindow及DecorView初始化相關部分得調用流程:

handleLaunchActivity()=>?performLaunchActivity( ) =>?activity.attach( )

接下來我們看看一下activity中attach()方法:

frameworks\base\core\java\android\app\Activity.java

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){...................mWindow = new PhoneWindow(this, window, activityConfigCallback); //(3)mWindow.setWindowControllerCallback(mWindowControllerCallback);mWindow.setCallback(this);     //(4)...................}

在注釋(3)可以看到Activity持有PhoneWindow并初始化了PhoneWindow實例,在注釋(4)中調用PhoneWindow的setCallback方法將activity實例設置給PhoneWindow,這一點很重要,我們在看DecorView的代碼時候,看到的mWindow.getCallback()方法,實際就是獲取DecorView相對應的Activity實例。

我們在第一節第3點講DecorView的布局的時候講到在oncreate()方法中調用setContentView()方法給activity設置布局是加載到DecorView的ContentViews里。接下來我們看一下這個代碼流程:

frameworks\base\core\java\android\app\Activity.java

    public void setContentView(View view) {getWindow().setContentView(view);initWindowDecorActionBar();}

我們在前面注釋(3)中講了Activity持有PhoneWindow并初始化了PhoneWindow實例mWindow。

public Window getWindow() {return mWindow;}

getWindow()是拿到我們初始化好的PhoneWindow。

我們看一下PhoneWindow中的setContentView()方法:

frameworks\base\core\java\com\android\internal\policy\PhoneWindow.java

    @Overridepublic 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) { //(5)installDecor();  //(6)} 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);//(7)}mContentParent.requestApplyInsets();final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}mContentParentExplicitlySet = true;}

以上,注釋(3)中的mContentParent 就是DecorView的ContentViews,在(6)中,如果mContentParent為空即通過installDecor()方法初始化一個DecorView實例、并將此PhoneWindow實例傳給DecorView,installDecor( )方法如下:

private void installDecor() {............if (mDecor == null) {mDecor = generateDecor(-1);mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);mDecor.setIsRootNamespace(true);if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);}} else {mDecor.setWindow(this);}............
}

在注釋(7)中將xml布局文件加載給DecorView的ContentViews。

總結:

Activity創建PhoneWindow對象,并把自己實例傳遞給PhoneWindow;PhoneWindow會創建一個DecorView對象,并把自己實例傳遞給DecorView。DecorView創建過程中根據開發者設置的不同的主題,加載不同的布局到DecorView的ContentViews里。

三、DecorView顯示時機

根據我們平時的開發經驗,我們給activity設置的布局,在activity調用了onResume()方法之后才會顯示出來,那么接下來我們看一下這個流程是否是我們想的這樣。

在以上我們詳述onCreate()方法中調用了setContentView()方法給DecorView設置了布局,此時并不可見。Activity的onResume()方法的調用以及DecorView的顯示都會在ActivityThread的handleResumeActivity()方法實現,我們看一下該方法:

\frameworks\base\core\java\android\app\ActivityThread.java

    @Overridepublic void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {.....................final ActivityClientRecord r = performResumeActivity(token, 
finalStateRequest, reason); //(8)if (r == null) {// We didn't actually resume the activity, so skipping any follow-up actions.return;}.....................if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE);  //(9)ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;l.softInputMode |= forwardBit;.....................if (a.mVisibleFromClient) {if (!a.mWindowAdded) {a.mWindowAdded = true;wm.addView(decor, l); //(10)} else {a.onWindowAttributesChanged(l);}}} else if (!willBeVisible) {if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");r.hideForNow = true;}if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {.....................if (r.activity.mVisibleFromClient) {r.activity.makeVisible(); //(11)}}.....................}

?我們先看注釋(8)performResumeActivity()方法的調用。

\frameworks\base\core\java\android\app\ActivityThread.java

  public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,String reason){final ActivityClientRecord r = mActivities.get(token);.................r.activity.performResume(r.startsNotResumed, reason);//(12).................}

注釋(12)中,調用了activity的performResume()的方法:

\frameworks\base\core\java\android\app\Activity.java

 final void performResume(boolean followedByPause, String reason) {dispatchActivityPreResumed();..................mInstrumentation.callActivityOnResume(this);//(13)..................}

注釋(13)中,調用了Instrumentation的callActivityOnResume()的方法:

public void callActivityOnResume(Activity activity) {activity.mResumed = true;activity.onResume();//(14).......................}

至此,我們知道了在注釋(14)中,我們熟悉的activity中的onResume()方法是被Instrumentation的callActivityOnResume()方法所調起的。

回到前面,也就是說在ActivityThread.java中的注釋(8)中的performResumeActivity()方法里就調起了activity中的onResume()方法。

再看注釋(9)decor.setVisibility(View.INVISIBLE),將DecorView設置為不可見,注釋(10)中wm.addView(decor, l)通過addView的方式將decor視圖添加;最后在注釋(11)r.activity.makeVisible()方法里將decor設置為VISIBLE。makeVisible()方法如下:

\frameworks\base\core\java\android\app\Activity.java

   void makeVisible() {if (!mWindowAdded) {ViewManager wm = getWindowManager();wm.addView(mDecor, getWindow().getAttributes());mWindowAdded = true;}mDecor.setVisibility(View.VISIBLE);}

總結:由以上代碼流程我們知道,activity里的decorView的布局的顯示是在activity中的OnResume()執行了之后才展示出來的,但是我們需要注意的是,即使我們發現了在activity中的OnResume()被調用了,但是handleResumeActivity()方法中的r.activity.makeVisible()方法沒有被執行,該布局視圖依然是不可見的。

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

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

相關文章

Halide AOT模式

這種模式會提前&#xff0c;會提前編譯好&#xff0c;變成dll什么的&#xff0c;可接受任何輸入的參數運行。 然后這樣調用&#xff0c;必須要make一下前一個file&#xff0c;才有后面的.h

魔行觀察-AI數據分析-蜜雪冰城

摘要 本報告旨在評估蜜雪冰城品牌作為投資對象的潛力和價值&#xff0c;基于其經營模式、門店分布、人均消費、覆蓋省份等關鍵指標進行分析。 數據數據源&#xff1a;魔行觀察&#xff1a;http://www.wmomo.com/#/brand/brandDetails?code10013603 品牌概覽 蜜雪冰城是中國…

拼多多職位數據信息采集

數據信息采集 洛哥爬蟲【視頻】 愛寫爬蟲 我愛扣腳 https://careers.pinduoduo.com/jobs#/from DrissionPage import ChromiumPage, ChromiumOptions def fetch_pinduoduo_jobs():# 創建ChromiumOptions對象co ChromiumOptions()# 提供瀏覽器可執行文件的路徑path rC:\Pro…

Vue 爬坑

都是基于最新的Vue3版本 "vue": "^3.4.29" 1 vue組建樣式設置 <script setup lang"ts"> import HelloWorld from ./components/HelloWorld.vue </script><template><div><a href"https://vitejs.dev" tar…

RPA 第一課

RPA 是 Robotic Process Automation 的簡稱&#xff0c;意思是「機器人流程自動化」。 顧名思義&#xff0c;它是一種以機器人&#xff08;軟件&#xff09;來替代人&#xff0c;實現重復工作自動化的工具。 首先要說一句&#xff0c;RPA 不是 ChatGPT 出來之后的產物&#x…

Ubuntu24.04安裝Skynet環境

安裝依賴 sudo apt-get -y install gcc sudo apt-get -y install g sudo apt-get -y install make sudo apt-get install -y autoconf automake libtool sudo apt-get install -y git 或者可以用&#xff1a; sudo apt-get -y install gcc g make autoconf automake libtool…

升級springboot3

坑爹的發版流水線&#xff0c;管天管地&#xff0c;springboot2過了維護期&#xff0c;就催著我們升級。 導致必須上jdk17 記錄一下升級需要處理的事情 先升級springboot和cloud&#xff0c;這里定下基調&#xff0c;其他的才好跟著升級 https://spring.io/projects/spring-b…

如何在Java中使用正則表達式

如何在Java中使用正則表達式 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01; 正則表達式&#xff08;Regular Expression&#xff0c;簡稱Regex或RegExp&#…

elementui中@click短時間內多次觸發,@click重復點擊,做不允許重復點擊處理

click快速點擊&#xff0c;發生多次觸發 2.代碼示例&#xff1a; //html<el-button :loading"submitLoading" type"primary" click"submitForm">確 定</el-button>data() {return {submitLoading:false,}}//方法/** 提交按鈕 */sub…

分布式鎖——基于Redis分布式鎖

單機鎖 服務器只有一個&#xff0c;JVM只有一個。 用synchronized加鎖&#xff0c;對lock對象加鎖&#xff0c;只有線程1結束&#xff0c;線程2,3才會開始。 再用uid避免一個線程多次進來。 分布式鎖 真正上線時&#xff1a; 【注&#xff1a;這些服務器連接的是一個Redis集…

STM32入門筆記(03): ADC(SPL庫函數版)(2)

A/D轉換的常用技術有逐次逼近式、雙積分式、并行式和跟蹤比較式等。目前用的較多的是前3種。 A/D轉換器的主要技術指標 轉換時間 分辨率 例如&#xff0c;8位A/D轉換器的數字輸出量的變化范圍為0&#xff5e;255&#xff0c;當輸入電壓的滿刻度為5V時&#xff0c;數字量每變化…

如何學好自動化測試

1. 什么是自動化測試 自動化測試是使用腳本和工具來執行測試任務&#xff0c;以替代手工測試過程。它可以提高效率、減少人工錯誤&#xff0c;并增加測試覆蓋率。在軟件開發過程中&#xff0c;自動化測試已經成為了不可或缺的一部分。 自動化測試主要有以下好處&#xff1a; …

Amos結構方程模型---探索性分析

初級 第5講 探索性分析_嗶哩嗶哩_bilibili amos中基本操作&#xff1a; 橢圓潛變量&#xff0c;不可預測 數據導入 改變形狀 判定系數 方差估計和假設檢驗&#xff1a; 探索性分析&#xff1a; ses&#xff08;潛變量&#xff09;社會經濟指數 從考慮最大的MI開始&#xff0c;卡…

【Python畫圖-馴化seaborn】一文搞懂seaborn中的箱線圖實踐技巧

【Python畫圖-馴化seaborn】一文搞懂seaborn中的箱線圖實踐技巧 本次修煉方法請往下查看 &#x1f308; 歡迎蒞臨我的個人主頁 &#x1f448;這里是我工作、學習、實踐 IT領域、真誠分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免費獲取相關內容文檔關注&a…

2736 卡片重排

題目描述 Description 可可共有兩種卡片&#xff0c;一種卡片是數字0-9編號&#xff0c;一種卡片是字母A-Z編號&#xff0c;現在兩種卡片混在一起&#xff0c;可可想將它們歸類擺放&#xff0c;但是要求同類卡片中&#xff0c;它們相對位置不可以改變&#xff0c;原先在前的仍…

python把項目編譯成so文件

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、使用步驟1.引入庫 總結 前言 提示 例如&#xff1a; 提示&#xff1a;以下是本篇文章正文內容&#xff0c;下面案例可供參考 一、使用步驟 1.引入庫 代…

C語言兩個較大數字相加

C語言兩個較大數字相加 思路分析 由于C語言中的基本數據類型&#xff08;如int、long等&#xff09;有固定的大小&#xff0c;無法直接處理非常大的數字&#xff08;如數百位的數字&#xff09;。因此&#xff0c;我們需要采用字符串或數組來表示大數字&#xff0c;并逐位進行…

1.4 ROS2集成開發環境搭建

1.4.1 安裝VSCode VSCode全稱Visual Studio Code&#xff0c;是微軟推出的一款輕量級代碼編輯器&#xff0c;免費、開源而且功能強大。它支持幾乎所有主流的程序語言的語法高亮、智能代碼補全、自定義熱鍵、括號匹配、代碼片段、代碼對比Diff、GIT 等特性&#xff0c;支持插件…

導入第三方包Could not find a package configuration file provided by “demo msgs“ with

報錯代碼 Could not find a package configuration file provided by "demo msgs" with any of the following names: demo msgsConfig.cmake demo msgs-config.cmake Add the installation prefix of "demo msgs" tO CMAKE PREFIX PATH or set "dem…

7.3數據庫第一次作業

安裝MySQL 1.打開安裝包 2.選擇自定義安裝&#xff08;custom&#xff09;并點擊下一步 3.自定義安裝路徑 4.點擊執行 5.執行成功 6.默認選項點擊下一步 7.選擇新的授權方式并點擊下一步 8.配置密碼 9.默認配置并點擊下一步 10.點擊執行&#xff08;Execute&#xff09; 11.執…