目录介绍

  • 01.前沿介绍
  • 02.handleLaunchActivity
  • 03.performLaunchActivity
  • 04.activity.attach
  • 05.Activity的onCreate方法
  • 06.setContentView
  • 07.关于一点总结

Activity一系列深度博客,挖掘activity从启动过程,到布局创建,以及绘制的过程。

  • 02.Activity基础介绍
  • 03.Activity启动流程
  • 04.Activity布局创建
  • 05.Activity布局绘制
  • 06.Activity探坑分析

吕诗禹想换个工作,渴望同行内推

  • 个人信息

    • 姓名:吕诗禹
    • 邮箱:17801164348@163.com
    • 微信:13940574490
    • GitHub:https://github.com/yangchong211
    • 目前工作情况:在职状态
    • 工作年限:4年
    • 工作地点:北京
  • 感谢同行朋友,如果可以,可以直接电话联系或者微信联系!

01.前沿介绍

  • 大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与android中另外一个很重要的组件Service最大的不同,但是这个展示的界面的功能是Activity直接控制的么?界面的布局文件是如何加载到内存并被Activity管理的?android中的View是一个怎样的概念?加载到内存中的布局文件是如何绘制出来的?
  • 其实Activity对界面布局的管理是都是通过Window对象来实现的,Window对象,顾名思义就是一个窗口对象,而Activity从用户角度就是一个个的窗口实例,因此不难想象每个Activity中都对应着一个Window对象,而这个Window对象就是负责加载显示界面的。至于window对象是如何展示不同的界面的,那是通过定义不同的View组件实现不同的界面展示。

02.handleLaunchActivity

  • 当ActivityManagerService接收到启动Activity的请求之后会通过IApplicationThread进程间通讯告知ApplicationThread并执行handleLauncherActivity方法,这里可以看一下其具体实现:

    • 可以发现这里的handleLauncherActivity方法内部调用了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 activityWindowManagerGlobal.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

  • 这个方法也是具体启动Activity的方法,我们来看一下它的具体实现逻辑:

    • 从代码中可以看到这里是通过反射的机制创建的Activity,并调用了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

  • 我们继续来看一下attach方法的实现逻辑:

    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对象,并且mWindow的成员实例是PhoneWindow实例,这样也从侧面说明了一个Activity对应着一个Window对象。除了window对象还初始化了一些Activity的其他成员变量,这里不再做讨论,继续回到我们的performLaunchActivity方法,在调用了Activity的attach方法之后又调用了:
    mInstrumentation.callActivityOnCreate(activity, r.state);
    
  • 这里的mInstrumentation是类Instrumentation,每个应用进程对应着一个Instrumentation和一个ActivityThread,Instrumentation就是具体操作Activity回调其生命周期方法的,我们这里看一下它的callActivityOnCreate方法的实现:
    public void callActivityOnCreate(Activity activity, Bundle icicle) {prePerformCreate(activity);activity.performCreate(icicle);postPerformCreate(activity);
    }
    
  • 这里代码比较简洁,preOerformCreate方法和postPerformCreate方法我们这里暂时不管,主要的执行逻辑是调用了activity.performCreate方法,我们来看一下Activity的performCreate方法的实现:
    final void performCreate(Bundle icicle) {onCreate(icicle);mActivityTransitionState.readState(icicle);performCreateCommon();
    }
    
  • 原来onCreate的生命周期方法是在这里回调的,其实这里的逻辑在前面几篇文章中有讲述,也可以参考前面的文章。

05.Activity的onCreate方法

  • 至此就回调到了我们Activity的onCreate方法,大家平时在重写onCreate方法的时候,怎么加载布局文件的呢?这里看一下我们的onCreate方法的典型写法:

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
    }
    
  • 无论我们怎么变化,我们的onCreate方法一般都是会调用这两句话的吧?那么这里的两段代码分辨是什么含义呢?我们首先看一下super.onCreate方法的实现逻辑,由于我们的Activity类继承与Activity,所以这里的super.onCreate方法,就是调用的Activity.onCreate方法,好吧,既然这样我们来看一下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;
    }
    
  • 可以发现,Activity的onCreate方法主要是做了一些Acitivty的初始化操作,那么如果我们不在自己的Activity调用super.onCreate方法呢?好吧,尝试之后,AndroidStudio在打开的Acitivty的onCreate方法中如果不调用super.onCreate方法的话,会报错。。。
    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)
    
  • 可以看到如果不调用super.onCreate方法的话,会在Activity的performLaunchActivity中报错,我们知道这里的performLaunchActivity方法就是我们启动Activity的时候回回调的方法,我们找找方法体实现中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方法中,我们在调用了Activity的onCreate方法之后会执行一个判断逻辑,若Activity的mCalled为false,则会抛出我们刚刚捕获的异常,那么这个mCalled成员变量是在什么时候被赋值的呢?好吧,就是在Activity的onCreate方法赋值的,所以我们在实现自己的Activity的时候只有调用了super.onCreate方法才不会抛出这个异常,反过来说,我们实现自己的Actiivty,那么一定要在onCreate方法中调用super.onCreate方法。

06.setContentView

  • 然后我们在看一下onCreate中的setContentView方法,这里的参数就是一个Layout布局文件,可以发现这里的setContentView方法就是Acitivty中的setContentView,好吧我们来看一下Activity中setContentView的实现:

    public void setContentView(@LayoutRes int layoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();
    }
    
  • 这里的getWindow方法就是获取Acitivty的mWindow成员变量,从刚刚我们在Activity.attach方法我们知道这里的mWindow的实例是PhoneWindow,所以这里调用的其实是PhoneWindow的setConentView方法,然后我们看一下PhoneWindow的setContentView是如何实现的。
    @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对象是一个View对象,由于第一次mContentParent为空,所以执行installerDector方法,这里我们看一下installerDector方法的具体实现:
    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是一个DectorView对象,而DectorView继承与FrameLayout,所以这里的mDector其实就是一个FrameLayout对象,并通过调用generateDector()方法初始化,我们继续看一下generateDector方法的具体实现:
    protected DecorView generateDecor() {return new DecorView(getContext(), -1);
    }
    
  • 就是通过new的方式创建了一个DectorView对象,然后我们继续看installDector方法:
    if (mContentParent == null) {mContentParent = generateLayout(mDecor);
    
  • 这里初始化了mContentParent对象,这是一个View对象,我们调用了generateLayout方法,好吧,来看一下generateLayout方法的具体实现:
    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 layoutif (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 layoutif (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;
    }
    
  • 可以发现这里就是通过调用LayoutInflater.inflate方法来加载布局文件到内存中,关于LayoutInflater.inflater是如何加载布局文件的,并且,通过对代码的分析,我们发现PhoneWindow中的几个成员变量:mDector,mContentRoot,mContentParent的关系
    mDector --> mContentRoot --> mContentParent(包含)

    • 并且我们来看一下典型的布局文件:
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:fitsSystemWindows="true"android:orientation="vertical"><ViewStub android:id="@+id/action_mode_bar_stub"android:inflatedId="@+id/action_mode_bar"android:layout="@layout/action_mode_bar"android:layout_width="match_parent"android:layout_height="wrap_content"android:theme="?attr/actionBarTheme" /><FrameLayoutandroid:id="@android:id/content"android:layout_width="match_parent"android:layout_height="match_parent"android:foregroundInsidePadding="false"android:foregroundGravity="fill_horizontal|top"android:foreground="?android:attr/windowContentOverlay" />
    </LinearLayout>
    
    • 这里就是整个Activity加载的跟布局文件:screen_simple.xml,其中ViewStub对应着Activity中的titleBar而这里的FrameLayout里面主要用于填充内容。
  • 然后我们具体看一下LayoutInflater.inflater方法:
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {return inflate(resource, root, root != null);
    }
    
  • 这里调用了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()+ ": No start tag found!");}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("<merge /> 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 xmlfinal 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 suppliedparams = 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;}
    }
    
  • 通过分析源码,不难发现,主要是通过循环解析xml文件并将信息解析到内存View对象,布局文件中定义的一个个组件都被顺序的解析到了内存中并被父子View的形式组织起来,这样通过给定的一个root View就可以将整个布局文件中定义的组件全部解析。分析完解析布局文件,回到我们的setContentVIew方法,在调用了installDector方法之后,又调用了:
    mLayoutInflater.inflate(layoutResID, mContentParent);
    
  • 这个方法的含义就是将我们传递的客户端的layoutId对应的布局文件作为mContentParent的子View加载到内存中,这样我们的layoutId作为mContentParent的子View,而mContentParent又是mContentRoot的子View,mContentRoot又是mDector的子View,通过LayoutInflater的inflate方法逐步加载到了内存中,而我们的Activity又持有自身的PhoneWindow的引用,这就相当于我们的Activity持有了我们定义的布局文件的引用,因而Activity的布局文件被加载到了内存中。

07.关于一点总结

  • 总结:

    • Activity的展示界面的特性是通过Window对象来控制的;
    • 每个Activity对象都对应这个一个Window对象,并且Window对象的初始化在启动Activity的时候完成,在执行Activity的onCreate方法之前;
    • 每个Window对象内部都存在一个FrameLayout类型的mDector对象,它是Acitivty界面的root view;
    • Activity中的window对象的实例是PhoneWindow对象,PhoneWindow对象中的几个成员变量mDector,mContentRoot,mContentParent都是View组件,它们的关系是:mDector --> mContentRoot --> mContentParent --> 自定义layoutView
    • LayoutInflater.inflate主要用于将布局文件加载到内存View组件中,也可以设定加载到某一个父组件中;
    • 典型的Activity的onCreate方法中需要调用super.onCreate方法和setContentView方法,若不调用super.onCreate方法,执行启动该Activity的逻辑会报错,若不执行setContentView的方法,该Activity只会显示一个空页面。

其他介绍

01.关于博客汇总链接

  • 1.技术博客汇总
  • 2.开源项目汇总
  • 3.生活博客汇总
  • 4.喜马拉雅音频汇总
  • 5.其他汇总

02.关于我的博客

  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yczbj/activities
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
  • segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
  • 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e

开源地址:https://github.com/yangchong211

Activity系列博客5篇相关推荐

  1. 追梦App系列博客——汇总篇

    文章目录 一.前言 二.为什么要写这个系列博客? 三.汇总篇的作用 四.导航目录 1.App设计 2.会议相关 五.建议 一.前言 当我们满怀希望跨入大学生活,却我们发现大学生活并不是我们所希冀的样子 ...

  2. 【DDR3 控制器设计】系列博客汇总篇(附直达链接)

    写在前面 本篇为 DDR3 控制器设计系列博客的汇总篇,此系列包含 DDR3 的相关实验:认识 MIG.初始化.读写操作.FIFO 接口等,并附上直达链接. 目前只更新了部分文章,持续学习更新中... ...

  3. 追梦App系列博客——理念篇

    文章目录 一.前言 二.项目灵感 三.追梦--追寻梦的起点 一.前言 当一个人只有空壳, 却没有一个有想法的灵魂: 当一个人浑浑噩噩地重复每日的工作, 却不知道这些工作的意义: 当一个人想有所改变, ...

  4. JavaWeb - 《黑马旅游网》系列博客及笔者源码传送门

    <黑马旅游网>系列博客及笔者源码传送门 系列博客 正篇 番外 笔者源码 系列博客 正篇 项目启动 用户注册 登录与退出 首页线路分类展示 旅游线路分页展示 旅游线路详情展示 线路搜索分页展 ...

  5. 大数据系列博客之 --- 深入简出 Shell 脚本语言(提升篇)

    首先声明,此系列shell系列博客分为四篇发布,分别是: 基础篇:https://www.cnblogs.com/lsy131479/p/9914747.html 提升篇:https://www.cn ...

  6. LINQ之路系列博客后记

    缘起 今年3月,我换了工作单位.后来多次收到公司的新人培训邮件,不过对此我并不感冒,说实话并不喜欢这种活动.印象中,新人培训无非是唠叨些公司的规章制度.侃述一下公司的光辉历史还有灿烂的未来发展等等.规 ...

  7. flutter text 自动换行_Flutter 系列博客——05 StatelessWidget vs StatefulWidget

    前言 上一篇我们对 Flutter UI 有了一个基本的了解. 这一篇我们通过自定义 Widget 来了解下如何写一个 Widget? 然而 Widget 有两个,StatelessWidget 和 ...

  8. Flutter 即学即用系列博客——09 MethodChannel 实现原生与 Flutter 通信(二)

    前言 上一篇我们讲解了如何通过 EventChannel 实现 Android -> Flutter 的通信. 并且也看到了 Flutter 内部 EventChannel 源码也是对 Meth ...

  9. flutter 返回指定界面_Flutter 即学即用系列博客——04 Flutter UI 初窥

    前面三篇可以算是一个小小的里程碑. 主要是介绍了 Flutter 环境的搭建.如何创建 Flutter 项目以及如何在旧有 Android 项目引入 Flutter. 这一篇我们来学习下 Flutte ...

最新文章

  1. [转载]IT知识体系结构图
  2. [ERR0134] Requested Service is not available【转载】
  3. python getopterror_python3 getopt用法
  4. 碰到日期题就怕的我来写一道水题吧
  5. 作者:洪文兴(1980-),男,厦门大学自动化系副教授,厦门信息产业与信息化研究院执行院长。...
  6. 阿里DRUID数据源
  7. 坐火车卧铺,到底是上、中、下哪个好?其实简单对比一下就知道了
  8. JParticles 2.0 发布,打造炫酷的粒子特效
  9. 使用canvas保存网页为pdf文件支持跨域
  10. 基于微信小程序的超市购物系统
  11. matlab的解线性方程组
  12. 用asp.net写的一个购物网站
  13. 如何设置自定义任务栏图标_如何在Windows中自定义和调整系统任务栏图标
  14. iPad/iPhone与电脑共享文件
  15. 2019年上半年 DDOS流量攻击分析以及解决方案
  16. oppo手机怎么关闭Android,OPPO手机必须关闭的3个设置,让你的手机永不卡顿,再用3年...
  17. 尚学堂-肖斌-100集hadoop视频教程
  18. 海盗湾联合创始人在瑞典被判入狱两年
  19. 海思PQTool进行CCM调试经验
  20. 各类炫酷二维码的制作(附带GUI工具开发)

热门文章

  1. UWB的三种算法详解
  2. 预算一万以内的机器学习台式机/主机配置推荐
  3. word,excel重难点问题解答
  4. 川土微电子 | 隔离电源的辐射抑制设计参考(三)
  5. 记录我的第一个Python项目
  6. J2ME移动开发平台搭建篇
  7. oracle截取6位,Oracle函数获取IDCARD中年龄
  8. distrowatch(distrowatch什么意思)
  9. 腾讯云服务器发布hexo
  10. linux test命令测试标志