摘要:通過(guò)分析源碼,不難發(fā)現(xiàn),主要是通過(guò)循環(huán)解析文件并將信息解析到內(nèi)存對(duì)象,布局文件中定義的一個(gè)個(gè)組件都被順序的解析到了內(nèi)存中并被父子的形式組織起來(lái),這樣通過(guò)給定的一個(gè)就可以將整個(gè)布局文件中定義的組件全部解析。
目錄介紹
01.前沿介紹
02.handleLaunchActivity
03.performLaunchActivity
04.activity.attach
05.Activity的onCreate方法
06.setContentView
07.關(guān)于一點(diǎn)總結(jié)
Activity一系列深度博客,挖掘activity從啟動(dòng)過(guò)程,到布局創(chuàng)建,以及繪制的過(guò)程。02.Activity基礎(chǔ)介紹
03.Activity啟動(dòng)流程
04.Activity布局創(chuàng)建
05.Activity布局繪制
06.Activity探坑分析
呂詩(shī)禹想換個(gè)工作,渴望同行內(nèi)推
個(gè)人信息
姓名:呂詩(shī)禹
郵箱:17801164348@163.com
微信:13940574490
GitHub:https://github.com/yangchong211
目前工作情況:在職狀態(tài)
工作年限:4年
工作地點(diǎn):北京
感謝同行朋友,如果可以,可以直接電話聯(lián)系或者微信聯(lián)系!
01.前沿介紹大家都知道在Android體系中Activity扮演了一個(gè)界面展示的角色,這也是它與android中另外一個(gè)很重要的組件Service最大的不同,但是這個(gè)展示的界面的功能是Activity直接控制的么?界面的布局文件是如何加載到內(nèi)存并被Activity管理的?android中的View是一個(gè)怎樣的概念?加載到內(nèi)存中的布局文件是如何繪制出來(lái)的?
其實(shí)Activity對(duì)界面布局的管理是都是通過(guò)Window對(duì)象來(lái)實(shí)現(xiàn)的,Window對(duì)象,顧名思義就是一個(gè)窗口對(duì)象,而Activity從用戶角度就是一個(gè)個(gè)的窗口實(shí)例,因此不難想象每個(gè)Activity中都對(duì)應(yīng)著一個(gè)Window對(duì)象,而這個(gè)Window對(duì)象就是負(fù)責(zé)加載顯示界面的。至于window對(duì)象是如何展示不同的界面的,那是通過(guò)定義不同的View組件實(shí)現(xiàn)不同的界面展示。
02.handleLaunchActivity
當(dāng)ActivityManagerService接收到啟動(dòng)Activity的請(qǐng)求之后會(huì)通過(guò)IApplicationThread進(jìn)程間通訊告知ApplicationThread并執(zhí)行handleLauncherActivity方法,這里可以看一下其具體實(shí)現(xiàn):
可以發(fā)現(xiàn)這里的handleLauncherActivity方法內(nèi)部調(diào)用了performLaunchActivity方法。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); mSomeActivitiesChanged = true; if (r.profilerInfo != null) { mProfiler.setProfiler(r.profilerInfo); mProfiler.startProfiling(); } // Make sure we are running with the most recent config. handleConfigurationChanged(null, null); if (localLOGV) Slog.v( TAG, "Handling launch of " + r); // Initialize before creating the activity WindowManagerGlobal.initialize(); Activity a = performLaunchActivity(r, customIntent); if (a != null) { r.createdConfig = new Configuration(mConfiguration); Bundle oldState = r.state; handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed); if (!r.activity.mFinished && r.startsNotResumed) { // The activity manager actually wants this one to start out // paused, because it needs to be visible but isn"t in the // foreground. We accomplish this by going through the // normal startup (because activities expect to go through // onResume() the first time they run, before their window // is displayed), and then pausing it. However, in this case // we do -not- need to do the full pause cycle (of freezing // and such) because the activity manager assumes it can just // retain the current state it has. try { r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity); // We need to keep around the original state, in case // we need to be created again. But we only do this // for pre-Honeycomb apps, which always save their state // when pausing, so we can not have them save their state // when restarting from a paused state. For HC and later, // we want to (and can) let the state be saved as the normal // part of stopping the activity. if (r.isPreHoneycomb()) { r.state = oldState; } if (!r.activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onPause()"); } } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to pause activity " + r.intent.getComponent().toShortString() + ": " + e.toString(), e); } } r.paused = true; } } else { // If there was an error, for any reason, tell the activity // manager to stop us. try { ActivityManagerNative.getDefault() .finishActivity(r.token, Activity.RESULT_CANCELED, null, false); } catch (RemoteException ex) { // Ignore } } }03.performLaunchActivity
這個(gè)方法也是具體啟動(dòng)Activity的方法,我們來(lái)看一下它的具體實(shí)現(xiàn)邏輯:
從代碼中可以看到這里是通過(guò)反射的機(jī)制創(chuàng)建的Activity,并調(diào)用了Activity的attach方法,那么這里的attach方法是做什么的呢?
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } ... Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (localLOGV) Slog.v(TAG, "Performing launch of " + r); if (localLOGV) Slog.v( TAG, r + ": app=" + app + ", appName=" + app.getPackageName() + ", pkg=" + r.packageInfo.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() + ", dir=" + r.packageInfo.getAppDir()); if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); 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); ... return activity; }04.activity.attach
我們繼續(xù)來(lái)看一下attach方法的實(shí)現(xiàn)邏輯:
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) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); } mUiThread = Thread.currentThread(); mMainThread = aThread; mInstrumentation = instr; mToken = token; mIdent = ident; mApplication = application; mIntent = intent; mReferrer = referrer; mComponent = intent.getComponent(); mActivityInfo = info; mTitle = title; mParent = parent; mEmbeddedID = id; mLastNonConfigurationInstances = lastNonConfigurationInstances; if (voiceInteractor != null) { if (lastNonConfigurationInstances != null) { mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor; } else { mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this, Looper.myLooper()); } } mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config; }
可以看到在attach方法這里初始化了一些Activity的成員變量,主要是mWindow對(duì)象,并且mWindow的成員實(shí)例是PhoneWindow實(shí)例,這樣也從側(cè)面說(shuō)明了一個(gè)Activity對(duì)應(yīng)著一個(gè)Window對(duì)象。除了window對(duì)象還初始化了一些Activity的其他成員變量,這里不再做討論,繼續(xù)回到我們的performLaunchActivity方法,在調(diào)用了Activity的attach方法之后又調(diào)用了:
mInstrumentation.callActivityOnCreate(activity, r.state);
這里的mInstrumentation是類Instrumentation,每個(gè)應(yīng)用進(jìn)程對(duì)應(yīng)著一個(gè)Instrumentation和一個(gè)ActivityThread,Instrumentation就是具體操作Activity回調(diào)其生命周期方法的,我們這里看一下它的callActivityOnCreate方法的實(shí)現(xiàn):
public void callActivityOnCreate(Activity activity, Bundle icicle) { prePerformCreate(activity); activity.performCreate(icicle); postPerformCreate(activity); }
這里代碼比較簡(jiǎn)潔,preOerformCreate方法和postPerformCreate方法我們這里暫時(shí)不管,主要的執(zhí)行邏輯是調(diào)用了activity.performCreate方法,我們來(lái)看一下Activity的performCreate方法的實(shí)現(xiàn):
final void performCreate(Bundle icicle) { onCreate(icicle); mActivityTransitionState.readState(icicle); performCreateCommon(); }
原來(lái)onCreate的生命周期方法是在這里回調(diào)的,其實(shí)這里的邏輯在前面幾篇文章中有講述,也可以參考前面的文章。
05.Activity的onCreate方法
至此就回調(diào)到了我們Activity的onCreate方法,大家平時(shí)在重寫onCreate方法的時(shí)候,怎么加載布局文件的呢?這里看一下我們的onCreate方法的典型寫法:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }
無(wú)論我們?cè)趺醋兓覀兊膐nCreate方法一般都是會(huì)調(diào)用這兩句話的吧?那么這里的兩段代碼分辨是什么含義呢?我們首先看一下super.onCreate方法的實(shí)現(xiàn)邏輯,由于我們的Activity類繼承與Activity,所以這里的super.onCreate方法,就是調(diào)用的Activity.onCreate方法,好吧,既然這樣我們來(lái)看一下Activity的onCreate方法:
protected void onCreate(@Nullable Bundle savedInstanceState) { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState); if (mLastNonConfigurationInstances != null) { mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders); } if (mActivityInfo.parentActivityName != null) { if (mActionBar == null) { mEnableDefaultActionBarUp = true; } else { mActionBar.setDefaultDisplayHomeAsUpEnabled(true); } } if (savedInstanceState != null) { Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); mFragments.restoreAllState(p, mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.fragments : null); } mFragments.dispatchCreate(); getApplication().dispatchActivityCreated(this, savedInstanceState); if (mVoiceInteractor != null) { mVoiceInteractor.attachActivity(this); } mCalled = true; }
可以發(fā)現(xiàn),Activity的onCreate方法主要是做了一些Acitivty的初始化操作,那么如果我們不在自己的Activity調(diào)用super.onCreate方法呢?好吧,嘗試之后,AndroidStudio在打開的Acitivty的onCreate方法中如果不調(diào)用super.onCreate方法的話,會(huì)報(bào)錯(cuò)。。。
FATAL EXCEPTION: main Process: com.example.aaron.helloworld, PID: 18001 android.util.SuperNotCalledException: Activity {com.example.aaron.helloworld/com.example.aaron.helloworld.SecondActivity} did not call through to super.onCreate() at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2422) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2528) at android.app.ActivityThread.access$800(ActivityThread.java:169) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1421) at android.os.Handler.dispatchMessage(Handler.java:111) at android.os.Looper.loop(Looper.java:194) at android.app.ActivityThread.main(ActivityThread.java:5552) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)
可以看到如果不調(diào)用super.onCreate方法的話,會(huì)在Activity的performLaunchActivity中報(bào)錯(cuò),我們知道這里的performLaunchActivity方法就是我們啟動(dòng)Activity的時(shí)候回回調(diào)的方法,我們找找方法體實(shí)現(xiàn)中throws的Exception。。。
activity.mCalled = false; if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); }
在Activity的performLaunchActivity方法中,我們?cè)谡{(diào)用了Activity的onCreate方法之后會(huì)執(zhí)行一個(gè)判斷邏輯,若Activity的mCalled為false,則會(huì)拋出我們剛剛捕獲的異常,那么這個(gè)mCalled成員變量是在什么時(shí)候被賦值的呢?好吧,就是在Activity的onCreate方法賦值的,所以我們?cè)趯?shí)現(xiàn)自己的Activity的時(shí)候只有調(diào)用了super.onCreate方法才不會(huì)拋出這個(gè)異常,反過(guò)來(lái)說(shuō),我們實(shí)現(xiàn)自己的Actiivty,那么一定要在onCreate方法中調(diào)用super.onCreate方法。
06.setContentView
然后我們?cè)诳匆幌耾nCreate中的setContentView方法,這里的參數(shù)就是一個(gè)Layout布局文件,可以發(fā)現(xiàn)這里的setContentView方法就是Acitivty中的setContentView,好吧我們來(lái)看一下Activity中setContentView的實(shí)現(xiàn):
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
這里的getWindow方法就是獲取Acitivty的mWindow成員變量,從剛剛我們?cè)贏ctivity.attach方法我們知道這里的mWindow的實(shí)例是PhoneWindow,所以這里調(diào)用的其實(shí)是PhoneWindow的setConentView方法,然后我們看一下PhoneWindow的setContentView是如何實(shí)現(xiàn)的。
@Override public 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) { installDecor(); } 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); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
這里的mContentParent對(duì)象是一個(gè)View對(duì)象,由于第一次mContentParent為空,所以執(zhí)行installerDector方法,這里我們看一下installerDector方法的具體實(shí)現(xiàn):
private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } ... }
這里的mDector是一個(gè)DectorView對(duì)象,而DectorView繼承與FrameLayout,所以這里的mDector其實(shí)就是一個(gè)FrameLayout對(duì)象,并通過(guò)調(diào)用generateDector()方法初始化,我們繼續(xù)看一下generateDector方法的具體實(shí)現(xiàn):
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
就是通過(guò)new的方式創(chuàng)建了一個(gè)DectorView對(duì)象,然后我們繼續(xù)看installDector方法:
if (mContentParent == null) { mContentParent = generateLayout(mDecor);
這里初始化了mContentParent對(duì)象,這是一個(gè)View對(duì)象,我們調(diào)用了generateLayout方法,好吧,來(lái)看一下generateLayout方法的具體實(shí)現(xiàn):
protected ViewGroup generateLayout(DecorView decor) { ... // Inflate the window decor. int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { layoutResource = R.layout.screen_swipe_dismiss; } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_title_icons; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = R.layout.screen_progress; // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_custom_title; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { // If no other features and not embedded, only need a title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar); } else { layoutResource = R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); } mDecor.startChanging(); View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn"t find content container view"); } if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { ProgressBar progress = getCircularProgressBar(false); if (progress != null) { progress.setIndeterminate(true); } } if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { registerSwipeCallbacks(); } // Remaining setup -- of background and title -- that only applies // to top-level windows. if (getContainer() == null) { final Drawable background; if (mBackgroundResource != 0) { background = getContext().getDrawable(mBackgroundResource); } else { background = mBackgroundDrawable; } mDecor.setWindowBackground(background); final Drawable frame; if (mFrameResource != 0) { frame = getContext().getDrawable(mFrameResource); } else { frame = null; } mDecor.setWindowFrame(frame); mDecor.setElevation(mElevation); mDecor.setClipToOutline(mClipToOutline); if (mTitle != null) { setTitle(mTitle); } if (mTitleColor == 0) { mTitleColor = mTextColor; } setTitleColor(mTitleColor); } mDecor.finishChanging(); return contentParent; }
可以發(fā)現(xiàn)這里就是通過(guò)調(diào)用LayoutInflater.inflate方法來(lái)加載布局文件到內(nèi)存中,關(guān)于LayoutInflater.inflater是如何加載布局文件的,并且,通過(guò)對(duì)代碼的分析,我們發(fā)現(xiàn)PhoneWindow中的幾個(gè)成員變量:mDector,mContentRoot,mContentParent的關(guān)系
mDector --> mContentRoot --> mContentParent(包含)
- 并且我們來(lái)看一下典型的布局文件: `````` - 這里就是整個(gè)Activity加載的跟布局文件:screen_simple.xml,其中ViewStub對(duì)應(yīng)著Activity中的titleBar而這里的FrameLayout里面主要用于填充內(nèi)容。
然后我們具體看一下LayoutInflater.inflater方法:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) { return inflate(resource, root, root != null); }
這里調(diào)用了inflate的重載方法。。。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final Context inflaterContext = mContext; final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = inflaterContext; View result = root; try { // Look for the root node. int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty } if (type != XmlPullParser.START_TAG) { throw new InflateException(parser.getPositionDescription()
} final String name = parser.getName(); if (DEBUG) { System.out.println("**************************"); System.out.println("Creating root view: " + name); System.out.println("**************************"); } if (TAG_MERGE.equals(name)) { if (root == null || !attachToRoot) { throw new InflateException("can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, inflaterContext, attrs, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { if (DEBUG) { System.out.println("Creating params from root: " + root); } // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) temp.setLayoutParams(params); } } if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); } // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } } } catch (XmlPullParserException e) { InflateException ex = new InflateException(e.getMessage()); ex.initCause(e); throw ex; } catch (Exception e) { InflateException ex = new InflateException( parser.getPositionDescription() + ": " + e.getMessage()); ex.initCause(e); throw ex; } finally { // Don"t retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; } Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result; } } ```
通過(guò)分析源碼,不難發(fā)現(xiàn),主要是通過(guò)循環(huán)解析xml文件并將信息解析到內(nèi)存View對(duì)象,布局文件中定義的一個(gè)個(gè)組件都被順序的解析到了內(nèi)存中并被父子View的形式組織起來(lái),這樣通過(guò)給定的一個(gè)root View就可以將整個(gè)布局文件中定義的組件全部解析。分析完解析布局文件,回到我們的setContentVIew方法,在調(diào)用了installDector方法之后,又調(diào)用了:
mLayoutInflater.inflate(layoutResID, mContentParent);
這個(gè)方法的含義就是將我們傳遞的客戶端的layoutId對(duì)應(yīng)的布局文件作為mContentParent的子View加載到內(nèi)存中,這樣我們的layoutId作為mContentParent的子View,而mContentParent又是mContentRoot的子View,mContentRoot又是mDector的子View,通過(guò)LayoutInflater的inflate方法逐步加載到了內(nèi)存中,而我們的Activity又持有自身的PhoneWindow的引用,這就相當(dāng)于我們的Activity持有了我們定義的布局文件的引用,因而Activity的布局文件被加載到了內(nèi)存中。
07.關(guān)于一點(diǎn)總結(jié)
總結(jié):
Activity的展示界面的特性是通過(guò)Window對(duì)象來(lái)控制的;
每個(gè)Activity對(duì)象都對(duì)應(yīng)這個(gè)一個(gè)Window對(duì)象,并且Window對(duì)象的初始化在啟動(dòng)Activity的時(shí)候完成,在執(zhí)行Activity的onCreate方法之前;
每個(gè)Window對(duì)象內(nèi)部都存在一個(gè)FrameLayout類型的mDector對(duì)象,它是Acitivty界面的root view;
Activity中的window對(duì)象的實(shí)例是PhoneWindow對(duì)象,PhoneWindow對(duì)象中的幾個(gè)成員變量mDector,mContentRoot,mContentParent都是View組件,它們的關(guān)系是:mDector --> mContentRoot --> mContentParent --> 自定義layoutView
LayoutInflater.inflate主要用于將布局文件加載到內(nèi)存View組件中,也可以設(shè)定加載到某一個(gè)父組件中;
典型的Activity的onCreate方法中需要調(diào)用super.onCreate方法和setContentView方法,若不調(diào)用super.onCreate方法,執(zhí)行啟動(dòng)該Activity的邏輯會(huì)報(bào)錯(cuò),若不執(zhí)行setContentView的方法,該Activity只會(huì)顯示一個(gè)空頁(yè)面。
其他介紹 01.關(guān)于博客匯總鏈接1.技術(shù)博客匯總
2.開源項(xiàng)目匯總
3.生活博客匯總
4.喜馬拉雅音頻匯總
5.其他匯總
02.關(guān)于我的博客github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡(jiǎn)書:http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽書:http://www.ximalaya.com/zhubo...
開源中國(guó):https://my.oschina.net/zbj161...
泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...
郵箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV
segmentfault頭條:https://segmentfault.com/u/xi...
掘金:https://juejin.im/user/593943...
開源地址:https://github.com/yangchong211文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/75568.html
摘要:使用實(shí)現(xiàn)功能運(yùn)行期注解案例使用簡(jiǎn)單的注解,便可以設(shè)置布局,等效于使用實(shí)現(xiàn)路由綜合型案例比較全面的介紹從零起步,一步一步封裝簡(jiǎn)易的路由開源庫(kù)。申明注解用的就是。返回值表示這個(gè)注解里可以存放什么類型值。 YCApt關(guān)于apt方案實(shí)踐與總結(jié) 目錄介紹 00.注解系列博客匯總 01.什么是apt 02.annotationProcessor和apt區(qū)別 03.項(xiàng)目目錄結(jié)構(gòu) 04.該案例作用 ...
閱讀 3584·2021-11-04 16:06
閱讀 3579·2021-09-09 11:56
閱讀 846·2021-09-01 11:39
閱讀 896·2019-08-29 15:28
閱讀 2294·2019-08-29 15:18
閱讀 830·2019-08-29 13:26
閱讀 3333·2019-08-29 13:22
閱讀 1046·2019-08-29 12:18