简体   繁体   English

java.lang.IllegalStateException:片段未附加到上下文

[英]java.lang.IllegalStateException: Fragment not attached to a context

I have a tablayout with a viewpager in my MainActivity.我的 MainActivity 中有一个带有 viewpager 的 tablayout。

My PagerAdapter looks like this:我的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;
        }
    }
}

I am coming back from another activity like this:我从另一个这样的活动回来:

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

But then I get an java.lang.IllegalStateException in one of my Fragments in the viewpager of the MainActivity.但是后来我在 MainActivity 的 viewpager 中的一个 Fragment 中得到了java.lang.IllegalStateException

I read many related questions and tried to solve this.我阅读了许多相关问题并试图解决这个问题。 It is said, that this happens when one keeps references to Fragments outside of the PagerAdapter.据说,当人们在 PagerAdapter 之外保留对 Fragments 的引用时,就会发生这种情况。 But I am not doing this, as you can see in my code.但是我没有这样做,正如您在我的代码中看到的那样。

Does anyone know what I am doing wrong?有谁知道我做错了什么?

Edit - 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)

Edit: Interesting is, that the view has defenitely loaded when the error occurs.编辑:有趣的是,当发生错误时,该视图已明确加载。 Because the error occurs about 10-15 seconds later after the fragment is shown again.因为在再次显示片段后大约10-15秒后会出现错误。 I this in my orderFragment, where the error occurs:我在我的 orderFragment 中出现了这个错误:

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

I do this in onCreateView and this result comes about 10-15 seconds after the view loaded.我在onCreateView执行此操作,此结果在视图加载后大约 10-15 秒出现。

The problem seems to be, that your fragment is listening to some events (via UserController and QueryResult) and these are fired before the fragment is attached to context.问题似乎是,您的片段正在侦听某些事件(通过 UserController 和 QueryResult),并且在将片段附加到上下文之前会触发这些事件。

Try to unregister the fragment when it becomes detached and to them again after attaching (LiveData can also help with this).尝试在片段分离时取消注册,并在附加后再次取消注册(LiveData 也可以帮助解决此问题)。 Another way could be to receive and store the event while detached and only process it after attaching.另一种方法可能是在分离时接收和存储事件,并且仅在附加后处理它。

Use this before update your Activity UI :在更新您的活动 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
}
viewPager.offscreenPageLimit = (total number of fragments - 1)
viewPager.adapter = Adapter

Use this if your are using viewpager And if you are using bottom navigation just simply check if(context != null) But i suggest to use max 3 fragments in offscreenPageLimit如果您使用的是 viewpager 请使用它 如果您使用的是底部导航,只需检查 if(context != null)但我建议在 offscreenPageLimit 中使用最多 3 个片段

Some of your callbacks are being fired after your fragment is detached from activity.在您的片段与活动分离后,您的一些回调将被触发。 To resolve this issue you need to check whether your fragment is added before acting upon any callbacks.要解决此问题,您需要在对任何回调采取行动之前检查您的片段是否已添加。 For example, change your orders object's initialization to this:例如,将您的orders对象的初始化更改为:

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

In my case this exception happened when I showed a DialogFragment and called it's methods.在我的情况下,当我显示一个DialogFragment并调用它的方法时发生了这个异常。 Because the fragment hasn't attached to a FragmentManager (this operation completes asynchronously) before calling methods, an application crashed.由于在调用方法之前片段尚未附加到FragmentManager (此操作异步完成),因此应用程序崩溃。

val fragment = YourDialogFragment.newInstance()
fragment.show(fragmentManager, YourDialogFragment.TAG)

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

If you add the fragment with FragmentManager , you will get another exception : java.lang.IllegalArgumentException: No view found for id 0x1020002 (android:id/content) for fragment (or similar, if you use another id).如果你使用FragmentManager添加FragmentManager ,你会得到另一个异常java.lang.IllegalArgumentException: No view found for id 0x1020002 (android:id/content) for fragment (或类似的,如果你使用另一个id)。

You can call fragment methods via post (or postDelayed ), but it is a bad solution:您可以通过post (或postDelayed )调用片段方法,但这是一个糟糕的解决方案:

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

Currently I use childFragmentManager instead of fragmentManager :目前我使用childFragmentManager而不是fragmentManager

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

I don't remember what I did, but now it works.我不记得我做了什么,但现在它起作用了。

I had similar problem.我有类似的问题。 I have solved it by following ferini's recommendation.我已经按照 ferini 的建议解决了这个问题。 I was using a live data which was firing before the context was attached.我使用的是在附加上下文之前触发的实时数据。

Here is my full implementation这是我的完整实现

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);
        }
    }

}

Your Solution您的解决方案

change your getItem() method to将您的 getItem() 方法更改为

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM