java.lang.IllegalStateException: Fragment未附加到上下文。

7

我在我的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的viewpager中的一个Fragment中遇到了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(); 吗? - Basil Battikhi
MainActivity.context 是什么,为什么要使用它? - AskNilesh
你能发布你的MainActivity吗? - Man
根据日志中的第一行“OrdersFragment{42c2a740} not attached to a context”,请参考https://developer.android.com/guide/components/fragments。 检查是否有遗漏。否则,请附上“OrdersFragment”的代码。 - Mohammad Tauqir
问题似乎出在调用getResources()上,它需要上下文。如果您将orderLoadingMessage.setBackgroundColor移动到onAttach回调中,则可能会解决此问题。请注意,有两种onAttach方法(一种已弃用),如果您支持4.0之前的设备,则也要查看它。至于为什么会发生这种情况,我不知道。在调用getResources()时遇到了相同的问题,当时是在onStart中。 - Ankit
显示剩余6条评论
7个回答

8
问题似乎在于,您的片段正在监听一些事件(通过UserController和QueryResult),并且这些事件在片段附加到上下文之前就已被触发。
尝试在片段变为分离状态时注销它,并在附加后再次注册它(LiveData也可以帮助解决此问题)。另一种方式是在分离时接收和存储事件,仅在附加后处理它。

好的,那么我想到的是,如果您实际上没有两个相同片段的实例。一个已经被附加并正常工作,但旧的一个已经被分离,仍然接收结果。 - ferini
我也考虑过这个问题。但是我怎么能确保已经分离的对象不再接收结果呢? - progNewbie
1
嗯,我不知道你是如何注册你的事件的,但你写道你是在onCreateView中进行的,所以最简单的方法就是在onDestroyView中取消注册。 - ferini

8

在更新您的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
}

4
viewPager.offscreenPageLimit = (total number of fragments - 1)
viewPager.adapter = Adapter

如果您正在使用ViewPager,请使用此方法 如果您正在使用底部导航栏,请简单地检查是否(context != null) 但我建议在offscreenPageLimit中最多只使用3个片段


3

你的一些回调在你的片段从活动中分离后仍然被触发。为了解决这个问题,你需要在执行任何回调之前检查你的片段是否已添加。例如,将你的orders对象初始化更改为以下内容:

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

2
在我的情况下,当我展示一个 DialogFragment 并调用它的方法时,就会出现这个异常。因为在调用方法之前,该片段尚未附加到 FragmentManager(此操作是异步完成的),导致应用程序崩溃。
val fragment = YourDialogFragment.newInstance()
fragment.show(fragmentManager, YourDialogFragment.TAG)

// This will throw an exception.
fragment.setCaptions("Yes", "No")

如果您使用FragmentManager添加片段,您将会遇到另一个异常java.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")

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


1

我曾经遇到过类似的问题。但是我按照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);
        }
    }

}


1
你好!虽然这段代码可能解决了问题,但是包括解释它如何以及为什么解决了问题将有助于提高您的帖子质量,并可能导致更多的赞。请记住,您正在回答未来读者的问题,而不仅仅是现在提问的人。请[编辑]您的答案以添加解释并指出适用的限制和假设。 - Brian61354270

-1

你的解决方案

将你的getItem()方法改为

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

正如我在评论中所解释的那样,一开始我的代码是这样的。我甚至已经将问题中的代码更改为这样。 - progNewbie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接