Android后台杀死系列之一:FragmentActivity及PhoneWindow后台杀死处理机制
Posted 看书的小蜗牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android后台杀死系列之一:FragmentActivity及PhoneWindow后台杀死处理机制相关的知识,希望对你有一定的参考价值。
App在后台久置后,再次从桌面或最近的任务列表唤醒时经常会发生崩溃,这往往是App在后台被系统杀死,再次恢复的时候遇到了问题,而在使用FragmentActivity+Fragment的时候会更加频繁。比如,如果Fragment没有提供默认构造方法,就会在重建的时候因为反射创建Fragment失败而崩溃,再比如,在onCreate里面new 一个FragmentDialog,并且show,被后台杀死后,再次唤醒的时候,就会show两个对话框,这是为什么?其实这就涉及了后台杀死及恢复的机制,其中涉及的知识点主要是FragmentActivity、ActivityManagerService、LowMemoryKiller机制、ActivityStack、Binder等一系列知识点。放在一篇文章里面可能会有些长,因此,android后台杀死系列写了三篇:
- 开篇:FragmentActivity及PhoneWindow后台杀死处理机制
- 原理篇1:后台杀死与App现场恢复(主要讲述AMS如何为App恢复现场的原理)
- 原理篇2:后台杀死与LowmemoryKiller(主要讲述App被后台杀死的原理)
本篇是Android后台杀死系列的第一篇,主要讲解在开发过程中,由于后台杀死涉及的一些崩溃,以及如何避免这些崩溃,还有就是简单的介绍一下onSaveInstanceState与onRestoreInstanceState执行时机与原理,这两个函数也是Android面试时常问的两个点,是比简单的启动模式Activity声明周期稍微更深入细致一些的地方,也通过这个点引入后台杀死及恢复原理。
FragmentActivity被后台杀死后恢复逻辑
当App被后台异常杀死后,再次点击icon,或者从最近任务列表进入的时候,系统会帮助恢复当时的场景,重新创建Activity,对于FragmentActivity,由于其中有Framgent,逻辑会相对再复杂一些,系统会首先重建被销毁的Fragment。
举个栗子
我们创建一个Activity,并且在onCreate函数中新建并show一个DialogFragment,之后通过某种方式将APP异常杀死(RogueKiller模拟后台杀死工具),再次从最近的任务唤起App的时候,会发现显示了两个DialogFragment,代码如下:
public class DialogFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DialogFragment dialogFragment = new FragmentDlg();
dialogFragment.show(getSupportFragmentManager(), "");
}
这不仅让我们奇怪,为什么呢?虽然被杀死了,但是onCreate函数在执行的时候还是只执行了一次啊,为什么会出现两个DialogFragment,这里其实就有一个DialogFragment是通过Android自身的恢复重建机制重建出来,在异常杀死的情况下onCreate(Bundle savedInstanceState)函数的savedInstanceState参数也不是null,而是包含了被杀死时所保存的场景信息。再来看个崩溃的例子,新建一个CrashFragment,并且丢弃默认无参构造方法:
public class CrashFragment extends Fragment {
public CrashFragment(String tag) {
super();
}
}
之后再Activity中Add或replace添加这个CrashFragment,在CrashFragment显示后,通过RogueKiller模拟后台杀死工具模拟后台杀死,再次从最近任务列表里唤起App的时候,就会遇到崩溃,
Caused by: android.support.v4.app.Fragment$InstantiationException:
Unable to instantiate fragment xxx.CrashFragment:
make sure class name exists, is public, and has an empty constructor that is public
at android.support.v4.app.Fragment.instantiate(Fragment.java:431)
at android.support.v4.app.FragmentState.instantiate(Fragment.java:102)
at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1952)
at android.support.v4.app.FragmentController.restoreAllState(FragmentController.java:144)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:307)
at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:81)
上面的这两个问题主要涉及后台杀死后FragmentActivity自身的恢复机制,其实super.onCreate(savedInstanceState)在恢复时做了很多我们没有看到的事情,先看一下崩溃:
为什么Fragment没有无参构造方法会引发崩溃
看一下support-V4中FragmentActivity中onCreate代码如下:
protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
...
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
}
mFragments.dispatchCreate();
}
可以看到如果savedInstanceState != null,就会执行mFragments.restoreAllState逻辑,其实这里就牵扯到恢复时重建逻辑,再被后台异常杀死前,或者说在Activity的onStop执行前,Activity的现场以及Fragment的现场都是已经被保存过的,其实是被保存早ActivityManagerService中,保存的格式FragmentState,重建的时候,会采用反射机制重新创Fragment
void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
...
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if (fs != null) {
Fragment f = fs.instantiate(mHost, mParent);
mActive.add(f);
...
其实就是调用FragmentState的instantiate,进而调用Fragment的instantiate,最后通过反射,构建Fragment,也就是,被加到FragmentActivity的Fragment在恢复的时候,会被自动创建,并且采用Fragment的默认无参构造方法,如果没哟这个方法,就会抛出InstantiationException异常,这也是为什么第二个例子中会出现崩溃的原因。
*/
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
try {
Class<?> clazz = sClassMap.get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
sClassMap.put(fname, clazz);
}
Fragment f = (Fragment)clazz.newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments = args;
}
return f;
} catch (ClassNotFoundException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
}
}
q
可以看到场景二提示的errormsg跟抛出的异常是可以对应上的,其实Fragment源码里面也说得很清楚:
/**
* Default constructor. <strong>Every</strong> fragment must have an
* empty constructor, so it can be instantiated when restoring its
* activity's state. It is strongly recommended that subclasses do not
* have other constructors with parameters, since these constructors
* will not be called when the fragment is re-instantiated; instead,
* arguments can be supplied by the caller with {@link #setArguments}
* and later retrieved by the Fragment with {@link #getArguments}.
*
* <p>Applications should generally not implement a constructor. The
* first place application code an run where the fragment is ready to
* be used is in {@link #onAttach(Activity)}, the point where the fragment
* is actually associated with its activity. Some applications may also
* want to implement {@link #onInflate} to retrieve attributes from a
* layout resource, though should take care here because this happens for
* the fragment is attached to its activity.
*/
public Fragment() {
}
大意就是,Fragment必须有一个空构造方法,这样才能保证重建流程,并且,Fragment的子类也不推荐有带参数的构造方法,最好采用setArguments来保存参数。下面再来看下为什么会出现两个DialogFragment。
为什么出现两个DialogFragment
Fragment在被创建之后,如果不通过add或者replace添加到Activity的布局中是不会显示的,在保存现场的时候,也是保存了add的这个状态的,来看一下Fragment的add逻辑:此时被后台杀死,或旋转屏幕,被恢复的DialogFragmentActivity时会出现两个FragmentDialog,一个被系统恢复的,一个新建的。
以上是关于Android后台杀死系列之一:FragmentActivity及PhoneWindow后台杀死处理机制的主要内容,如果未能解决你的问题,请参考以下文章