app结构
activity带有ViewPager
ViewPager上带有4个fragment
这是非常典型的结构
操作过程
点icon打开app
点击home键隐藏app
再次点击icon唤回app
点击home键隐藏app
开启多个其他app,要很耗费内存那种
点击home键隐藏这些app
再次点击icon唤回app
app虽然唤回,但是上面的fragment已经不听招呼,空白,无反应
仔细debug,发现按键能够操作fragment,并且fragment能否反馈,但屏幕上还是显示一个空白fragment以及一个初始状态的变量值
所谓初始状态的变量值,是指静态代码设置的值,比如如下的字符串:
public class FragmentListview extends Fragment {
String currentPath = "static_ini_code_null";
在静态代码之后的初始化过程中这个值应该被改变,但是却没有,可见这是一个仅仅静态代码初始化的fragment
困扰了很久。在activity和fragment的所有生命周期函数处添加了打印,终于发现端倪
以下是第一次唤回app的流程,打印了activity,以及0和1两个fragment
lifecycle: DirPlayerActivity.onRestart()
lifecycle: FragmentListview.onStart() 0:/mnt/sdcard FragmentListview{41c2c1a0 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onStart() 1:/mnt/sdcard FragmentListview{41c3f848 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onStart()
lifecycle: MyArrayAdapter.MyArrayAdapter()
lifecycle: MyArrayAdapter.MyArrayAdapter()
lifecycle: FragmentListview.setListviewAdapter() 0:/mnt/sdcard FragmentListview{41c2c1a0 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.setListviewAdapter() 0:/mnt/sdcard FragmentListview{41c2c1a0 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: DirPlayerActivity.updateFileInfor()! FragmentListview{41c2c1a0 #0 id=0x7f0a0001 android:switcher:2131361793:0} [Lcom.maxproj.android.dirplayer.MyArrayAdapter;@41c2c018
lifecycle: MyArrayAdapter.MyArrayAdapter()
lifecycle: FragmentListview.setListviewAdapter() 1:/mnt/sdcard FragmentListview{41c3f848 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: FragmentListview.setListviewAdapter() 1:/mnt/sdcard FragmentListview{41c3f848 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.updateFileInfor()! FragmentListview{41c3f848 #1 id=0x7f0a0001 android:switcher:2131361793:1} [Lcom.maxproj.android.dirplayer.MyArrayAdapter;@41c2c018
lifecycle: MyArrayAdapter.MyArrayAdapter()
lifecycle: DirPlayerActivity.onResume()
lifecycle: FragmentListview.onResume() 0:/mnt/sdcard FragmentListview{41c2c1a0 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onResume() 1:/mnt/sdcard FragmentListview{41c3f848 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onResumeFragments()
lifecycle: FragmentListview.onPause() 0:/mnt/sdcard FragmentListview{41c2c1a0 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onPause() 1:/mnt/sdcard FragmentListview{41c3f848 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onPause()
lifecycle: FragmentListview.onStop() 0:/mnt/sdcard FragmentListview{41c2c1a0 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onStop() 1:/mnt/sdcard FragmentListview{41c3f848 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onStop()
lifecycle: FragmentListview.onDestroy() 0:/mnt/sdcard FragmentListview{41c2c1a0 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onDetach() 0:/mnt/sdcard FragmentListview{41c2c1a0 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onDestroy() 1:/mnt/sdcard FragmentListview{41c3f848 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: FragmentListview.onDetach() 1:/mnt/sdcard FragmentListview{41c3f848 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onDestroy()
可见唤回时是从onRestart()开始,且没有重新生成fragment的操作
这是第二次唤回app的流程,仍然打印了activity,以及0和1两个fragment
lifecycle: FragmentListview.newInstance() 0 FragmentListview{41c40198}
lifecycle: DirPlayerActivity.StaticCodeIniFragment{} 0 FragmentListview{41c40198}
lifecycle: FragmentListview.newInstance() 1 FragmentListview{41c4e3e8}
lifecycle: DirPlayerActivity.StaticCodeIniFragment{} 1 FragmentListview{41c4e3e8}
lifecycle: FragmentListview.onAttach() 0:static_ini_code_null FragmentListview{41c5ee80 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onCreate() 0:static_ini_code_null FragmentListview{41c5ee80 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onAttach() 0:static_ini_code_null FragmentListview{41c5f0c8 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: FragmentListview.onCreate() 0:static_ini_code_null FragmentListview{41c5f0c8 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onCreate()
lifecycle: FragmentListview.onCreateView() 0:static_ini_code_null FragmentListview{41c5ee80 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onActivityCreated() 0:static_ini_code_null FragmentListview{41c5ee80 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onCreateView() 0:static_ini_code_null FragmentListview{41c5f0c8 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: FragmentListview.onActivityCreated() 0:static_ini_code_null FragmentListview{41c5f0c8 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: FragmentListview.onStart() 0:static_ini_code_null FragmentListview{41c5ee80 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onStart() 0:static_ini_code_null FragmentListview{41c5f0c8 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onStart()
lifecycle: MyArrayAdapter.MyArrayAdapter()
lifecycle: MyArrayAdapter.MyArrayAdapter()
lifecycle: FragmentListview.setListviewAdapter() 0:static_ini_code_null FragmentListview{41c40198}
lifecycle: FragmentListview.setListviewAdapter() 0:/mnt/sdcard FragmentListview{41c40198}
lifecycle: DirPlayerActivity.updateFileInfor()! FragmentListview{41c40198} [Lcom.maxproj.android.dirplayer.MyArrayAdapter;@41c3f260
lifecycle: MyArrayAdapter.MyArrayAdapter()
lifecycle: FragmentListview.setListviewAdapter() 1:static_ini_code_null FragmentListview{41c4e3e8}
lifecycle: FragmentListview.setListviewAdapter() 1:/mnt/sdcard FragmentListview{41c4e3e8}
lifecycle: DirPlayerActivity.updateFileInfor()! FragmentListview{41c4e3e8} [Lcom.maxproj.android.dirplayer.MyArrayAdapter;@41c3f260
lifecycle: MyArrayAdapter.MyArrayAdapter()
lifecycle: DirPlayerActivity.onResume()
lifecycle: FragmentListview.onResume() 0:static_ini_code_null FragmentListview{41c5ee80 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onResume() 0:static_ini_code_null FragmentListview{41c5f0c8 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onResumeFragments()
lifecycle: FragmentListview.onPause() 0:static_ini_code_null FragmentListview{41c5ee80 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onPause() 0:static_ini_code_null FragmentListview{41c5f0c8 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onPause()
lifecycle: FragmentListview.onSaveInstanceState() 0:static_ini_code_null FragmentListview{41c5ee80 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onSaveInstanceState() 0:static_ini_code_null FragmentListview{41c5f0c8 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onSaveInstanceState()
lifecycle: FragmentListview.onStop() 0:static_ini_code_null FragmentListview{41c5ee80 #0 id=0x7f0a0001 android:switcher:2131361793:0}
lifecycle: FragmentListview.onStop() 0:static_ini_code_null FragmentListview{41c5f0c8 #1 id=0x7f0a0001 android:switcher:2131361793:1}
lifecycle: DirPlayerActivity.onStop()
可见,app不是从onRestart()开始,而是完全从activity静态代码开始初始化activity并且到达onCreate()
并且看到来历不明的两个fragment被onAttach()过来,这并不是activity静态代码生成的fragment
一个诀窍:可以将一个fragment或其他object当成字符串打印,会打印出该fragment的内存地址(估计是)
通过这个我们可以看出每次操作的是哪个fragment或其他object,尽管它们可能是同一类对象
如果高亮上面被打印出的各个fragment地址,就会发现activity静态代码生成的两个fragment被后续的activity代码初始化了
比如被setListviewAdapter()设置了adapter,但是这两个fragment却走不到onCreateView()和onResume()
被attach过来的fragment未经历activity代码的初始化,但是却走到了onCreateView()和onResume()
也即,app显示了被attach过来的fragment,仅仅被该fragment的静态代码初始化过
这正可以解释操作时看到的现象,被attach过来的应该是系统帮忙恢复的fragment
在官网上找到了low memory下系统可能的恢复操作,其中包括帮忙恢复fragment
但是,这个恢复的fragment怎样接收?怎样关联回activity?官网上却找不到具体的处理方法
在stackoverflow上找到若干类似的问题,其中之一和我预想的方法一致
也即,在fragment的onAttach()中将自己关联给activity,具体关键代码如下
//fragment代码
public class FragmentListview extends Fragment {
int tab = -1; // attach时如果为-1,说明是系统帮忙恢复的。正常初始化后会被设置为0和1
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
if (tab == -1){ // 系统帮忙创建的,更新到activity
tab = ((DirPlayerActivity) getActivity()).sysAttachFragment; // 这里计数被系统attach了多少个fragment
fragmentListviewInterface.sysAttachFragmentListviewLowMem(
tab,
this);
((DirPlayerActivity) getActivity()).sysAttachFragment++;
}
}
}
//activity代码
public class DirPlayerActivity extends FragmentActivity implements
FragmentListview.FragmentListviewInterface
{
int sysAttachFragment = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dir_player);
for (int i = 0; i < LocalConst.tabCount; i++) {
if(sysAttachFragment == 0){ // 正常启动,而不是系统帮忙恢复
fragmentListview[i] = FragmentListview.newInstance(i);
}
}
}
public void sysAttachFragmentListviewLowMem(int tab, FragmentListview fragment){
fragmentListview[tab] = fragment;
}
}
简单测试了一下,似乎可行
但是,感觉很不好
因为fragment也许和activity有很多关联,有很复杂的初始化过程
系统帮忙恢复的操作却非常的粗鲁
直接生成一个未正常初始化的fragment就强塞过来,并占领了对应的位置
这时候千头万绪,关联和恢复处理相当麻烦不可靠
在low memory的情况下恢复app的状态有必要吗?值得吗?
至少有一些app不会有这样的需求
所以,系统应该提供一个选项,让app自己选择要不要恢复原来的状态
有这样的选项吗?我没找到。或有,只是我不知道而已?
在我的这个项目中我就不希望系统帮忙恢复fragment
让app代码自己处理,结果会干净清爽得多,安心得多