0

I use ViewPager and Tabs in my application . When I test the app and move quickly between tabs , it crashes . I have one Fragment and 3 tabs and the content of the Fragment change in every tab . The content is data from an API using Retrofit 2 . I also use FragmentStatePagerAdapter .

here is my Logs .

java.lang.IllegalStateException: Fragment MainFragment{423155d0} not attached to Activity
                                                                           at android.support.v4.app.Fragment.getResources(Fragment.java:715)
                                                                           at com.webstore.footballscores.Fragments.MainFragment$4.onResponse(MainFragment.java:393)
                                                                           at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
                                                                           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:5641)
                                                                           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:1288)
                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1104)
                                                                           at dalvik.system.NativeStart.main(Native Method)

This error in this line

    res = getResources();

And the rest of code .

 connection.enqueue(new Callback<List<FixturesObject>>() {
        @Override
        public void onResponse(Call<List<FixturesObject>> call, Response<List<FixturesObject>> response) {
            if (!response.isSuccessful()) {
                progressDoalog.dismiss();
                Toast.makeText(getActivity(), "Something went wrong , Please refresh again", Toast.LENGTH_SHORT).show();
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        swipeRefreshLayout.setRefreshing(false);
                    }

                }, 4000);
            } else if (response.isSuccessful()) {
                i=1;
                imageView.setVisibility(View.GONE);
                progressDoalog.dismiss();

                    if (isAdded()) {
                 res = getResources();
                String[] teamEngList = res.getStringArray(R.array.team_english);
                String[] teamArabList = res.getStringArray(R.array.team_arabic);
                }

                     if (response.body().get(i).getLeagueId().equals("109")) {

                                for (int j = 0; j < teamEngList.length; j++) {
                                    if (teamEngList[j].equals(response.body().get(i).getMatchAwayteamName())) {
                                        response.body().get(i).setMatchAwayteamName(teamArabList[j]);
                                    } else if (teamEngList[j].equals(response.body().get(i).getMatchHometeamName())) {
                                        response.body().get(i).setMatchHometeamName(teamArabList[j]);
                                    }
                                }
                                spainObject.add(response.body().get(i));
                                listDataChild.put(listDataHeader.get(listDataHeader.size() - 1), spainObject);
                            }

                   adapter.clearData();

                    adapter.addData(listDataHeader, listDataChild);
                   }

How can I fix this error . Thanks

Ahmed
  • 259
  • 3
  • 14

3 Answers3

1

When you are swiping the ViewPager is creating fragments for visible pages and destroying for those that are invisible. Your network requests are independent from that process. When a network request finishes and the corresponding fragment is destroyed, you are trying to call getResources() but this call requires having a context. A fragment does not have a context anymore if it has been destroyed, thus, you get a crash.

To fix the issue you need to make sure that the context is available before you make that call. Try this:

if (!isAdded()) return;

res = getResources();
//...

Some documentation: https://developer.android.com/reference/android/support/v4/app/Fragment#isAdded()

Gennadii Saprykin
  • 4,505
  • 8
  • 31
  • 41
1

The reason why you get an exception

You are using FragmentStatePagerAdapter so Fragment in ViewPager can be destroyed then detached (see my demo here).
In each Fragment, you run an asynchronous task (using Retrofit) and when it finished you update UI.
If you update UI for a fragment which is destroyed before, you will get the exception

Solutions

There are some ways for solving your problem

  • Cancel receive the callback from the asynchronous request or cancel the request.
  • Before update UI after asynchronous task finished, check if the fragment is isDetached or not
  • Don't let your Fragment destroyed, you can use setOffscreenPageLimit

Depend on your case, you can choose 1 in 3 ways

Linh
  • 57,942
  • 23
  • 262
  • 279
  • In your answer you explain the lifecycle of each fragment in `FragmentStatePagerAdapter` . I want to tell you an issue and I hope you understand me :) . I have 3 tabs and I use a `ProgressDialog` . So when app start the `ProgressDialog` appear . Now the `fragment 2` has its data . but when I move to it , `ProgressDialog` appear to load `fragment 3` . my problem is , this `ProgressDialog` is very annoying for user and I can't delete the `ProgressDialog` . Is there a way to make it better ? I hope you understand my explain . – Ahmed Jun 21 '18 at 11:24
0

When the request is completed the fragment has already been detached. Instead of waiting for the response then throwing it away, you could cancel() the Call, for example.

@Override
public void onDestroy() {
    connection.cancel();
    super.onDestroy();
}

Note that the callback will still be triggered, but with onFailure() instead of onResponse()

bwt
  • 17,292
  • 1
  • 42
  • 60