仅在第二次导航到 Fragment 时导致 java.IllegalStateException 错误,无活动
Posted
技术标签:
【中文标题】仅在第二次导航到 Fragment 时导致 java.IllegalStateException 错误,无活动【英文标题】:causing a java.IllegalStateException error, No Activity, only when navigating to Fragment for the SECOND time 【发布时间】:2013-02-02 12:08:54 【问题描述】:我遇到了一个非常令人费解的错误,我什至不知道如何开始解决。
我有一个带有一个活动的简单应用程序,视图是用片段实现的。其中一个片段内部有一个 ViewPager;所以我决定我想使用 v4 支持库的 getChildFragmentManager 类。我还不得不使用导致问题的 ActionBarSherlock,因为它没有随 v4 库的 v11 一起提供。
我通过将 ABS 中的 v4 支持库替换为 v11 库来解决此问题,并且所有内容都已编译并似乎可以正常工作,包括 ViewPager。
这是奇怪的部分:
第一次打开带有 ViewPager 的 Fragment,它可以正常工作;但是第二次导航到,应用程序崩溃,给出一个无用的堆栈跟踪。通过调试,我发现问题出在 getChildFragmentManager 返回的 FragmentManager 上;它抛出无活动错误。
有人知道是什么原因造成的吗?
我将发布您认为相关的代码。
谢谢你, 大卫
【问题讨论】:
请发布完整的 logcat 输出 究竟什么是“无活动”错误?就像在片段中没有附加到一个活动?导致错误的代码行是什么? 【参考方案1】:我点击了jeremyvillalobos answer 中的链接(非常有帮助),该链接将我带到了this workaround。
public class CustomFragment extends Fragment
private static final Field sChildFragmentManagerField;
static
Field f = null;
try
f = Fragment.class.getDeclaredField("mChildFragmentManager");
f.setAccessible(true);
catch (NoSuchFieldException e)
Log.e(LOGTAG, "Error getting mChildFragmentManager field", e);
sChildFragmentManagerField = f;
@Override
public void onDetach()
super.onDetach();
if (sChildFragmentManagerField != null)
try
sChildFragmentManagerField.set(this, null);
catch (Exception e)
Log.e(LOGTAG, "Error setting mChildFragmentManager field", e);
...
它对我很有效,无需重新实例化片段。
【讨论】:
我用这个扩展了我的 Fragments 并且像一个魅力一样工作。解决了我很久的很多问题!干得好。 这应该是答案:) 嗨...它对我不起作用..我的代码在这里:github.com/gbhola-bst/FragmentsTesting 当我回到 pagerFragment(包含 viewPager 的片段)时,我无法获得任何视图。如果我使用你的代码,它会给出一个异常。 我能够在片段之间导航而不会崩溃,但是使用后退按钮然后再次导航到它会导致崩溃。将我的所有片段更改为从此类扩展,而是解决了这个问题。所以我猜这个实现还没有被修复。【参考方案2】:这似乎是在
报告的错误https://code.google.com/p/android/issues/detail?id=42601
变量
FragmentManagerImpl mChildFragmentManager;
在 Fragment.java 中,分离时未设置为 null。所以下次加载片段时,变量仍然指向最后一个父级。
正如在该线程中所讨论的,一种解决方法是重新实例化 Fragment。
在我的例子中,我在 ActionBar 选项卡中的片段之间切换。有问题的 Fragment 已经嵌套了 Fragment 并且在返回文件加载器 Fragment 时正在崩溃应用程序。所以这是解决方法代码:
class MainTabsListener implements ActionBar.TabListener
public Fragment fragment;
public int TabPosition;
public MainTabsListener(Fragment fragment, int tab_position)
this.fragment = fragment;
TabPosition = tab_position;
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft)
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft)
CurrentFragment = fragment;
CurrentTabSelectedPos = TabPosition;
/**
* This is a work-around for Issue 42601
* https://code.google.com/p/android/issues/detail?id=42601
*
* The method getChildFragmentManager() does not clear up
* when the Fragment is detached.
*/
if( fragment instanceof FileLoaderFragment )
fragment = reinstatiateFileLoaderFragment();
ft.replace(R.id.fragment_container, fragment);
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft)
ft.remove(fragment);
【讨论】:
【参考方案3】:遗憾的是,这是支持 v4 的错误,仍然存在 :(
当您通过导航抽屉或其他类似的东西选择其他片段时,具有子片段的片段被分离。所以那些子片段的 fragmentManager(getChildFragmentManager()) 不再存在。当这些片段返回时,发生了错误。炸弹!
很明显,支持v4应该在onDetach()中清理mChildFragmentManager,但它没有,所以我们必须依靠自己。比如片段中包含子片段的如下代码:
@Override
public void onDetach()
try
Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
catch (NoSuchFieldException e)
e.printStackTrace();
catch (IllegalAccessException e)
e.printStackTrace();
super.onDetach();
一切都会好起来的,祝你有美好的一天:)
【讨论】:
【参考方案4】:你的错误可能是 android.view.InflateException 吗?
如果是这样,你应该动态地膨胀 Fragment,不要使用 XML 布局。
并且,您不应将定义为 XML 布局的片段定位到片段事务。
【讨论】:
不,这不是错误。我确实暂时修复了它,而不是进行替换,而是隐藏它并显示它。这并不理想,因为这种方法可能会导致配置更改出现问题。【参考方案5】:我也有同样的问题。
在一个活动中,我有 3 个按钮可以用 transaction.replace(...) 切换片段
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.layout_tablet_paneau, mLigneMessageFragment);
其中一个片段包含一个带有自定义 FragmentPagerAdapter 的 ViewPage。因此,我必须执行 getChildFragmentManager(),以允许嵌套的 Fragments。
构造函数在这里:
public LignePagerAdapter(Fragment ligneMessageTabletFragment)
super(ligneMessageTabletFragment.getChildFragmentManager());
所以我遇到了同样的错误:这个片段的第一次显示出错了,但是当我显示其他片段并返回这个片段时,我得到了这个异常:
02-26 11:57:50.798: D/ACRA(776): 等待 Toast + worker 结束。杀死应用程序?真的 02-26 11:57:50.798: E/AndroidRuntime(776): 致命异常: main 02-26 11:57:50.798: E/AndroidRuntime(776): java.lang.IllegalStateException: 没有活动 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1075) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1070) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1861) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1474) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:931) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.os.Handler.handleCallback(Handler.java:587) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.os.Handler.dispatchMessage(Handler.java:92) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.os.Looper.loop(Looper.java:132) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.app.ActivityThread.main(ActivityThread.java:4126) 02-26 11:57:50.798: E/AndroidRuntime(776): at java.lang.reflect.Method.invokeNative(Native Method) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 java.lang.reflect.Method.invoke(Method.java:491) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844) 02-26 11:57:50.798: E/AndroidRuntime(776): 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602) 02-26 11:57:50.798: E/AndroidRuntime(776): at dalvik.system.NativeStart.main(Native Method) 02-26 11:57:52.818: I/dalvikvm(776): threadid=4: 对信号 3 做出反应 02-26 11:57:52.818: I/dalvikvm(776): 将堆栈跟踪写入“/data/anr/traces.txt”所以我可以重新创建它来解决问题,而不是放置相同的片段实例,但我似乎效率不高。
transaction.replace(R.id.layout_tablet_paneau, LigneMessageTabletFragment.newInstance());
【讨论】:
【参考方案6】:参考@lopisan 答案:
我长期使用他的解决方案。
但我想有更好的方法来做到这一点!
如果 mChildFragmentManager.mActivity 为 null,则将 mChildFragmentManager 设置为 null。当 performActivityCreated 方法时。
@Override
void performActivityCreated(Bundle savedInstanceState)
if (getFragmentManagerActivity(mChildFragmentManager) == null)
setChildFragmentManager(this, null);
super.performActivityCreated(savedInstanceState);
public static FragmentActivity getFragmentManagerActivity(FragmentManager fragmentManager)
FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
return fm.mActivity;
private static final Field sChildFragmentManagerField;
static
/**
* BUG : causing a java.IllegalStateException error, No Activity, only
* when navigating to Fragment for the SECOND time
* http://***.com /questions/15207305/getting-the-error-java-lang-illegalstateexception-activity-has-been-destroyed
* http://***.com/questions/14929907/causing-a-java-illegalstateexception-error-no-activity-only-when-navigating-to
*/
Field f = null;
try
f = Fragment.class.getDeclaredField("mChildFragmentManager");
f.setAccessible(true);
catch (NoSuchFieldException e)
Log.e(TAG, "Error getting mChildFragmentManager field", e);
sChildFragmentManagerField = f;
public static void setChildFragmentManager(Fragment fragment, FragmentManager fragmentManager)
if (sChildFragmentManagerField != null)
try
sChildFragmentManagerField.set(fragment, fragmentManager);
catch (Exception e)
Log.e(TAG, "Error setting mChildFragmentManager field", e);
【讨论】:
【参考方案7】:我在使用 BottomNavigationView 开关和替换导致应用程序崩溃的片段时遇到了同样的错误。在正常速度下它工作得非常好,如果用户快速切换它会崩溃说
IllegalStateException 无活动
在日志中
在我之前
fragmentA = new FragmentA();
fragmentB = new FragmentB();
fragmentC = new FragmentC();
mBottomNavigationView.setOnNavigationItemSelectedListener(item ->
switch (item.getItemId())
case R.id.fragmentA:
replaceFragment(fragmentA);
case R.id.fragmentB:
repalceFragment(fragmentB);
case R.id.fragmentC:
repalceFragment(fragmentC);
;
我认为这种片段的预先创建并使用它们进行替换会造成问题,现在我每次都重新创建它。它开始工作正常
mBottomNavigationView.setOnNavigationItemSelectedListener(item ->
switch (item.getItemId())
case R.id.fragmentA:
fragment = new FragmentA();
replaceFragment(fragment);
case R.id.fragmentB:
fragment = new FragmentB();
repalceFragment(fragment);
case R.id.fragmentC:
fragment = new FragmentC();
repalceFragment(fragment);
;
public void repalceFragment(Fragment fragment)
if (!this.isFinishing())
if (!fragment.isAdded())
if (!this.mDisplayedFragment.isRemoving())
getFragmentManager().beginTransaction().replace(R.id.layout_fragment_container, fragment).commit();
this.mDisplayedFragment = fragment;
希望它对某些人有所帮助,我做了太多的防御性编码,但它解决了我快速切换片段的问题
【讨论】:
【参考方案8】:请检查此链接的第 32 号:
Child FragmentManager retained after detaching parent Fragment
步骤:
-
Download and copy this class to your project
使用支持 v4 one 扩展每个 Fragment(最佳实践)
使用该类而不是 FragmentStatePagerAdapter
【讨论】:
以上是关于仅在第二次导航到 Fragment 时导致 java.IllegalStateException 错误,无活动的主要内容,如果未能解决你的问题,请参考以下文章
Objective-C:UIDatePicker UIControlEventValueChanged 仅在第二次选择时触发