首先要關(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()流程。