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

时间:2018-07-19 11:45:26

标签: java android android-fragments android-viewpager

我的MainActivity中有一个带有viewpager的布局。

我的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中的一个片段中得到了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秒出现。

7 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

在我的情况下,当我显示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")

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

答案 3 :(得分:0)

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

如果您使用的是Viewpager,请使用此 如果您使用的是底部导航,只需检查if(context!= null)

答案 4 :(得分:0)

我有类似的问题。我已经按照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);
        }
    }

}

答案 5 :(得分:0)

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

答案 6 :(得分:-1)

您的解决方案

将您的getItem()方法更改为

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