沉舟侧畔千帆进,病树前头万木春。这篇文章主要讲述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被后台杀死的原理)
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后台杀死处理机制)】
推荐阅读
- Android之Activity
- 转(android Support 兼容包详解)
- Android Studio第二十九期 - RecycleView的表格形式
- Android 调用系统邮件,发送邮件到指定邮箱
- Android:dialog去除边框的实现(自带Style的padding)
- [Android自定义控件] Android自定义控件
- Android自定义字体
- 配置Android的NDK开发环境(eclipse)
- Android高级开发知识总结