一、Fragment知识总结
1.1 正常流程的生命周期分析
Activity和Fragment生命周期关联如下图所示:
在Android3.0之前,只有Activity这个组件承担了所有视图逻辑的工作量,并且每次系统启动Activity都会经过AMS,来完成对Activity的管理(这个后文总结)。从上图可知,Fragment和Activity的生命周期息息相关,可以看做一个微型的Activity。下面对Fragment生命周期方法的介绍:
- onAttach 在Fragment和Activity建立关联,Activity调用此方法内的。
- onCreateView 当Fragment创建视图调用。
- onActivityCreated 在相关联的Activity的onCreate()方法已返回时调用。
- onDestroyView 当Fragment中的视图被移除时调用。
- onDetach 当Fragment和Activity取消关联时调用。
下面一个例子来介绍Activity和Fragment生命周期变化的先后过程:
-
打开界面:
Fragment::onCreate() -> Fragment::onCreateView()->Activity::onCreate()->Fragment::onActivityCreated()->Activity::onStart()->Fragment::onStart()->Activity::onResume()->Fragment::onResume()
-
按下Home键:
Fragment::onPause()->Activity::onPause()
-
重新打开界面:
Activity::onRestart()->Activity::onStart()->Fragment::onStart()->Activity::onResume()->Fragment::onResume()
-
按后退键:
Fragment::onPause()->Activity::onPause()->Fragment::onStop()->Activity::onStop()->Fragment::onDestroyView()->Fragment::onDestroy()->Fragment::onDetach()->Activity::onDestroy()
从正常流程的生命周期方法可以看出,Fragment生命周期是由Activity来掌控的,并且是一致的,这也很好理解。所以像Google新推出的Jetpack组件LifeCycle就是使用FragmentManager添加了一个ReportFragment来实现对视图生命周期的监听,当然很早开源框架Glide就已经使用了这种方式来监听视图的生命周期来实现对图片资源的回收和释放。
1.2 replace/hide/show方法生命周期分析
-
采用replace方法替换,并加入回退栈
private void replaceFragment(int containerID, Fragment fragment) { FragmentManager fm = getSupportFragmentManager(); FragmentTransaction transaction = fm.beginTransaction(); transaction.replace(containerID, fragment); transaction.addToBackStack(null); transaction.commit(); }
这里用两个Fragment来代替,第一个是FirstFragment,第二个是SecondFragment,以此调用顺序如下:
-------------------------------------------------replace之前-------------------------------------------------
FirstFragment::onAttach()->FirstFragment::onCreate()->FirstFragment::onCreateView()->onViewCreated()->
onActivityCreated()->onViewStateRestored()->onResume()
-------------------------------------------------replace之后-------------------------------------------------
SecondFragment::onAttach()->SecondFragment::onCreate()->FirstFragment::onPause()->FirstFragment::onStop()->FirstFragment::onDestroyView->SecondFragment::onCreateView()->SecondFragment->onViewCreated()->SecondFragment::onActivityCreated()->SecondFragment::onViewStateRestored->SecondFragment::onStart()->SecondFragment::onResume()
-------------------------------------------------replace回来-------------------------------------------------
SecondFragment::onPause()->SecondFragment::onStop()->SecondFragment::onDestroyView()->FirstFragment::onCreateView()->FirstFragment::onViewCreated()->FirstFragment::onActivityCreated()->FirstFragment::onViewStateRestored()->FirstFragment::onResume()
-
采用replace方法替换,不加入回退栈
private void replaceFragment(int containerID, Fragment fragment) { FragmentManager fm = getSupportFragmentManager(); FragmentTransaction transaction = fm.beginTransaction(); transaction.replace(containerID, fragment); // transaction.addToBackStack(null); transaction.commit();
总结:
在replace之前生命周期方法调用和加入回退栈一样,在replace之后,FirstFragment在销毁视图时候还依次调用onDestroy()->onDetach()方法,replace回来之后和加入回退栈也一样。说明如果把fragment加入回退栈会保存老的Fragment内存中存在实例,只是销毁fragment的视图而已。
-
采用hide/show 方法
private void switchFragment(int containerID, Fragment fragment) { if (currentFragment != fragment) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); if (fragment.isAdded()) { transaction.hide(currentFragment).show(fragment).commit(); } else { transaction.hide(currentFragment).add(containerID, fragment).commit(); } currentFragment = fragment; } }
总结:hide/show Fragment不销毁,也不销毁视图,隐藏和显示都不走生命周期方法。
二、Activity知识总结
2.1 Activity的生命周期问题
Activity的生命周期是个老生常谈的问题,对于每个Android开发者不需要多讲,如下图所示。但是在Activity的动态运行过程中,会有各种情况产生,这时候的Activity生命周期回调也是面试过程中,面试官很喜欢考察的问题,下面会列举几种情况来分析。
-
A Activity -> B Activity时都有哪些生命周期回调?
a) 正常情况下:
A.onPause()->B.onCreate()->B.onStart()->B.onResume()->A.onStop()
b) 当B Activity的launchMode是singleTop且B Activity已经在栈顶时:
B.onPause()->B.onNewIntent()->B.onResume()
c) 当B Activity的launchMode是singleInstance,singleTask且对应的B Activity有可复用的实例:
A.onPause()->B.onNewIntent()->B.onRestart()->B.onStart()->B.onResume()->A.onStop()->A.onDestroy()
-
弹出的Dialog对生命周期有什么影响?
弹出的Dialog不会对Activity的生命周期发生影响,如果启动一个Theme为Dialog的Activity,则会调用Activity.onPause()
-
Activity为什么在onResume()之后才显示?
因为只有在执行到onResume()之后,才会调用WindowManager.addView(),此时会生成ViewRootImpl,ViewRootImpl会将顶级View DecorView添加到WMS中,达到和WMS的双向通行,此时会调用ViewRootImpl的requestLayout()方法,对界面View的测量、绘制等工作。
-
onActivityResult是在哪两个生命周期之间回调?
B.onPause()->A.onActivityResult()->A.onRestart()->A.onStart()->A.onResume()
2.2 launchMode类型
standard 标准模式
-
singleTop 栈顶复用模式
当前栈中栈中有Activity实例,并且位于栈顶时,不会创建实例,而是复用并且将Intent对象传入,回调OnNewIntent()方法。例如新闻客户端,点击通知栏,反复进入App页面的情况,频繁创建Activity肯定是不合理的。
-
singleTask 栈内复用模式
在复用的时候,首先会根据taskAffinity去找对应的任务栈:
a) 如果不存在指定的任务栈,系统会新建对应的任务栈,并新建Activity实例压入栈中。
b) 如果存在指定的任务栈,则会查找该任务栈中是否存在该Activity实例,如果不存在该实例,则会在该任务栈中新建Activity实例;如果存在该实例,则会直接引用,并且回调该实例的onNewIntent()方法,并且任务栈中该实例之上的Activity会被全部销毁。
使用场景:APP首页一般会使用singleTask来启动,而且长时间保留在栈中。
-
singleInstance 栈内单例模式
a)不存在,首先会新建一个任务栈,其次创建该Activity实例。
b) 存在,则会直接引用该实例,并且回调onNewIntent()方法。
2.3 onSaveInstanceState和onRetainNonConfigurationInstance区别
onSaveInstanceState 是Activity默认支持的状态保存方法,一般在横竖屏切换或者由于系统内存不足kill后台的Activity的情况下调用的,一般是在stop生命周期的方法下回调的:
private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
// Before P onSaveInstanceState was called before onStop, starting with P it's
// called after. Before Honeycomb state was always saved before onPause.
final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
&& !r.isPreHoneycomb();
//......
r.setState(ON_STOP);
if (shouldSaveState && !isPreP) {
callActivityOnSaveInstanceState(r);
}
}
private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
r.state = new Bundle();
r.state.setAllowFds(false);
if (r.isPersistable()) {
r.persistentState = new PersistableBundle();
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
}
}
然后是用ActivityThread传递下来的Bundle来保存Activity View的状态,通过循环遍历,调用子View的onSaveInstanceState方法,使用id作为key来保存的。保存的值位于ActivityThread的ActivityRecord中。
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
if (mAutoFillResetNeeded) {
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
而onRetainNonConfigurationInstance是ComponentActivity来实现的,其实Activity也有这个方法,但是并未实现。用于实现Google新组件ViewModel的。和onSaveInstanceState不用的地方是它可以是一个NonConfigurationInstances来保存状态,主要保存ViewModel,并且回调的生命周期方法不一样:
/** Core implementation of activity destroy call. */
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
//...
if (!r.stopped) {
callActivityOnStop(r, false /* saveState */, "destroy");
}
if (getNonConfigInstance) {
try {
//调用onRetainNonConfigurationInstance
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to retain activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
}
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnDestroy(r.activity);
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + safeToComponentShortString(r.intent) +
" did not call through to super.onDestroy()");
}
if (r.window != null) {
r.window.closeAllPanels();
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to destroy activity " + safeToComponentShortString(r.intent)
+ ": " + e.toString(), e);
}
}
r.setState(ON_DESTROY);
}
mActivities.remove(token);
StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
该值也是保存在ActivityThread的LoadedApk->ActivityRecord中。