Android繪制流程窗口啟動(dòng)流程分析(中)

  首先要關(guān)注的就是preserviedWindow參數(shù),這個(gè)參數(shù)就是上一段中提到的mPendingRevomeWindow變量,這個(gè)參數(shù)在什么時(shí)候會(huì)不為空呢?其實(shí)這里的邏輯是用來快速重啟acitivity的,比如你的一個(gè)activity已經(jīng)啟動(dòng)了,但是主題換了或者configuration變了,這里只需要重新加載資源和view,沒必重新再執(zhí)行DecorView的創(chuàng)建工作。

  另一個(gè)要關(guān)注的就是mDecor變量,這個(gè)變量是DecorView類型的,如果這里沒有初始化的話,則會(huì)在調(diào)用setContentView方法中new一個(gè)DecorView對象出來。DecorView對象繼承自FrameLayout,所以他本質(zhì)上還是一個(gè)view,只是對FrameLayout做了一定的包裝,例如添加了一些與window需要調(diào)用的方法setWindowBackground()、setWindowFrame()等。我們知道,acitivty界面的view是呈樹狀結(jié)構(gòu)的,而mDecor變量在這里作為activity的界面的根view存在。這三個(gè)點(diǎn)關(guān)系就比如,PhoneWindow是一塊手機(jī)電子屏,DecorView就是電子屏要顯示的內(nèi)容,Activity就是手機(jī)電子屏安裝位置。

  再來看創(chuàng)建PhonewWindow之后調(diào)用的setWindowManager()方法的邏輯,這段代碼是在PhonewWindow.java的父類Window.java中代碼如下。

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,

? ? ? ? ? ? boolean hardwareAccelerated) {

? ? ? ? mAppToken = appToken;

? ? ? ? mAppName = appName;

? ? ? ? mHardwareAccelerated = hardwareAccelerated

? ? ? ? ? ? ? ? || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);

? ? ? ? if (wm == null) {

? ? ? ? ? ? wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

? ? ? ? }

? ? ? ? mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

? ? }


對于wWindowManager變量,實(shí)際上這里是創(chuàng)建了一個(gè)WindowManagerImpl對象。首先是這種首先獲取系統(tǒng)服務(wù)的代理到wm上,然后強(qiáng)制轉(zhuǎn)換為WindowManagerImpl調(diào)用createLocalWindowManager(),在createLocalWindowManager()實(shí)際是執(zhí)行了一個(gè)new WindowManagerImpl()到方法來創(chuàng)建。關(guān)于這部分代碼看了很久很困惑的一個(gè)點(diǎn),就是為啥要弄個(gè)這么復(fù)雜的邏輯,直接把上面加粗的代碼改為new WindowManagerImpl(...),這養(yǎng)會(huì)有什么區(qū)別嗎?如果有大蝦看到這里,希望能幫我解答。

  在WindowManager中保存了對于單例對象WindowManagerGloble的引用,即mGlobal變量。此外,WindowManager.java實(shí)現(xiàn)了WindowManager又,而WindowManager繼承自ViewManager接口,ViewManager接口方法如下方代碼。

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對于addView()、updateViewLayout()和removeView()的實(shí)現(xiàn),都是調(diào)用到mGlobal變量對應(yīng)的addView()、updateViewLayout()和removeView()方法來實(shí)現(xiàn)的。這里我們

這樣我們分析完activity以及對應(yīng)的window對象的創(chuàng)建,回到performLauncerActivity()方法中Activity a = performLaunchActivity(r, customIntent)這一步驟,之后便回調(diào)activity方法的onCreate(),在onCreate()的setContentView方法會(huì)初始化DecorView,并根據(jù)傳入?yún)?shù)加載布局,詳細(xì)步驟在下一節(jié)介紹。

  再回到最初的handlerLaunchActivity()方法中,通過調(diào)用performLauncerActivity()創(chuàng)建出一個(gè)Acitivty對象后,如果創(chuàng)建成功則執(zhí)行handleResumeActivity(),便執(zhí)行到了Acitivity的onResume()方法,即是完成了acitivty的啟動(dòng)。


二、setContentView()流程

  首先,我們一般在onCreate()里調(diào)用setContentView()的方法。

@override

? ? protectedvoid onCreate(Bundle savedInstanceState) {

? ?   super.onCreate(savedInstanceState);

? ?   setContentView(R.layout.activity_main);

? ? }

這里實(shí)際調(diào)用到到地方是Acitivity.java類中的setContentView()方法,如下。

publicvoidsetContentView(@LayoutResint layoutResID) {

? ?   getWindow().setContentView(layoutResID);

? ?   initWindowDecorActionBar();

? ? }

  這里getWindow()返回的是Acitivity.java類中的mWindow變量,就是Activity創(chuàng)建時(shí)一起創(chuàng)建的PhoneWindow對象,進(jìn)入到

? ? @Override

? ? publicvoidsetContentView(int layoutResID) {if(mContentParent ==null) {installDecor();}elseif(!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {? ? ? ? ? ? mContentParent.removeAllViews();//如果多次調(diào)用setContentView則會(huì)執(zhí)行removeAllView操作}if(hasFeature(FEATURE_CONTENT_TRANSITIONS)) {//過渡動(dòng)畫機(jī)制相關(guān)

? ? ? ? ? ? finalScene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,

? ? ? ? ? ? ? ? ? ? getContext());

? ? ? ? ? ? transitionTo(newScene);

? ? ? ? } else{mLayoutInflater.inflate(layoutResID, mContentParent);}? ? ? ? mContentParent.requestApplyInsets();finalCallback cb = getCallback();

? ? ? ? if(cb !=null&& !isDestroyed()) {

? ? ? ? ? ? cb.onContentChanged();

? ? ? ? }

? ? ? ? mContentParentExplicitlySet =true;

? ? }

  代碼里涉及到FEATURE_CONTENT_TRANSITIONS的屬性,這里是Android的過渡動(dòng)畫相關(guān)機(jī)制,這里我們不再展開詳述。一般的Acitivty啟動(dòng)時(shí),會(huì)進(jìn)入mContentParent為null的邏輯,首先調(diào)用的是installDecor()方法,完成DecorView的創(chuàng)建工作;之后調(diào)用mLayoutInflater.inflate()方法將我們傳入的資源文件轉(zhuǎn)換為view樹,裝載到mContentParent中。首先來看installDecor()代碼。

privatevoid installDecor() {

? ? ? ? mForceDecorInstall =false;

? ? ? ? if(mDecor ==null) {//創(chuàng)建DecorViewmDecor= generateDecor(-1);mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);? ? ? ? ? ? mDecor.setIsRootNamespace(true);

? ? ? ? ? ? if(!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {

? ? ? ? ? ? ? ? mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);

? ? ? ? ? ? }

? ? ? ? } else {

? ? ? ? ? ? mDecor.setWindow(this);

? ? ? ? }

? ? ? ? if(mContentParent ==null) {mContentParent = generateLayout(mDecor);...? ? }

  在這個(gè)方法又兩個(gè)主要步驟,首先是使用generateDecor()方法創(chuàng)建了DecorView對象,generateDecor()方法比較簡單,主要就是執(zhí)行new DecorView(context, featureId, this, getAttributes())方法,這里不再貼出代碼;重點(diǎn)來看generateLayout()方法,這個(gè)方法生成的mContentParent是作為來我們后續(xù)加載加載的用戶的布局的父布局存在的。


protected ViewGroup generateLayout(DecorView decor) {

? ? ? ? // Apply data from current theme.

     //獲取當(dāng)前主題的相關(guān)屬性

? ? ? ? TypedArray a = getWindowStyle();

? ? ? ? ...? //一大段的根據(jù)獲取到到主題屬性,解析保存到PhonwWindow的相關(guān)參數(shù)的變量中

? ? ? ? int layoutResource;

? ? ? ? int features = getLocalFeatures();

? ? ? ?      ... //一大段根據(jù)PhoneWindow的設(shè)定好的屬性(features和mIsFloating)的判斷,為layoutResource進(jìn)行賦值,? ? ? ? ? ? //值可以為R.layout.screen_custom_title、R.layout.screen_action_bar等

? ? ? ? mDecor.startChanging();     //將layoutRsourece值對應(yīng)的布局文件加載到DecorView中

? ? ? ? mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

     //在加載給DecorView的布局文件中有一塊id為ID_ANDROID_CONTENT的區(qū)域是用于用戶顯示自己布局的,也是setContextView傳入的布局顯示的地方     //這塊區(qū)域會(huì)以ViewGroup的形式賦值給mContentParent變量,這個(gè)ViewGroup即是用戶布局的父布局節(jié)點(diǎn)

? ? ? ? ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

? ? ? ?      ... //繼續(xù)一大段的屬性配置

? ? ? ? mDecor.finishChanging();

? ? ? ? return contentParent;

? ? }


 generateLayout方法實(shí)際就是解析出主題的相關(guān)屬性,根據(jù)不同的主題樣式的屬性值選擇不同的布局文件設(shè)置到DecorView中(DecorView本事就是FrameLayout)。在view的樹狀結(jié)構(gòu)下,DecorView即是整個(gè)Window顯示的視圖的根節(jié)點(diǎn),在DecorView的子節(jié)點(diǎn)中又有一塊id為ID_ANDROID_CONTENT的區(qū)域有一塊區(qū)域作為mContentParent變量用于加載用戶的布局,與mContentParent平級(jí)的視圖有ActionBar視圖和Title的視圖??偨Y(jié)來說,installDecor()方法實(shí)質(zhì)就是產(chǎn)生mDecor和mContentParent對象。在installDecor之后,會(huì)執(zhí)行到mLayoutInflater.inflate(layoutResID, mContentParent)方法將用戶傳入的布局轉(zhuǎn)化為view再加入到mContentParent上。這樣就完成了setContentView()流程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容