日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android应用程序窗口(Activity)的视图对象(View)的创建过程分析

發布時間:2025/4/5 Android 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android应用程序窗口(Activity)的视图对象(View)的创建过程分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章轉載至CSDN社區羅升陽的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8245546

從前文可知道,每一個Activity組件都有一個關聯的Window對象,用來描述一個應用程序窗口。每一個應用程序窗口內部又包含有一個View對象,用來描述應用程序窗口的視圖。應用程序窗口視圖是真正用來實現UI內容和布局的,也就是說,每一個Activity組件的UI內容和布局都是通過與其所關聯的一個Window對象的內部的一個View對象來實現的。在本文中,我們就詳細分析應用程序窗口視圖的創建過程。

?? ? ? 在前面Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文中提到,應用程序窗口內部所包含的視圖對象的實際類型為DecorView。DecorView類繼承了View類,是作為容器(ViewGroup)來使用的,它的實現如圖1所示:


圖1 DecorView類的實現

?? ? ? ?這個圖的具體描述可以參考Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文中的圖5,這里不再詳述。

?? ? ? ?從前面Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文還可以知道,每一個應用程序窗口的視圖對象都有一個關聯的ViewRoot對象,這些關聯關系是由窗口管理器來維護的,如圖2所示:


圖2 應用程序窗口視圖與ViewRoot的關系圖

?? ? ? ?這個圖的具體描述可以參考Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文中的圖6,這里不再詳述。

?? ? ? ?簡單來說,ViewRoot相當于是MVC模型中的Controller,它有以下職責:

?? ? ? ?1. 負責為應用程序窗口視圖創建Surface。

?? ? ? ?2. 配合WindowManagerService來管理系統的應用程序窗口。

?? ? ? ?3. 負責管理、布局和渲染應用程序窗口視圖的UI。

?? ? ? ?那么,應用程序窗口的視圖對象及其所關聯的ViewRoot對象是什么時候開始創建的呢? 從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一 文可以知道,Activity組件在啟動的時候,系統會為它創建窗口對象(Window),同時,系統也會為這個窗口對象創建視圖對象。另一方面,當 Activity組件被激活的時候,系統如果發現與它的應用程序窗口視圖對象所關聯的ViewRoot對象還沒有創建,那么就會先創建這個 ViewRoot對象,以便接下來可以將它的UI渲染出來。

?? ? ? 從前面Android應用程序啟動過程源代碼分析一 文可以知道,Activity組件在啟動的過程中,會調用ActivityThread類的成員函數handleLaunchActivity,用來創建 以及首次激活Activity組件,因此,接下來我們就從這個函數開始,具體分析應用程序窗口的視圖對象及其所關聯的ViewRoot對象的創建過程,如 圖3所示:


圖3 應用程序窗口視圖的創建過程

?? ? ? ?這個過程一共可以分為13個步驟,接下來我們就詳細分析每一個步驟。

?? ? ? ?Step 1. ActivityThread.handleLaunchActivity

?

[java] view plaincopy
  • public?final?class?ActivityThread?{??
  • ????......??
  • ??
  • ????private?final?void?handleLaunchActivity(ActivityClientRecord?r,?Intent?customIntent)?{??
  • ????????......??
  • ??
  • ????????Activity?a?=?performLaunchActivity(r,?customIntent);??
  • ??
  • ????????if?(a?!=?null)?{??
  • ????????????......??
  • ??
  • ????????????handleResumeActivity(r.token,?false,?r.isForward);??
  • ??
  • ????????????......??
  • ????????}??
  • ??
  • ????????......??
  • ????}??
  • ??
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java文件中。

    ?? ? ? ?函數首先調用ActivityThread類的成員函數performLaunchActivity來創建要啟動的Activity組件。在創建 Activity組件的過程中,還會為該Activity組件創建窗口對象和視圖對象。Activity組件創建完成之后,就可以將它激活起來了,這是通 過調用ActivityThread類的成員函數handleResumeActivity來執行的。

    ?? ? ? ?接下來,我們首先分析ActivityThread類的成員函數performLaunchActivity的實現,以便可以了解應用程序窗口視圖對象 的創建過程,接著再回過頭來繼續分析ActivityThread類的成員函數handleResumeActivity的實現,以便可以了解與應用程序 窗口視圖對象所關聯的ViewRoot對象的創建過程。

    ?? ? ? ?Step 2.?ActivityThread.performLaunchActivity

    ?? ? ? ?這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java文件中。

    ?? ? ? ?這一步可以參考Android應用程序窗口(Activity)的運行上下文環境(Context)的創建過程分析一文的Step 1,它主要就是創建一個Activity組件實例,并且調用這個Activity組件實例的成員函數onCreate來讓其執行一些自定義的初始化工作。

    ?? ? ? ?Step 3.?Activity.onCreate

    ?? ? ? ?這個函數定義在文件frameworks/base/core/java/android/app/Activity.java中。

    ?? ? ? ?這一步可以參考Android應用程序窗口(Activity)的運行上下文環境(Context)的創建過程分析一文的Step 10。我們在實現一個Activity組件的時候,也就是在實現一個Activity子類的時候,一般都會重寫成員函數onCreate,以便可以執行一些自定義的初始化工作,其中就包含初始化UI的工作。例如,在前面在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務一文中,我們實現了一個名稱為Hello的Activity組件,用來測試硬件服務,它的成員函數onCreate的樣子長得大概如下所示:

    ?

    [java] view plaincopy
  • public?class?Hello?extends?Activity?implements?OnClickListener?{????
  • ????......????
  • ????????
  • ????/**?Called?when?the?activity?is?first?created.?*/????
  • ????@Override????
  • ????public?void?onCreate(Bundle?savedInstanceState)?{????
  • ????????super.onCreate(savedInstanceState);????
  • ????????setContentView(R.layout.main);????
  • ????
  • ????????......????
  • ????}????
  • ??
  • ????......??
  • }??
  • ?? ? ? 其中,調用從父類Activity繼承下來的成員函數setContentView就是用來創建應用程序窗口視圖對象的。

    ?? ? ? 接下來,我們就繼續分析Activity類的成員函數setContentView的實現。

    ?? ? ? Step 4.?Activity.setContentView

    ?

    [java] view plaincopy
  • public?class?Activity?extends?ContextThemeWrapper??
  • ????????implements?LayoutInflater.Factory,??
  • ????????Window.Callback,?KeyEvent.Callback,??
  • ????????OnCreateContextMenuListener,?ComponentCallbacks?{??
  • ????......??
  • ??
  • ????private?Window?mWindow;??
  • ????......??
  • ??
  • ????public?Window?getWindow()?{??
  • ????????return?mWindow;??
  • ????}??
  • ????......??
  • ??
  • ????public?void?setContentView(int?layoutResID)?{??
  • ????????getWindow().setContentView(layoutResID);??
  • ????}??
  • ??
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/core/java/android/app/Activity.java中。

    ?? ? ? ?Activity類的成員函數setContentView首先調用另外一個成員函數getWindow來獲得成員變量mWindow所描述的一個窗口 對象,接著再調用這個窗口對象的成員函數setContentView來執行創建應用程序窗口視圖對象的工作。

    ?? ? ? ?從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一文可以知道,Activity類的成員變量mWindow指向的是一個PhoneWindow對象,因此,接下來我們就繼續分析PhoneWindow類的成員函數setContentView的實現。

    ?? ? ? ?Step 5. PhoneWindow.setContentView

    ?

    [java] view plaincopy
  • public?class?PhoneWindow?extends?Window?implements?MenuBuilder.Callback?{??
  • ????......??
  • ??
  • ????//?This?is?the?view?in?which?the?window?contents?are?placed.?It?is?either??
  • ????//?mDecor?itself,?or?a?child?of?mDecor?where?the?contents?go.??
  • ????private?ViewGroup?mContentParent;??
  • ????......??
  • ??
  • ????@Override??
  • ????public?void?setContentView(int?layoutResID)?{??
  • ????????if?(mContentParent?==?null)?{??
  • ????????????installDecor();??
  • ????????}?else?{??
  • ????????????mContentParent.removeAllViews();??
  • ????????}??
  • ????????mLayoutInflater.inflate(layoutResID,?mContentParent);??
  • ????????final?Callback?cb?=?getCallback();??
  • ????????if?(cb?!=?null)?{??
  • ????????????cb.onContentChanged();??
  • ????????}??
  • ????}??
  • ??
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。

    ?? ? ? ?PhoneWindow類的成員變量mContentParent用來描述一個類型為DecorView的視圖對象,或者這個類型為DecorView 的視圖對象的一個子視圖對象,用作UI容器。當它的值等于null的時候,就說明正在處理的應用程序窗口的視圖對象還沒有創建。在這種情況下,就會調用成 員函數installDecor來創建應用程序窗口視圖對象。否則的話,就說明是要重新設置應用程序窗口的視圖。在重新設置之前,首先調用成員變量 mContentParent所描述的一個ViewGroup對象來移除原來的UI內空。

    ?? ? ? ?由于我們是在Activity組件啟動的過程中創建應用程序窗口視圖的,因此,我們就假設此時PhoneWindow類的成員變量 mContentParent的值等于null。接下來,函數就會調用成員函數installDecor來創建應用程序窗口視圖對象,接著再通過調用 PhoneWindow類的成員變量mLayoutInflater所描述的一個LayoutInflater對象的成員函數inflate來將參數 layoutResID所描述的一個UI布局設置到前面所創建的應用程序窗口視圖中去,最后還會調用一個Callback接口的成員函數 onContentChanged來通知對應的Activity組件,它的視圖內容發生改變了。從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一文可以知道,Activity組件自己實現了這個Callback接口,并且將這個Callback接口設置到了與它所關聯的應用程序窗口對象的內部去,因此,前面實際調用的是Activity類的成員函數onContentChanged來發出一個視圖內容變化通知。

    ?? ? ?接下來,我們就繼續分析PhoneWindow類的成員函數installDecor的實現,以便可以繼續了解應用程序窗口視圖對象的創建過程。

    ?? ? ?Step 6.?PhoneWindow.installDecor

    ?

    [java] view plaincopy
  • public?class?PhoneWindow?extends?Window?implements?MenuBuilder.Callback?{??
  • ????......??
  • ??
  • ????//?This?is?the?top-level?view?of?the?window,?containing?the?window?decor.??
  • ????private?DecorView?mDecor;??
  • ????......??
  • ??
  • ????//?This?is?the?view?in?which?the?window?contents?are?placed.?It?is?either??
  • ????//?mDecor?itself,?or?a?child?of?mDecor?where?the?contents?go.??
  • ????private?ViewGroup?mContentParent;??
  • ????......??
  • ??
  • ????private?TextView?mTitleView;??
  • ????......??
  • ??
  • ????private?CharSequence?mTitle?=?null;??
  • ????......??
  • ??
  • ????private?void?installDecor()?{??
  • ????????if?(mDecor?==?null)?{??
  • ????????????mDecor?=?generateDecor();??
  • ????????????......??
  • ????????}??
  • ????????if?(mContentParent?==?null)?{??
  • ????????????mContentParent?=?generateLayout(mDecor);??
  • ??
  • ????????????mTitleView?=?(TextView)findViewById(com.android.internal.R.id.title);??
  • ????????????if?(mTitleView?!=?null)?{??
  • ????????????????if?((getLocalFeatures()?&?(1?<<?FEATURE_NO_TITLE))?!=?0)?{??
  • ????????????????????View?titleContainer?=?findViewById(com.android.internal.R.id.title_container);??
  • ????????????????????if?(titleContainer?!=?null)?{??
  • ????????????????????????titleContainer.setVisibility(View.GONE);??
  • ????????????????????}?else?{??
  • ????????????????????????mTitleView.setVisibility(View.GONE);??
  • ????????????????????}??
  • ????????????????????if?(mContentParent?instanceof?FrameLayout)?{??
  • ????????????????????????((FrameLayout)mContentParent).setForeground(null);??
  • ????????????????????}??
  • ????????????????}?else?{??
  • ????????????????????mTitleView.setText(mTitle);??
  • ????????????????}??
  • ????????????}??
  • ????????}??
  • ????}??
  • ??
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。

    ?? ? ? ?由于我們是在Activity組件啟動的過程中創建應用程序窗口視圖的,因此,我們同時假設此時PhoneWindow類的成員變量mDecor的值等 于null。這時候PhoneWindow類的成員函數installDecor就會調用另外一個成員函數generateDecor來創建一個 DecorView對象,并且保存在PhoneWindow類的成員變量mDecor中。

    ?? ? ? ?PhoneWindow類的成員函數installDecor接著再調用另外一個成員函數generateLayout來根據當前應用程序窗口的 Feature來加載對應的窗口布局文件。這些布局文件保存在frameworks/base/core/res/res/layout目錄下,它們必須 包含有一個id值為“content”的布局控件。這個布局控件必須要從ViewGroup類繼承下來,用來作為窗口的UI容器。PhoneWindow 類的成員函數generateLayout執行完成之后,就會這個id值為“content”的ViewGroup控件來給PhoneWindow類的成 員函數installDecor,后者再將其保存在成員變量mContentParent中。

    ?? ? ? PhoneWindow類的成員函數installDecor還會檢查前面加載的窗口布局文件是否包含有一個id值為“title”的TextView控 件。如果包含有的話,就會將它保存在PhoneWindow類的成員變量mTitleView中,用來描述當前應用程序窗口的標題欄。但是,如果當前應用 程序窗口是沒有標題欄的,即它的Feature位FEATURE_NO_TITLE的值等于1,那么PhoneWindow類的成員函數 installDecor就需要將前面得到的標題欄隱藏起來。注意,PhoneWindow類的成員變量mTitleView所描述的標題欄有可能是包含 在一個id值為“title_container”的容器里面的,在這種情況下,就需要隱藏該標題欄容器。另一方面,如果當前應用程序窗口是設置有標題欄 的,那么PhoneWindow類的成員函數installDecor就會設置它的標題欄文字。應用程序窗口的標題欄文字保存在PhoneWindow類 的成員變量mTitle中,我們可以調用PhoneWindow類的成員函數setTitle來設置。

    ?? ? ? 這一步執行完成之后,應用程序窗口視圖就創建完成了,回到前面的Step 1中,即ActivityThread類的成員函數handleLaunchActivity中,接下來就會調用ActivityThread類的另外一 個成員函數handleResumeActivity來激活正在啟動的Activity組件。由于在是第一次激活該Activity組件,因此,在激活之 前,還會為該Activity組件創建一個ViewRoot對象,并且與前面所創建的應用程序窗口視圖關聯起來,以便后面可以通過該ViewRoot對象 來控制應用程序窗口視圖的UI展現。

    ?? ? ? 接下來,我們就繼續分析ActivityThread類的成員函數handleResumeActivity的實現。

    ?? ? ? Step 7.?ActivityThread.handleResumeActivity

    ?

    [java] view plaincopy
  • public?final?class?ActivityThread?{??
  • ????......??
  • ??
  • ????final?void?handleResumeActivity(IBinder?token,?boolean?clearHide,?boolean?isForward)?{??
  • ????????......??
  • ??
  • ????????ActivityClientRecord?r?=?performResumeActivity(token,?clearHide);??
  • ??
  • ????????if?(r?!=?null)?{??
  • ????????????final?Activity?a?=?r.activity;??
  • ????????????......??
  • ??
  • ????????????//?If?the?window?hasn't?yet?been?added?to?the?window?manager,??
  • ????????????//?and?this?guy?didn't?finish?itself?or?start?another?activity,??
  • ????????????//?then?go?ahead?and?add?the?window.??
  • ????????????boolean?willBeVisible?=?!a.mStartedActivity;??
  • ????????????if?(!willBeVisible)?{??
  • ????????????????try?{??
  • ????????????????????willBeVisible?=?ActivityManagerNative.getDefault().willActivityBeVisible(??
  • ????????????????????????????a.getActivityToken());??
  • ????????????????}?catch?(RemoteException?e)?{??
  • ????????????????}??
  • ????????????}??
  • ????????????if?(r.window?==?null?&&?!a.mFinished?&&?willBeVisible)?{??
  • ????????????????r.window?=?r.activity.getWindow();??
  • ????????????????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;??
  • ????????????????......??
  • ????????????????if?(a.mVisibleFromClient)?{??
  • ????????????????????a.mWindowAdded?=?true;??
  • ????????????????????wm.addView(decor,?l);??
  • ????????????????}??
  • ????????????}???
  • ??
  • ????????????......??
  • ????????}??
  • ??
  • ????????......??
  • ????}??
  • ????
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。

    ?? ? ? ?ActivityThread類的成員函數handleResumeActivity首先調用另外一個成員函數 performResumeActivity來通知Activity組件,它要被激活了,即會導致Activity組件的成員函數onResume被調 用。ActivityThread類的成員函數performResumeActivity的返回值是一個ActivityClientRecord對象 r,這個ActivityClientRecord對象的成員變量activity描述的就是正在激活的Activity組件a。

    ?? ? ? ?ActivityThread類的成員函數handleResumeActivity接下來判斷正在激活的Activity組件接下來是否是可見的。如 果是可見的,那么變量willBeVisible的值就會等于true。Activity類的成員變量mStartedActivity用來描述一個 Activity組件是否正在啟動一個新的Activity組件,并且等待這個新的Activity組件的執行結果。如果是的話,那么這個 Activity組件的成員變量mStartedActivity的值就會等于true,表示在新的Activity組件的執行結果返回來之前,當前 Activity組件要保持不可見的狀態。因此,當Activity組件a的成員變量mStartedActivity的值等于true的時候,它接下來 就是不可見的,否則的話,就是可見的。

    ?? ? ? ?雖然說在Activity組件a的成員變量mStartedActivity的值等于true的情況下,它接下來的狀態要保持不可見的,但是有可能它所 啟動的Activity組件的UI不是全屏的。在這種情況下,Activity組件a的UI仍然是有部分可見的,這時候也要將變量 willBeVisible的值設置為true。因此,如果前面得到變量willBeVisible的值等于false,那么 ActivityThread類的成員函數handleResumeActivity接下來就會通過Binder進程間通信機制來調用 ActivityManagerService服務的成員函數willActivityBeVisible來檢查位于Activity組件a上面的其它 Activity組件(包含了Activity組件a正在等待其執行結果的Activity組件)是否是全屏的。如果不是,那么 ActivityManagerService服務的成員函數willActivityBeVisible的返回值就會等于true,表示接下來需要顯示 Activity組件a。

    ?? ? ? 前面得到的ActivityClientRecord對象r的成員變量window用來描述當前正在激活的Activity組件a所關聯的應用程序窗口對 象。當它的值等于null的時候,就表示當前正在激活的Activity組件a所關聯的應用程序窗口對象還沒有關聯一個ViewRoot對象。進一步地, 如果這個正在激活的Activity組件a還活著,并且接下來是可見的,即ActivityClientRecord對象r的成員變量mFinished 的值等于false,并且前面得到的變量willBeVisible的值等于true,那么這時候就說明需要為與Activity組件a所關聯的一個應用 程序窗口視圖對象關聯的一個ViewRoot對象。

    ?? ? ? 將一個Activity組件的應用程序窗口視圖對象與一個ViewRoot對象關聯是通過該Activity組件所使用的窗口管理器來執行的。從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一 文可以知道,一個Activity組件所使用的本地窗口管理器保存它的成員變量mWindowManager中,這可以通過Activity類的成員函數 getWindowManager來獲得。在接下來的Step 10中,我們再分析Activity類的成員函數getWindowManager的實現。

    ?? ? ? 由于我們現在要給Activity組件a的應用程序窗口視圖對象關聯一個ViewRoot對象,因此,我們就需要首先獲得這個應用程序窗口視圖對象。從前 面的Step 6可以知道,一個Activity組件的應用程序窗口視圖對象保存在與其所關聯的一個應用程序窗口對象的內部,因此,我們又要首先獲得這個應用程序窗口對 象。與一個Activity組件所關聯的應用程序窗口對象可以通過調用該Activity組件的成員函數getWindow來獲得。一旦獲得了這個應用程 序窗口對象(類型為PhoneWindow)之后,我們就可以調用它的成員函數getDecorView來獲得它內部的視圖對象。在接下來的Step 8和Step 9中,我們再分別分析Activity類的成員函數Activity類的成員函數getWindow和PhoneWindow類的成員函數 getDecorView的實現。

    ?? ? ?在關聯應用程序窗口視圖對象和ViewRoot對象的時候,還需要第三個參數,即應用程序窗口的布局參數,這是一個類型為 WindowManager.LayoutParams的對象,可以通過調用應用程序窗口的成員函數getAttributes來獲得。一切準備就緒之 后,還要判斷最后一個條件是否成立,即當前正在激活的Activity組件a在本地進程中是否是可見的,即它的成員變量 mVisibleFromClient的值是否等于true。如果是可見的,那么最后就可以調用前面所獲得的一個本地窗口管理器wm(類型為 LocalWindowManager)的成員函數addView來執行關聯應用程序窗口視圖對象和ViewRoot對象的操作。

    ?? ? 接下來,我們就分別分析Activity類的成員函數getWindow、PhoneWindow類的成員函數getDecorView、ctivity 類的成員函數getWindowManager以及LocalWindowManager類的成員函數addView的實現。

    ?? ? Step 8.?Activity.getWindow

    [java] view plaincopy
  • public?class?Activity?extends?ContextThemeWrapper??
  • ????????implements?LayoutInflater.Factory,??
  • ????????Window.Callback,?KeyEvent.Callback,??
  • ????????OnCreateContextMenuListener,?ComponentCallbacks?{??
  • ????......??
  • ??
  • ????private?Window?mWindow;??
  • ????......??
  • ??
  • ????public?Window?getWindow()?{??
  • ????????return?mWindow;??
  • ????}??
  • ??
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/core/java/android/app/Activity.java中。

    ?? ? ? ?從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一文可以知道,Activity類的成員變量mWindow指向的是一個類型為PhoneWindow的窗口對象,因此,Activity類的成員函數getWindow返回給調用者的是一個PhoneWindow對象。

    ?? ? ? ?這一步執完成之后,返回到前面的Step 7中,即ActivityThread類的成員函數handleResumeActivity中,接下來就會繼續調用前面所獲得的一個 PhoneWindow對象的成員函數getDecorView來獲得當前正在激活的Activity組件所關聯的一個應用程序窗口視圖對象。

    ?? ? ? ?Step 9.?PhoneWindow.getDecorView

    [java] view plaincopy
  • public?class?PhoneWindow?extends?Window?implements?MenuBuilder.Callback?{??
  • ????......??
  • ??
  • ????private?DecorView?mDecor;??
  • ????......??
  • ??
  • ????@Override??
  • ????public?final?View?getDecorView()?{??
  • ????????if?(mDecor?==?null)?{??
  • ????????????installDecor();??
  • ????????}??
  • ????????return?mDecor;??
  • ????}??
  • ??
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。

    ?? ? ? ?PhoneWindow類的成員函數getDecorView首先判斷成員變量mDecor的值是否等于null。如果是的話,那么就說明當前正在處理 的應用程序窗口還沒有創建視圖對象。這時候就會調用另外一個成員函數installDecor來創建這個視圖對象。從前面的調用過程可以知道,當前正在處 理的應用程序窗口已經創建過視圖對象,因此,這里的成員變量mDecor的值不等于null,PhoneWindow類的成員函數 getDecorView直接將它返回給調用者。

    ?? ? ? ?這一步執完成之后,返回到前面的Step 7中,即ActivityThread類的成員函數handleResumeActivity中,接下來就會繼續調用當前正在激活的Activity組件 的成員函數getWindowManager來獲得一個本地窗口管理器。

    ?? ? ? ?Step 10.?Activity.getWindowManager

    [java] view plaincopy
  • public?class?Activity?extends?ContextThemeWrapper??
  • ????????implements?LayoutInflater.Factory,??
  • ????????Window.Callback,?KeyEvent.Callback,??
  • ????????OnCreateContextMenuListener,?ComponentCallbacks?{??
  • ????......??
  • ??
  • ????private?WindowManager?mWindowManager;??
  • ????......??
  • ??
  • ????public?WindowManager?getWindowManager()?{??
  • ????????return?mWindowManager;??
  • ????}??
  • ??
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/core/java/android/app/Activity.java中。

    ?? ? ? ?從前面Android應用程序窗口(Activity)的運行上下文環境(Context)的創建過程分析一文可以知道,Activity類的成員變量mWindowManager指向的一是類型為LocalWindowManager的本地窗口管理器,Activity類的成員函數getWindowManager直接將它返回給調用者。

    ?? ? ? ?這一步執完成之后,返回到前面的Step 7中,即ActivityThread類的成員函數handleResumeActivity中,接下來就會繼續調用前面所獲得的一個 LocalWindowManager對象的成員函數addView來為當前正在激活的Activity組件的應用程序窗口視圖對象關聯一個 ViewRoot對象。

    ?? ? ? ?Step 11.?LocalWindowManager.addView

    [java] view plaincopy
  • public?abstract?class?Window?{??
  • ????......??
  • ??
  • ????private?class?LocalWindowManager?implements?WindowManager?{??
  • ????????......??
  • ??
  • ????????public?final?void?addView(View?view,?ViewGroup.LayoutParams?params)?{??
  • ????????????......??
  • ??
  • ????????????mWindowManager.addView(view,?params);??
  • ????????}??
  • ??
  • ????????......??
  • ??
  • ????????private?final?WindowManager?mWindowManager;??
  • ???
  • ????????......??
  • ????}??
  • ??
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/core/java/android/view/Window.java中。

    ?? ? ? ?從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一 文可以知道,LocalWindowManager類的成員變量mWindowManager指向的是一個WindowManagerImpl對象,因 此,LocalWindowManager類的成員函數addView接下來調用WindowManagerImpl類的成員函數addView來給參數 view所描述的一個應用程序窗口視圖對象關聯一個ViewRoot對象。

    ?? ? ? ?Step 12.?WindowManagerImpl.addView

    [java] view plaincopy
  • public?class?WindowManagerImpl?implements?WindowManager?{??
  • ????......??
  • ??
  • ????public?void?addView(View?view,?ViewGroup.LayoutParams?params)??
  • ????{??
  • ????????addView(view,?params,?false);??
  • ????}??
  • ??
  • ????......??
  • ??
  • ????private?void?addView(View?view,?ViewGroup.LayoutParams?params,?boolean?nest)??
  • ????{??
  • ????????......??
  • ??
  • ????????final?WindowManager.LayoutParams?wparams??
  • ????????????????=?(WindowManager.LayoutParams)params;??
  • ??
  • ????????ViewRoot?root;??
  • ????????View?panelParentView?=?null;??
  • ??
  • ????????synchronized?(this)?{??
  • ????????????//?Here's?an?odd/questionable?case:?if?someone?tries?to?add?a??
  • ????????????//?view?multiple?times,?then?we?simply?bump?up?a?nesting?count??
  • ????????????//?and?they?need?to?remove?the?view?the?corresponding?number?of??
  • ????????????//?times?to?have?it?actually?removed?from?the?window?manager.??
  • ????????????//?This?is?useful?specifically?for?the?notification?manager,??
  • ????????????//?which?can?continually?add/remove?the?same?view?as?a??
  • ????????????//?notification?gets?updated.??
  • ????????????int?index?=?findViewLocked(view,?false);??
  • ????????????if?(index?>=?0)?{??
  • ????????????????if?(!nest)?{??
  • ????????????????????throw?new?IllegalStateException("View?"?+?view??
  • ????????????????????????????+?"?has?already?been?added?to?the?window?manager.");??
  • ????????????????}??
  • ????????????????root?=?mRoots[index];??
  • ????????????????root.mAddNesting++;??
  • ????????????????//?Update?layout?parameters.??
  • ????????????????view.setLayoutParams(wparams);??
  • ????????????????root.setLayoutParams(wparams,?true);??
  • ????????????????return;??
  • ????????????}??
  • ??
  • ????????????//?If?this?is?a?panel?window,?then?find?the?window?it?is?being??
  • ????????????//?attached?to?for?future?reference.??
  • ????????????if?(wparams.type?>=?WindowManager.LayoutParams.FIRST_SUB_WINDOW?&&??
  • ????????????????????wparams.type?<=?WindowManager.LayoutParams.LAST_SUB_WINDOW)?{??
  • ????????????????final?int?count?=?mViews?!=?null???mViews.length?:?0;??
  • ????????????????for?(int?i=0;?i<count;?i++)?{??
  • ????????????????????if?(mRoots[i].mWindow.asBinder()?==?wparams.token)?{??
  • ????????????????????????panelParentView?=?mViews[i];??
  • ????????????????????}??
  • ????????????????}??
  • ????????????}??
  • ??
  • ????????????root?=?new?ViewRoot(view.getContext());??
  • ????????????root.mAddNesting?=?1;??
  • ??
  • ????????????view.setLayoutParams(wparams);??
  • ??
  • ????????????if?(mViews?==?null)?{??
  • ????????????????index?=?1;??
  • ????????????????mViews?=?new?View[1];??
  • ????????????????mRoots?=?new?ViewRoot[1];??
  • ????????????????mParams?=?new?WindowManager.LayoutParams[1];??
  • ????????????}?else?{??
  • ????????????????index?=?mViews.length?+?1;??
  • ????????????????Object[]?old?=?mViews;??
  • ????????????????mViews?=?new?View[index];??
  • ????????????????System.arraycopy(old,?0,?mViews,?0,?index-1);??
  • ????????????????old?=?mRoots;??
  • ????????????????mRoots?=?new?ViewRoot[index];??
  • ????????????????System.arraycopy(old,?0,?mRoots,?0,?index-1);??
  • ????????????????old?=?mParams;??
  • ????????????????mParams?=?new?WindowManager.LayoutParams[index];??
  • ????????????????System.arraycopy(old,?0,?mParams,?0,?index-1);??
  • ????????????}??
  • ????????????index--;??
  • ??
  • ????????????mViews[index]?=?view;??
  • ????????????mRoots[index]?=?root;??
  • ????????????mParams[index]?=?wparams;??
  • ????????}??
  • ????????//?do?this?last?because?it?fires?off?messages?to?start?doing?things??
  • ????????root.setView(view,?wparams,?panelParentView);??
  • ????}??
  • ??
  • ????......??
  • ??
  • ????private?View[]?mViews;??
  • ????private?ViewRoot[]?mRoots;??
  • ????private?WindowManager.LayoutParams[]?mParams;??
  • ??
  • ????......??
  • }??
  • ?? ? ? 這個函數定義在文件frameworks/base/core/java/android/view/WindowManagerImpl.java中。

    ?? ? ? 在WindowManagerImpl類中,兩個參數版本的成員函數addView是通過調用三個參數版本的成同函數addView來實現的,因此,我們接下來就主要分析三個參數版本的成員函數addView的實現。

    ?? ? ? 在分析WindowManagerImpl類的三個參數版本的成員函數addView的實現之前,我們首先了解一下WindowManagerImpl類 是如何關聯一個應用程序窗口視圖對象(View對象)和一個ViewRoot對象的。一個View對象在與一個ViewRoot對象關聯的同時,還會關聯 一個WindowManager.LayoutParams對象,這個WindowManager.LayoutParams對象是用來描述應用程序窗口 視圖的布局屬性的。

    ?? ? ??WindowManagerImpl類有三個成員變量mViews、mRoots和mParams,它們分別是類型為View、ViewRoot和 WindowManager.LayoutParams的數組。這三個數組的大小是始終保持相等的。這樣, 有關聯關系的View對象、ViewRoot對象和WindowManager.LayoutParams對象就會分別保存在數組mViews、 mRoots和mParams的相同位置上,也就是說,mViews[i]、mRoots[i]和mParams[i]所描述的View對象、 ViewRoot對象和WindowManager.LayoutParams對象是具有關聯關系的。因此,WindowManagerImpl類的三個 參數版本的成員函數addView在關聯一個View對象、一個ViewRoot對象和一個WindowManager.LayoutParams對象的 時候,只要分別將它們放在數組mViews、mRoots和mParams的相同位置上就可以了。

    ?? ? ? 理解了一個View對象、一個ViewRoot對象和一個WindowManager.LayoutParams對象是如何關聯之后,WindowManagerImpl類的三個參數版本的成員函數addView的實現就容易理解了。

    ?? ? ? 參數view和參數params描述的就是要關聯的View對象和WindowManager.LayoutParams對象。成員函數addView首 先調用另外一個成員函數findViewLocked來檢查參數view所描述的一個View對象是否已經存在于數組中mViews中了。如果已經存在的 話,那么就說明該View對象已經關聯過ViewRoot對象以及WindowManager.LayoutParams對象了。在這種情況下,如果參數 nest的值等于false,那么成員函數addView是不允許重復對參數view所描述的一個View對象進行重新關聯的。另一方面,如果參數 nest的值等于true,那么成員函數addView只是重新修改參數view所描述的一個View對象及其所關聯的一個ViewRoot對象內部使用 的一個WindowManager.LayoutParams對象,即更新為參數params所描述的一個 WindowManager.LayoutParams對象,這是通過調用它們的成員函數setLayoutParams來實現的。

    ?? ? ? 如果參數view所描述的一個View對象還沒有被關聯過一個ViewRoot對象,那么成員函數addView就會創建一個ViewRoot對象,并且 將它與參數view和params分別描述的一個View對象和一個WindowManager.LayoutParams對象保存在數組mViews、 mRoots和mParams的相同位置上。注意,如果數組mViews、mRoots和mParams尚未創建,那么成員函數addView就會首先分 別為它們創建一個大小為1的數組,以便可以用來分別保存所要關聯的View對象、ViewRoot對象和 WindowManager.LayoutParams對象。另一方面,如果數組mViews、mRoots和mParams已經創建,那么成員函數 addView就需要分別將它們的大小增加1,以便可以在它們的末尾位置上分別保存所要關聯的View對象、ViewRoot對象和 WindowManager.LayoutParams對象。

    ?? ? ? 還有另外一個需要注意的地方是當參數view描述的是一個子應用程序窗口的視圖對象時,即WindowManager.LayoutParams對象 wparams的成員變量type的值大于等于WindowManager.LayoutParams.FIRST_SUB_WINDOW并且小于等于 WindowManager.LayoutParams.LAST_SUB_WINDOW時,那么成員函數addView還需要找到這個子視圖對象的父視 圖對象panelParentView,這是通過遍歷數組mRoots來查找的。首先,WindowManager.LayoutParams對象 wparams的成員變量token指向了一個類型為W的Binder本地對象的一個IBinder接口,用來描述參數view所描述的一個子應用程序窗 口視圖對象所屬的父應用程序窗口視圖對象。其次,每一個ViewRoot對象都通過其成員變量mWindow來保存一個類型為W的Binder本地對象, 因此,如果在數組mRoots中,存在一個ViewRoot對象,它的成員變量mWindow所描述的一個W對象的一個IBinder接口等于 WindowManager.LayoutParams對象wparams的成員變量token所描述的一個IBinder接口時,那么就說明與該 ViewRoot對象所關聯的View對象即為參數view的父應用程序窗口視圖對象。

    ?? ? ? ?成員函數addView為參數view所描述的一個View對象和參數params所描述的一個WindowManager.LayoutParams 對象關聯好一個ViewRoot對象root之后,最后還會將這個View對view象和這個WindowManager.LayoutParams對 象,以及變量panelParentView所描述的一個父應用程序窗視圖對象,保存在這個ViewRoot對象root的內部去,這是通過調用這個 ViewRoot對象root的成員函數setView來實現的,因此,接下來我們就繼續分析ViewRoot類的成員函數setView的實現。

    ?? ? ? ?Step 13. ViewRoot.setView

    [java] view plaincopy
  • public?final?class?ViewRoot?extends?Handler?implements?ViewParent,??
  • ????????View.AttachInfo.Callbacks?{??
  • ????......??
  • ??
  • ????final?WindowManager.LayoutParams?mWindowAttributes?=?new?WindowManager.LayoutParams();??
  • ????......??
  • ??
  • ????View?mView;??
  • ????......??
  • ??
  • ????final?View.AttachInfo?mAttachInfo;??
  • ????......??
  • ??
  • ????boolean?mAdded;??
  • ????......??
  • ??
  • ????public?void?setView(View?view,?WindowManager.LayoutParams?attrs,??
  • ????????????View?panelParentView)?{??
  • ????????synchronized?(this)?{??
  • ????????????if?(mView?==?null)?{??
  • ????????????????mView?=?view;??
  • ????????????????mWindowAttributes.copyFrom(attrs);??
  • ????????????????......??
  • ??
  • ????????????????mAttachInfo.mRootView?=?view;??
  • ????????????????.......??
  • ??
  • ????????????????if?(panelParentView?!=?null)?{??
  • ????????????????????mAttachInfo.mPanelParentWindowToken??
  • ????????????????????????????=?panelParentView.getApplicationWindowToken();??
  • ????????????????}??
  • ????????????????mAdded?=?true;??
  • ????????????????......??
  • ??
  • ????????????????requestLayout();??
  • ????????????????......??
  • ????????????????try?{??
  • ????????????????????res?=?sWindowSession.add(mWindow,?mWindowAttributes,??
  • ????????????????????????????getHostVisibility(),?mAttachInfo.mContentInsets,??
  • ????????????????????????????mInputChannel);??
  • ????????????????}?catch?(RemoteException?e)?{??
  • ????????????????????mAdded?=?false;??
  • ????????????????????mView?=?null;??
  • ????????????????????......??
  • ????????????????????throw?new?RuntimeException("Adding?window?failed",?e);??
  • ????????????????}?finally?{??
  • ????????????????????if?(restore)?{??
  • ????????????????????????attrs.restore();??
  • ????????????????????}??
  • ????????????????}??
  • ??
  • ????????????????......??
  • ????????????}??
  • ??
  • ????????????......??
  • ????????}??
  • ????}??
  • ??
  • ????......??
  • }??
  • ?? ? ? ?這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java中。

    ?? ? ? ?參數view所描述的一個View對象會分別被保存在ViewRoot類的成員變量mView以及成員變量mAttachInfo所描述的一個 AttachInfo的成員變量mRootView中,而參數attrs所描述的一個WindowManager.LayoutParams對象的內容會 被拷貝到ViewRoot類的成員變量mWindowAttributes中去。

    ?? ? ? 當參數panelParentView的值不等于null的時候,就表示參數view描述的是一個子應用程序窗口視圖對象。在這種情況下,參數 panelParentView描述的就是一個父應用程序窗口視圖對象。這時候我們就需要獲得用來描述這個父應用程序窗口視圖對象的一個類型為W的 Binder本地對象的IBinder接口,以便可以保存在ViewRoot類的成員變量mAttachInfo所描述的一個AttachInfo的成員 變量mPanelParentWindowToken中去。這樣以后就可以知道ViewRoot類的成員變量mView所描述的一個子應用程序窗口視圖所 屬的父應用程序窗口視圖是什么了。注意,通過調用參數panelParentView的所描述的一個View對象的成員函數 getApplicationWindowToken即可以獲得一個對應的W對象的IBinder接口。

    ?? ? ? 上述操作執行完成之后,ViewRoot類的成員函數setView就可以將成員變量mAdded的值設置為true了,表示當前正在處理的一個 ViewRoot對象已經關聯好一個View對象了。接下來,ViewRoot類的成員函數setView還需要執行兩個操作:

    ?? ? ? 1. 調用ViewRoot類的另外一個成員函數requestLayout來請求對應用程序窗口視圖的UI作第一次布局。

    ?? ? ? 2. 調用ViewRoot類的靜態成員變量sWindowSession所描述的一個類型為Session的Binder代理對象的成員函數add來請求 WindowManagerService增加一個WindowState對象,以便可以用來描述當前正在處理的一個ViewRoot所關聯的一個應用程 序窗口。

    ?? ? ? 至此,我們就分析完成Android應用程序窗口視圖對象的創建過程了。在接下來的一篇文章中,我們將會繼續分析Android應用程序窗口與 WindowManagerService服務的連接過程,即Android應用程序窗口請求WindowManagerService為其增加一個 WindowState對象的過程,而在接下來的兩篇文章中,我們還會分析用來渲染Android應用程序窗口的Surface的創建過程,以及 Android應用程序窗口的渲染過程。通過這三個過程的分析,我們就可以看到上述第1點和第2點提到的兩個函數的執行過程,敬請期待!

    老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!

    總結

    以上是生活随笔為你收集整理的Android应用程序窗口(Activity)的视图对象(View)的创建过程分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。