Tell Android AsyncTaskLoader to Get More Data

Let me start by saying that this is NOT a question about scrolling ListViews. I donโ€™t want to know how to say when the user scrolls to the bottom of the list, so please donโ€™t give me answers to this question or mark it as a duplicate.

I use a class that extends AsyncTaskLoader to populate a ListView with data from a web service.

First I load 50 items and everything works fine. I need to know how to tell the loader to load the next 50 items in stages. I understand WHERE to do this in ListView code, but I cannot find a better way to tell the loader that I want to load more data without reloading it and not loading everything again.

Again, to clarify, the problem I'm trying to solve here simply notifies the bootloader to download more data. He already knows how to load more data when loadInBackground () is called a second time, and the ListView already knows where / when to notify the loader, the question is exactly how.

Some of the relevant code:

@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); m_adapter = new SearchAdapter(getActivity()); setListAdapter(m_adapter); // if the loader doesn't already exist, one will be created // otherwise the existing loader is reused so we don't have // to worry about orientation and other configuration changes getLoaderManager().initLoader(SEARCH_LOADER_ID, null, this); } @Override public Loader<List<Result>> onCreateLoader(int id, Bundle args) { String query = args != null ? args.getString(QUERY_KEY) : ""; return new SearchLoader(getActivity(), query); } private class SearchAdapter extends ArrayAdapter<Result> { // ... @Override public View getView(int position, View convertView, ViewGroup parent) { // ... if (position == getCount() - 2) // TODO: Need to notify Loader here // ... } } private static class SearchLoader extends OurAsyncTaskLoader<List<Result>> { public SearchLoader(Context context, String query) { super(context); m_query = query; m_data = Lists.newArrayList(); m_loadedAllResults = false; } @Override public List<Result> loadInBackground() { if (m_loadedAllResults) return m_data; // the Loader implementation does a == check rather than a .equals() check // on the data, so we need this to be a new List so that it will know we have // new data m_data = Lists.newArrayList(m_data); MyWebService service = new MyWebService(); List<Result> results = service.getResults(m_query, m_data.size(), COUNT); service.close(); if (results == null) return null; if (results.size() < COUNT) m_loadedAllResults = true; for (Result result : results) m_data.add(result) return m_data; } private static final int COUNT = 50; private final String m_query; private boolean m_loadedAllResults; private List<Result> m_data; } 
+6
source share
2 answers

I figured out a way that works. In my SearchAdapter#getView() method, I have the following code:

 private class SearchAdapter extends ArrayAdapter<Result> { // ... @Override public View getView(int position, View convertView, ViewGroup parent) { // ... if (position == getCount() - 2) getLoaderManager().getLoader(SEARCH_LOADER_ID).onContentChanged(); // ... } } 

I would still like to know if this is a "best practice" way, but it seems to be solving my problem right now.

+3
source

In your scenario, I recommend that you use ForceLoadContentObserver, which you can associate with a URI in the ContentResolver. Like this:

 class SearchLoader .... ForceLoadContentObserver contentObserver = new ForceLoadContentObserver(); .... @Override public void onStartLoading() { if (cacheResult == null || takeContentChanged()) { // This will see if there a change notification to observer and take it. onForceLoad(); } else { deliverResult(cacheResult); } } @Override public Result loadInBackground() { Result result = loadResult(); // notification uri built upon Result.BASE_URI so it receives all notifications to BASE_URI. getContext().getContentResolver().registerContentObserver(result.getNotificationUri(), true, contentObserver); } @Override public void onReset() { // ... Do your clean stuff... getContext().getContentResolver().unregisterContentObserver(contentObserver); } ... } 

This way you can break your change using:

 context.getContentResolver().notifyChanged(Result.BASE_URI, null); 

Even when the activity holding the bootloader is in the background or the Result cannot be delivered. And you do not need instances of loaders.

All notifications around the Uri object. Uri is a powerful data presentation in Android.

But I also have a confusion. In this case, both your approach and my approach involve changing the content means loading more data . But what if you need to reload all the data? What notice will you use?

0
source

Source: https://habr.com/ru/post/946417/


All Articles