【问题标题】:Why is Android O failing with "does not belong to this FragmentManager!"为什么 Android O 失败并显示“不属于这个 FragmentManager!”
【发布时间】:2017-05-26 14:22:43
【问题描述】:

我已将我的应用程序迁移到 Android Studio 3 中的 Android O

在 Android O 模拟器上运行我的所有 dialogFragments 现在都失败了:-

java.lang.IllegalStateException: Fragment MyDialogFragment{43ccf50 #2 MyDialogFragment} declared target fragment SettingsFragment{ceed549 #0 id=0x7f0f0142 android:switcher:2131689794:0} that does not belong to this FragmentManager!
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1316)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1624)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1689)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:794)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2470)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2260)
at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2213)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2122)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:746)
at android.os.Handler.handleCallback(Handler.java:769)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6535)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

我没有进行任何代码更改。

在 Android O 中发生了哪些变化,以前工作的 DialogFragments 现在无法显示?

Android Studio 3.0 Canary 1
Build #AI-171.4010489, built on May 15, 2017
JRE: 1.8.0_112-release-b736 x86_64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Mac OS X 10.11.6

    compileSdkVersion 'android-O'
    buildToolsVersion "26.0.0-rc2"

   AndroidManifest.xml
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 'O'
        }

compile 'com.android.support:appcompat-v7:26.0.0-beta1'
compile 'com.android.support:cardview-v7:26.0.0-beta1'
compile 'com.android.support:design:26.0.0-beta1'
    compile 'com.android.support:percent:26.0.0-beta1'

  dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0-alpha1'

    }

【问题讨论】:

  • 这是一个错误,因为我看到了其他报告,因此您需要在问题跟踪器中填写错误报告。但是我不确定该错误是在 v4 支持库中还是在 Android 本身中。所以你应该尝试使用内置片段而不是 v4 来看看是否有变化。
  • 有人知道是不是支持库吗?
  • 我认为这个问题是由 Android O “修复” Fragment 相关的错误引起的。我相信以前版本的 android 允许损坏的代码按预期工作。我可能是错的。
  • 伙计们,我通过将 Fragment 替换为 V4 SupportFragment 进行了检查。但在这方面也存在这个问题......因为 Oreo 一直在检查他们的旧错误......

标签: android android-dialogfragment android-8.0-oreo


【解决方案1】:

最近,我的应用在针对 Android O 时遇到了同样的问题。作为解决方案,请使用:

myDialogFragment.show(SettingsFragment.this.getFragmentManager(), TAG);

代替:

myDialogFragment.show(getFragmentManager(), TAG);
// or  
myDialogFragment.show(getSupportFragmentManager(), TAG);
// or  
myDialogFragment.show(getChildFragmentManager(), TAG);

【讨论】:

    【解决方案2】:

    对我来说,这不仅是 Android O 上的问题,也是旧版本上的问题。 我测试的最旧版本是 API Level 16。

    我正在使用以下代码实例化我的片段:

    MyFragment myFragment = MyFragment.newInstance();
    myFragment.setTargetFragment(ParentFragment.this, 0);
    myFragment.show(getActivity().getSupportFragmentManager(), null);
    

    其中ParentFragment.this 是扩展android.support.v4.app.Fragment 的自定义类,MyFragment 也扩展了此类并且是ParentFragment 片段的子片段(因此得名)。

    我认为我必须使用 SupportFragmentManager(getSupportFragmentManager() 方法),因为我正在使用支持包的片段,所以我尝试调用 getActivity().getSupportFragmentManager() 来获取支持此方法的活动引用。

    这似乎不是正确的方法。 我将这些调用更改为:

    MyFragment myFragment = MyFragment.newInstance();
    myFragment.setTargetFragment(ParentFragment.this, 0);
    myFragment.show(getFragmentManager(), null);
    

    所以片段自己决定使用哪个 FragmentManager 并且错误现在消失了。

    希望这对某人有所帮助。

    【讨论】:

    • 虽然这个解决方案有效,但我不知道为什么它不适用于getChildFragmentManager()
    • 它对我有用,但 IDE 说 Argument 'getFragmentManager()' 可能为空。我设置了一个 if (getFragmentManager() != null) 并且它消失了。
    【解决方案3】:

    在开发应用程序时,我遇到了同样的问题,我通过(在 kotlin 中)解决了它

    chooseRegionFragment.setTargetFragment(this@ParentFragment, REQUEST_CODE_CHOOSE_REGION)
    
    chooseRegionFragment.show(this@ParentFragment.parentFragmentManager, TAG)
    

    翻译成

    chooseRegionFragment.setTargetFragment(ParentFragment.this, REQUEST_CODE_CHOOSE_REGION); 
    
    chooseRegionFragment.show(ParentFragment.this.getParentFragmentManager, TAG);
    

    在java中

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题,绝对是一个 android 错误。当您使用它作为目标显示另一个片段的片段时,就会发生这种情况。作为解决方法,您可以使用:

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
          getActivity().getFragmentManager().beginTransaction().add(dialogFrag, "dialog").commit();
      else
          getChildFragmentManager().beginTransaction().add(dialogFrag,"dialog").commit();
      

      【讨论】:

      • 是的,这个解决方案在 N_MR1 之后可以正常工作,我们应该使用 getFragmentManager 而从 JELLY_BEAN_MR1 到 N_MR1 似乎应该使用 getChildFragmentManager。
      • 嘿,但是如果我使用 getFragmentManager() 而不是 getChildFragmentManager(),childFragment.getParent() 将返回 null。你如何解决这个问题?
      • 使用 getFragmentManager() 代替 getChildFragmentManager() 会起作用,但它会影响 getParentFragment()。如果您不对嵌套片段使用 getChildFragmentManager(),那么您将无法通过在子/嵌套片段中使用 getParentFragment() 来获取父片段。
      【解决方案5】:

      我的情况与 Markus Ressel 相同,但我使用的是 getChildFragmentManager()。我用 getFragmentManager() 替换了它,它解决了这个问题。

      更新:我现在一直在使用 childFragmentManager 并有一些反馈。

      在处理由片段托管的内部片段(因此片段中的片段)时,请使用 childFragmentManager。这个片段管理器与活动 getSupportFragmentManager 不同。两者不一样。这是关注点分离。

      所以我制定了一个规则,即托管子 Fragment 的 Fragment 将始终使用 childFragmentManager,不在主机 Fragment 内的东西可以使用 getSupportfragmentManager。

      【讨论】:

        【解决方案6】:

        使用以下解决方案,您无需担心您正在处理哪些片段管理器,

        假设您一定使用过 BaseFragment。

        首先创建一个接口:

        public interface OnRequestUpdateListener {
        void onRequestUpdate(final int requestCode, final Intent data);
        
        void setRequestFragment(final BaseFragment requestFragment, int requestCode);
        
        BaseFragment getRequestFragment();
        
        int getRequestCode();
        

        }

        在您的 BaseFragment 中实现该接口

        public class BaseFragment extends Fragment implements OnRequestUpdateListener {
        private BaseFragment requestFragment;
        
        private int requestCode;
        
        @Override
        public void onRequestUpdate(int requestCode, Intent data) {
         // you can implement your logic the same way you do in onActivityResult
        }
        
        @Override
        public void setRequestFragment(BaseFragment requestFragment, int requestCode) {
            this.requestFragment = requestFragment;
            this.requestCode = requestCode;
        }
        
        @Override
        public BaseFragment getRequestFragment() {
            return requestFragment;
        }
        
        @Override
        public int getRequestCode() {
            return requestCode;
        }
        

        }

        然后,将 setTargetFragment 替换为 setRequestFragment,将 getTargetFragment 替换为 getRequestFragment。

        在这里,您还可以使用 onRequestUpdate 代替 onActivityResult。

        这是一个自定义解决方案,无需担心您使用的是哪个片段管理器。

        使用 getFragmentManager() 代替 getChildFragmentManager() 也可以,但会影响 getParentFragment()。如果您不对嵌套片段使用 getChildFragmentManager(),那么您将无法通过在子/嵌套片段中使用 getParentFragment() 来获取父片段。

        【讨论】:

          【解决方案7】:

          当我们迁移到 Android Studio 3 并将支持库升级到版本 26 时,我刚刚在我目前正在处理的项目中遇到了同样的问题。突然之间,在不更改代码的情况下,我们得到了大量的这个异常.最后我发现了以下内容:

          Google 在今年 1 月在 v4 片段管理器的源代码中添加了一个新的“健全性检查”(不确定发布到哪个版本),如果片段设置了目标片段,则拒绝使用片段管理器添加片段并且在同一个 Fragment Manager 的活动 Fragment 中找不到目标 Fragment。补丁详解here

          早期版本似乎并不关心这一点。我花了几天时间来更新我们代码中的所有区域,其中使用支持片段管理器添加的片段使用子片段管理器作为其子片段,以父片段作为目标。好吧,写不好的代码迟到的惩罚。

          【讨论】:

          • 感谢您指向此补丁。它不知何故不能解释两件事: 1. 为什么这是一个坏主意? 2.子片段调用并将数据发送回父片段的正确或更好的方法是什么。有什么想法吗?
          • @AAP 在两个不同的片段管理器中有两个片段意味着这两个片段的生命周期是不协调的。例如,可能会发生片段的目标片段已经从其片段管理器中删除但不会被垃圾收集,因为它仍然有引用。如果您不使用诸如 MVVM 之类的架构,其中您有一个可以被所有片段引用的视图模型,您可以改为调用创建片段的 Activity 中的方法。 Activity 及其片段的生命周期更加协调。
          【解决方案8】:

          我的应用程序运行良好,直到将目标版本升级到 27,然后我在调用 setTargetFragment (Fragment fragment, int requestCode) 时遇到同样的问题

          示例:

          chooseRegionFragment.setTargetFragment(ParentFragment.this, REQUEST_CODE_CHOOSE_REGION); 
          

          改成:

          chooseRegionFragment.setTargetFragment(getRootParentFragment(this), REQUEST_CODE_CHOOSE_REGION);
          

          getRootParentFragment(this)这个方法会为你找到片段的根父节点

          /** 
             * find root parent of fragment 
             */ 
            public static Fragment getRootParentFragment(Fragment fragment) { 
              Fragment parent = fragment.getParentFragment(); 
              if(parent == null) 
                return fragment; 
              else 
                return getRootParentFragment(parent); 
            } 
          

          【讨论】:

            猜你喜欢
            • 2021-05-08
            • 1970-01-01
            • 2012-01-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-14
            相关资源
            最近更新 更多