【问题标题】:java.lang.IllegalStateException: Fragment not attached to a contextjava.lang.IllegalStateException:片段未附加到上下文
【发布时间】:2018-07-19 11:45:26
【问题描述】:

我的 MainActivity 中有一个带有 viewpager 的 tablayout。

我的PagerAdapter 看起来像这样:

public class MainActivityPagerAdapter extends PagerAdapter {

    public MainActivityPagerAdapter(FragmentManager fm, int numOfTabs) {
        super(fm, numOfTabs);
    }

    @Override
    public Fragment getItem(int position) {

        switch (position) {
            case 0:
                return new StoresFragment();
            case 1:
                return new OrdersFragment();
            default:
                return null;
        }
    }
}

我从另一个这样的活动回来:

Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish(); //finishAffinity();

但随后我在 MainActivity 的视图页面中的一个片段中得到了一个 java.lang.IllegalStateException

我阅读了许多相关问题并试图解决这个问题。据说,当人们在 PagerAdapter 之外保留对 Fragments 的引用时,就会发生这种情况。但我没有这样做,正如您在我的代码中看到的那样。

有谁知道我做错了什么?

编辑 - Stacktrace

FATAL EXCEPTION: main
    Process: com.lifo.skipandgo, PID: 23665
    java.lang.IllegalStateException: Fragment OrdersFragment{42c2a740} not attached to a context.
    at android.support.v4.app.Fragment.requireContext(Fragment.java:614)
    at android.support.v4.app.Fragment.getResources(Fragment.java:678)
    at com.lifo.skipandgo.activities.fragments.OrdersFragment$1.results(OrdersFragment.java:111)
    at com.lifo.skipandgo.connectors.firestore.QueryResult.receivedResult(QueryResult.java:37)
    at com.lifo.skipandgo.controllers.UserController$2.onUpdate(UserController.java:88)
    at com.lifo.skipandgo.connectors.firestore.QuerySubscription.onEvent(QuerySubscription.java:59)
    at com.lifo.skipandgo.connectors.firestore.QuerySubscription.onEvent(QuerySubscription.java:18)
    at com.google.firebase.firestore.zzg.onEvent(Unknown Source)
    at com.google.firebase.firestore.g.zzh.zza(SourceFile:28)
    at com.google.firebase.firestore.g.zzi.run(Unknown Source)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:146)
    at android.app.ActivityThread.main(ActivityThread.java:5653)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
    at dalvik.system.NativeStart.main(Native Method)

编辑: 有趣的是,当错误发生时,视图已经明确加载。因为在片段再次显示后大约 10-15 秒后发生错误。我在我的 orderFragment 中出现了错误:

orders = new QueryResult<UserOrder>(UserOrder.class) {
            @Override
            public void results(List<UserOrder> results) { 
orderLoadingMessage.setBackgroundColor(getResources().getColor(R.color.green)); 
    }
}

我在 onCreateView 中执行此操作,此结果在视图加载后大约 10-15 秒出现。

【问题讨论】:

  • 您要加载哪个片段?请分享
  • 你有没有试过回new StoresFragment();
  • 什么是MainActivity.context,为什么?
  • 尝试用 MainActivity.this 更改 Fragment.instantiate(MainActivity.context, StoresFragment.class.getName()) 中的上下文;。
  • @BasilBattikhi 是的,这是我首先尝试的。只需返回new StoresFragment();new OrdersFragment()

标签: java android android-fragments android-viewpager


【解决方案1】:

问题似乎是,您的片段正在侦听一些事件(通过 UserController 和 QueryResult),并且这些事件在片段附加到上下文之前被触发。

尝试在片段分离时注销片段,并在附加后再次注销片段(LiveData 也可以帮助解决此问题)。另一种方法可能是在分离时接收和存储事件,并且仅在附加后处理它。

【讨论】:

  • 好的,那么我想到的是,如果您实际上没有相同片段的两个实例。一个已附加并且工作正常,但旧的已分离并且仍然收到结果。
  • 我也想过。但我怎么能保证,分离的人不再收到结果了?
  • 好吧,我不知道您是如何注册您的活动的,但您写道您在 onCreateView 中注册,所以最简单的方法是在 onDestroyView 中取消注册。
【解决方案2】:

在更新您的 Activity UI 之前使用它:

if(isAdded())// This {@link androidx.fragment.app.Fragment} class method is responsible to check if the your view is attached to the Activity or not
{
        // TODO Update your UI here
}

【讨论】:

    【解决方案3】:

    在您的片段与活动分离后,您的一些回调会被触发。要解决此问题,您需要在处理任何回调之前检查您的片段是否已添加。例如,将您的 orders 对象的初始化更改为:

    orders = new QueryResult<UserOrder>(UserOrder.class) {
                @Override
                public void results(List<UserOrder> results) { 
                    if(isAdded()) {
                        orderLoadingMessage.setBackgroundColor(
                            getResources().getColor(R.color.green)); 
                    }
                }
            }
    

    【讨论】:

      【解决方案4】:
      viewPager.offscreenPageLimit = (total number of fragments - 1)
      viewPager.adapter = Adapter
      

      如果您使用的是 viewpager,请使用它 如果您使用的是底部导航,只需检查 if(context != null) 但我建议在 offscreenPageLimit 中使用最多 3 个片段

      【讨论】:

        【解决方案5】:

        在我的例子中,当我显示 DialogFragment 并调用它的方法时发生了这个异常。因为在调用方法之前片段没有附加到FragmentManager(此操作异步完成),所以应用程序崩溃了。

        val fragment = YourDialogFragment.newInstance()
        fragment.show(fragmentManager, YourDialogFragment.TAG)
        
        // This will throw an exception.
        fragment.setCaptions("Yes", "No")
        

        如果您添加带有FragmentManager 的片段,您将获得另一个exceptionjava.lang.IllegalArgumentException: No view found for id 0x1020002 (android:id/content) for fragment(或类似的,如果您使用另一个ID)。

        您可以通过post(或postDelayed)调用片段方法,但这是一个不好的解决方案:

        view?.post {
            fragment.setCaptions("Yes", "No")
        }
        

        目前我使用childFragmentManager 而不是fragmentManager

        val fragment = YourDialogFragment.newInstance()
        fragment.show(childFragmentManager, YourDialogFragment.TAG)
        fragment.setCaptions("Yes", "No")
        

        我不记得我做了什么,但现在可以了。

        【讨论】:

          【解决方案6】:

          我有类似的问题。我已经按照 ferini 的建议解决了这个问题。我使用的是在附加上下文之前触发的实时数据。

          这是我的完整实现

          public class PurchaseOrderFragment extends Fragment {
          
          
              FragmentPurchaseOrderBinding binding;
              CurrentDenominationViewModel currentDenominationViewModel;
              @Inject
              ViewModelFactory viewModelFactory;
              CurrentVoucherChangedObserver observer;
          
              @Override
              public void onCreate(@Nullable Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  currentDenominationViewModel = new ViewModelProvider(requireActivity(),viewModelFactory).get(CurrentDenominationViewModel.class);
          observer =  new CurrentVoucherChangedObserver();
          
              }
          
              @Override
              public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                                       Bundle savedInstanceState) {
                  // Inflate the layout for this fragment
                  binding = DataBindingUtil.inflate(inflater,R.layout.fragment_purchase_order, container, false);
                  return binding.getRoot();
              }
          
              @Override
              public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
                  super.onViewCreated(view, savedInstanceState);
                  
                  currentDenominationViewModel.getCurrentVoucherStatisticsLiveData().observe(requireActivity(), observer);
              }
          
              @Override
              public void onAttach(@NonNull Context context) {
                  AndroidSupportInjection.inject(this);
                  super.onAttach(context);
              }
          
              @Override
              public void onDetach() {
                  super.onDetach();
                  currentDenominationViewModel.getCurrentVoucherStatisticsLiveData().removeObserver(observer);
              }
          
              final  class CurrentVoucherChangedObserver implements Observer<VoucherStatistics> {
                  @Override
                  public void onChanged(VoucherStatistics x) {
                      String denomination = x.getDenomination()+"";
                      binding.tvDenomination.setText(denomination);
          
                      String stockAmount = requireContext().getResources().getString(R.string.StockAmount);
                      String text= "("+String.format(stockAmount,x.getQuantity()+"")+")";
                      binding.tvInStock.setText(text);
                  }
              }
          
          }
          
          

          【讨论】:

          • 您好!虽然这段代码可以解决问题,including an explanation 解决问题的方式和原因确实有助于提高帖子的质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的回答添加解释并说明适用的限制和假设。
          【解决方案7】:

          您的解决方案

          将您的 getItem() 方法更改为

          switch (position) {
                  case 0:
                      return new StoresFragment();
                  case 1:
                      return new OrdersFragment();
                  default:
                      return null;
              }
          

          【讨论】:

          • 正如我在我的 cmets 中解释的那样,我一开始就是这样。我什至还把问题中的代码改成了这个。
          猜你喜欢
          • 2023-04-04
          • 2015-04-24
          • 2021-09-28
          • 2018-09-20
          • 2020-07-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多