tedski
tedski

Reputation: 2311

Only load ListFragment once via AsyncTask

I'm trying to learn Android development and am still trying to wrap my head around how the framework works and how to properly extend all these classes.

My goal is this: An Activity that has a ViewPager that cycles through five ListFragments. The data being bound to each ListFragment is coming from a web API, which is called using an AsyncTask.

My problem is that whenever I switch to a new fragment, the MyListFragment.newInstance() is called again, which calls the AsyncTask every time. I'm trying to have the AsyncTask called once, the first time the ListFragment is loaded, to cut down on the web calls and save the page state. Eventually I want to do a "Pull down to refresh" type thing, which would mean not reloading the data when I switch a page.

I've been following this guide on Android Developers to try to make this happen. What I ended up with is this:

public class MainActivity extends FragmentActivity {

    MyPagerAdapter mMyPagerAdapter;
    ViewPager mViewPager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMyPagerAdapter = new MyPagerAdapter(getSupportFragmentManager());

        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mMyPagerAdapter);
    }


    public class MyPagerAdapter extends FragmentPagerAdapter {
        public static final int NUM_PAGES = 5;

        public MyPagerAdapter(FragmentManager fm) {
            super(fm);
        }   

        @Override
        public Fragment getItem(int i) {
            return MyListFragment.newInstance(getPageTitle(i).toString());
        }

        @Override
        public int getCount() {
            return NUM_PAGES;
        }

        @Override
        public CharSequence getPageTitle(int position) { 
            String[] titles = getResources().getStringArray(R.array.thing_sort_titles);
            return titles[position];
        }

    }


    public static class MyListFragment extends ListFragment {
        public static final String ARG_CATEGORY = "category_title";
        ArrayList<HashMap<String, String>> mylist;

        public MyListFragment() {
            mylist = new ArrayList<HashMap<String, String>>();
        }

        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);

            Bundle args = getArguments();
            URI url = new URI("http://api.remotesite.com/" + args.getString(ARG_CATEGORY));
            new MyListAsyncTask().execute(url);
        }

        static MyListFragment newInstance(String category) {
            MyListFragment f = new MyListFragment();

            Bundle args = new Bundle();
            args.putString(ARG_CATEGORY, category);
            f.setArguments(args);

            return f;
        }

        private class MyListAsyncTask extends AsyncTask<URI, Integer, ArrayList<HashMap<String, String>>> {

            protected ArrayList<HashMap<String, String>> doInBackground(URI... uris) {
                try {
                    JSONArray json = JSON.getJSONfromURL(uris[0]);
                    for (int i = 0; i < json.length(); i++) {
                        HashMap<String, String> map = new HashMap<String, String>();
                        JSONObject obj = json.getJSONObject(i)
                        map.put("title", obj.getString("title"));
                        map.put("author", obj.getString("author"));
                        map.put("id", obj.getString("id"));
                        mylist.add(map);
                    }
                } catch (Exception ex) {
                }
                return mylist;
            }

            protected void onPostExecute(ArrayList<HashMap<String, String>> list) {
                ListAdapter adapter = new SimpleAdapter(
                    getActivity(), 
                    mylist,
                    R.layout.list_item, 
                    new String[] { "title", "author" }, 
                    new int[] { R.id.text_title, R.id.text_author }
                );
                MyListFragment.this.setListAdapter(adapter);
            }
        }
    }
}

I tried doing an array of ListFragments in the FragmentPagerAdapter, and only call MyListFragment.newInstance if that index was null, but it didn't end up changing anything.

Upvotes: 2

Views: 1068

Answers (2)

Tanmay Mandal
Tanmay Mandal

Reputation: 40168

Please try to use setOffscreenPageLimit to resolve your issue. If you use it it will cache your fragment and it would not be called again.

Upvotes: 1

Budius
Budius

Reputation: 39836

I'll suggest you use the Loaders (http://developer.android.com/guide/components/loaders.html) for your case.

The loaders are quite clever on that regard that it automatically deliver back to the calling component the same data as before if you use the same ID.

for example:

Fragment position 0 calls initLoader(0, null, this); then Fragment position 1 calls initLoader(1, null, this); and so on....

that way whenever initLoader(1 << id is called the second (3rd, 4th) time, it will not execute the web call and directly deliver the same result that was delivered on the 1st call.

Upvotes: 1

Related Questions