標籤:檔案 fill 比較 成員變數 重寫 tin exception rip 一個
1.引言
最近準備重新學習下Android,加深理解,快速形成自己的知識結構體系。最先學習的就算View部分,從自訂View到Activty階層,到layout載入過程。等等都會看一遍,在此記錄下Layout的載入過程
2.正題2.1 Activity的流程載入
Activity類中setContentView 追蹤(善於用bookMark)
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
追蹤代碼,PhoneWindow的setContentView方法:
image.png
mLayoutInflater.inflate(layoutResId,mContentParent)
由程式碼片段可以看出來:layoutResId的父布局就是mContentParent。那麼mContentParent究竟是什麼呢?
我們知道DecorView 包括TitleView +ContentView.
image.png
追蹤代碼:installDescor():
image.png
generateLayout()方法:
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. // 獲得表單的 style 樣式 TypedArray a = getWindowStyle(); // 省略大量無關代碼 // Inflate the window decor. int layoutResource; int features = getLocalFeatures(); //填充帶有 style 和 feature 屬性的 layoutResource (是一個layout id) View in = mLayoutInflater.inflate(layoutResource, null); // 插入的頂層布局 DecorView 中 decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; // 找到我們XML檔案的父布局 contentParent ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn‘t find content container view"); } // 省略無關代碼 mDecor.finishChanging(); // 返回 contentParent 並賦值給成員變數 mContentParent return contentParent;}
給DecorView添加一個名字為mContentRoot的view。並且mContentRoot裡麵包含了一個id為ID_ANDROID_CONTENT的 mContentParent.
得到了mContentParent之後,在根據mLayoutInflater.inflate(layoutResId,mContentParent) 就將Layout布局載入進了DecorView,然後這個視圖才能被我們看到。
驗證:
image.png
說明了mContentParent是FramLayout
3.AppCompatActivity 載入View得過程
@Override public void setContentView(@LayoutRes int layoutResID) { getDelegate().setContentView(layoutResID); }
getDelegate():
@NonNull public AppCompatDelegate getDelegate() { if (mDelegate == null) { mDelegate = AppCompatDelegate.create(this, this); } return mDelegate; }
跟蹤create()方法,
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) { return create(activity, activity.getWindow(), callback); }
activity.getWindow() 由上面可知,得到的肯定是PhoneWindow。問題來了,activity這個時候真的能得到一個非null的PhoneWindow嗎?。發現在activity調用attach方法的時候就會初始化PhoneWindow:
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) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(this); ......}
Activity.attach是在performLaunchActivity中本調用,也就是在ActivityThread中被調用。所以在Activity中任何時候getWindow(),都能得到PhoneWindow。
繼續跟蹤代碼:
create方法調用的是:
private static AppCompatDelegate create(Context context, Window window, AppCompatCallback callback) { if (Build.VERSION.SDK_INT >= 24) { return new AppCompatDelegateImplN(context, window, callback); } else if (Build.VERSION.SDK_INT >= 23) { return new AppCompatDelegateImplV23(context, window, callback); } else if (Build.VERSION.SDK_INT >= 14) { return new AppCompatDelegateImplV14(context, window, callback); } else if (Build.VERSION.SDK_INT >= 11) { return new AppCompatDelegateImplV11(context, window, callback); } else { return new AppCompatDelegateImplV9(context, window, callback); } }
setContentView 是在AppCompatDelegateImplV9類中被重寫,也就是說最終是調用AppCompatDelegateImplV9.setContentView();
@Override public void setContentView(int resId) { ensureSubDecor(); ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content); contentParent.removeAllViews(); LayoutInflater.from(mContext).inflate(resId, contentParent); mOriginalWindowCallback.onContentChanged(); }
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
很容易讓我們想到mSubDecor 是不是等同於上文中的mContentRoot 呢?
看看ensureSubDecor怎麼寫的:
if (mOverlayActionMode) { subDecor = (ViewGroup) inflater.inflate( R.layout.abc_screen_simple_overlay_action_mode, null); } else { subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null); }
subDecor 是系統提供的ViewGroup 裡面有一個android.R.id.content布局,和mContentRoot 作用一模一樣。
基本上分析到此結束,也是比較簡單。
Android View之布局載入流程