[英]Fragment recreated every time after orientation change, unable to restore state
更新:事实证明问题来自其他地方。 感谢@Luksprog指出我忽略了什么。
NavigationDrawerFragment
类中实现。 NavigationDrawerFragment
的onCreate()
方法,保留最后选择的项目。 NavigationDrawerFragment
将再次调用selectItem(),这会触发我的菜单项选择处理程序。 这会导致Android恢复ListFragment。 通过检查菜单选择处理程序代码中的活动菜单项可以防止这种情况。
我想保留ViewPager
的最后一个查看页面索引,当任何原因重新创建活动时,例如方向更改。
ViewPager
位于Fragment
(名为ListFragment
)中,该Fragment
附加到活动。 我正在使用compat库,因此该片段是android.support.v4.app.Fragment
的子类。
我认为可以通过重写onSaveInstanceState()
方法并在onCreate()
添加适当的逻辑来完成,如文档中所述:
要正确处理重新启动,您的活动必须通过正常的Activity生命周期恢复其先前的状态,在此生命周期中Android会在销毁活动之前调用onSaveInstanceState(),以便您可以保存有关应用程序状态的数据。 然后,您可以在onCreate()或onRestoreInstanceState()期间恢复状态。
但是片段的情况似乎不同。 当我从这个ListFragment
导航到另一个活动并按下“返回”时,可以正确恢复页面索引。 但是,当我旋转设备时,页面索引会丢失。
我添加了一些日志记录,看看有什么问题。 从日志中我发现虽然ListFragment
onSaveInstanceState()
(我称之为ListFragment A)被正确调用,但这个特定的Fragment类不再显示在activity中。 当方向改变并重新创建活动时,Android会调用onSaveInstanceState()
然后调用onDetach()
来分离此片段。 然后Android创建一个新的ListFragment
实例 (我将其命名为ListFragment B)并将其附加到新的旋转活动。 此ListFragment B具有传递给构造函数的空savedInstanceState
,因此最后一个页面索引(以及Fragment A的savedInstanceState中的任何配置)都将丢失。
实际上,每次屏幕旋转时都会创建一个新的ListFragment
实例,但似乎旧的实例不会被销毁。 我在旋转设备时会看到如下所示的日志:
D/ListFragment﹕ [1110257048] onSaveInstanceState() called, storing last page index 3
D/ListFragment﹕ [1109835992] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108826176] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108083096] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1106541040] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108316656] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1109134136] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108630992] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108592888] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1109729064] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1110257048] onDestroy()
D/ListFragment﹕ [1110257048] onDetach()
D/ListFragment﹕ [1109835992] onDestroy()
D/ListFragment﹕ [1109835992] onDetach()
D/ListFragment﹕ [1108826176] onDestroy()
D/ListFragment﹕ [1108826176] onDetach()
D/ListFragment﹕ [1108083096] onDestroy()
D/ListFragment﹕ [1108083096] onDetach()
D/ListFragment﹕ [1106541040] onDestroy()
D/ListFragment﹕ [1106541040] onDetach()
D/ListFragment﹕ [1108316656] onDestroy()
D/ListFragment﹕ [1108316656] onDetach()
D/ListFragment﹕ [1109134136] onDestroy()
D/ListFragment﹕ [1109134136] onDetach()
D/ListFragment﹕ [1108630992] onDestroy()
D/ListFragment﹕ [1108630992] onDetach()
D/ListFragment﹕ [1108592888] onDestroy()
D/ListFragment﹕ [1108592888] onDetach()
D/ListFragment﹕ [1109729064] onDestroy()
D/ListFragment﹕ [1109729064] onDetach()
D/ListFragment﹕ [1110903656] onAttach()
D/ListFragment﹕ [1110903656] onCreate()
D/ListFragment﹕ [1110903656] savedInstanceState is not NULL.
D/ListFragment﹕ [1110903656] Retrieving last page index 3
D/ListFragment﹕ [1110905248] onAttach()
D/ListFragment﹕ [1110905248] onCreate()
D/ListFragment﹕ [1110905248] savedInstanceState is not NULL.
D/ListFragment﹕ [1110905248] Retrieving last page index 0
D/ListFragment﹕ [1110906440] onAttach()
D/ListFragment﹕ [1110906440] onCreate()
D/ListFragment﹕ [1110906440] savedInstanceState is not NULL.
D/ListFragment﹕ [1110906440] Retrieving last page index 0
D/ListFragment﹕ [1110907632] onAttach()
D/ListFragment﹕ [1110907632] onCreate()
D/ListFragment﹕ [1110907632] savedInstanceState is not NULL.
D/ListFragment﹕ [1110907632] Retrieving last page index 0
D/ListFragment﹕ [1110908824] onAttach()
D/ListFragment﹕ [1110908824] onCreate()
D/ListFragment﹕ [1110908824] savedInstanceState is not NULL.
D/ListFragment﹕ [1110908824] Retrieving last page index 0
D/ListFragment﹕ [1110910016] onAttach()
D/ListFragment﹕ [1110910016] onCreate()
D/ListFragment﹕ [1110910016] savedInstanceState is not NULL.
D/ListFragment﹕ [1110910016] Retrieving last page index 0
D/ListFragment﹕ [1110911208] onAttach()
D/ListFragment﹕ [1110911208] onCreate()
D/ListFragment﹕ [1110911208] savedInstanceState is not NULL.
D/ListFragment﹕ [1110911208] Retrieving last page index 0
D/ListFragment﹕ [1110912400] onAttach()
D/ListFragment﹕ [1110912400] onCreate()
D/ListFragment﹕ [1110912400] savedInstanceState is not NULL.
D/ListFragment﹕ [1110912400] Retrieving last page index 0
D/ListFragment﹕ [1110913592] onAttach()
D/ListFragment﹕ [1110913592] onCreate()
D/ListFragment﹕ [1110913592] savedInstanceState is not NULL.
D/ListFragment﹕ [1110913592] Retrieving last page index 0
D/ListFragment﹕ [1110914784] onAttach()
D/ListFragment﹕ [1110914784] onCreate()
D/ListFragment﹕ [1110914784] savedInstanceState is not NULL.
D/ListFragment﹕ [1110914784] Retrieving last page index 0
D/HomeActivity﹕ fragment updated
D/ListFragment﹕ [1110914784] onCreateView()
D/ListFragment﹕ [1111031048] onAttach()
D/HomeActivity﹕ Fragment attached.
D/ListFragment﹕ [1111031048] onCreate()
D/ListFragment﹕ [1111031048] savedInstanceState is NULL.
D/ListFragment﹕ [1111031048] onCreateView()
D/ListFragment﹕ [1111031048] onResume(), restoring page index 0
这是我旋转屏幕大约10次后的日志。 标记中的数字是类的hashCode()
。 上面的行显示,先前创建的片段的onSaveInstanceState()
和onCreate()
即使在被最新的(1111031048)替换之后仍然会被调用。
请注意,我没有在片段类中调用setRetainInstance()
。 实际上,我尝试了setRetainInstance(false)
和setRetainInstance(true)
但它没有改变任何东西。
我这里做错了吗? 我可以理解, ListFragment
需要重新创建,但为什么savedInstanceState
为空? 如果这是预期的行为,那么解决我需求的正确方法是什么,即在配置更改时保持页面索引?
应该可以使页面索引成为静态类变量,但我不确定它是否实际解决了问题,或者只是隐藏它(因为我在上面的日志中闻到了内存泄漏)。
尽管答案已经被接受,但我要澄清一点:“问题”在Android Studio模板中。 正如Edmund所指出的那样,问题在于导航抽屉在重新创建时调用菜单,从而重新调用片段。 为了解决这个问题,我对Android Studio提出的NavigationDrawerFragment.java文件做了一些修改。 原版的:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Read in the flag indicating whether or not the user has demonstrated awareness of the
// drawer. See PREF_USER_LEARNED_DRAWER for details.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
if (savedInstanceState != null) {
mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
mFromSavedInstanceState = true;
}
// Select either the default item (0) or the last selected item.
selectItem(mCurrentSelectedPosition);
}
新的一个:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Read in the flag indicating whether or not the user has demonstrated awareness of the
// drawer. See PREF_USER_LEARNED_DRAWER for details.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
if (savedInstanceState != null) {
mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
mFromSavedInstanceState = true;
} else {
// Select either the default item (0) or the last selected item.
selectItem(mCurrentSelectedPosition);
}
}
刚刚失去半个生产日来解决这个问题。 希望它可以帮助别人。
在问题中更新,这已得到解决,并再次感谢@Luksprog指出我忽略了什么。
Fragment
的行为实际上与Activity
类对齐。
以下是我的问题的原因:
NavigationDrawerFragment
类中实现。 NavigationDrawerFragment
的onCreate()
方法,保留最后选择的项目。 NavigationDrawerFragment
将再次调用selectItem()
,这会触发我的菜单项选择处理程序。 这会导致ListFragment
被替换。 通过检查菜单选择处理程序代码中的活动菜单项,或禁用该selectItem()
调用,可以防止这种情况。
您不需要处理savedInstanceState。 因为存储在片段管理器中的所有片段(它像片段数组)以及当方向改变活动时会破坏,但片段管理器仍然保留之前添加的所有片段。 所以你只需要检查片段是否已经添加。
private void openFragment(Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment existingFragment = fragmentManager.findFragmentByTag(tag);
if (existingFragment != null) {
fragmentTransaction.add(R.id.container, fragment, tag);
fragmentTransaction.commit();
}
}
尝试在这里使用onCreateView()
而不是onCreate()
,它特定于Fragments并且可能会有所不同。
覆盖onSaveInstanceState()
以存储您的状态,并使用savedInstanceState.get*()
在onCreateView()
恢复它。 这就是我们在应用程序中执行此操作的方式,它应该可以正常运行。 正如@Ari所提到的,请确保调用super.onSaveInstanceState(outState)
。
class MyFragment extends ListFragment {
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(ARG_PAGE, page);
super.onSaveInstanceState(outState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(savedInstanceState != null){
if(savedInstanceState.containsKey(ARG_PAGE)){
page = savedInstanceState.getInteger(ARG_PAGE);
}
}
}
}
在AndroidManifest.xml中添加
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize">
</activity>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.