Ahmed
Ahmed

Reputation: 269

When I move quickly between tabs and use ViewPager quickly, it crashes

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

Upvotes: 0

Views: 568

Answers (3)

Linh
Linh

Reputation: 60989

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

Upvotes: 1

Gennadii Saprykin
Gennadii Saprykin

Reputation: 4573

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

Upvotes: 1

Philippe Banwarth
Philippe Banwarth

Reputation: 17755

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

Upvotes: 0

Related Questions