github: Done S05.01: with AsyncTaskLoader
- Have MainActivity implement the loader callbacks
- Create a loader ID
- Fill out the loader callbacks using the code from FetchWeatherQuery
- Call initLoader in onCreate (whether or not the loader already exists)
- Restart the loader if refresh is triggered from the menu
1. Implement the proper LoaderCallbacks interface and the methods of that interface
public class MainActivity extends AppCompatActivity implements ForecastAdapterOnClickHandler, LoaderManager.LoaderCallbacks<String[]> {
2. Within onCreateLoader, return a new AsyncTaskLoader
@Override public Loader<String[]> onCreateLoader(int id, final Bundle args) { return new AsyncTaskLoader<String[]>(this) { String[] mWeatherData = null; /** * Subclasses of AsyncTaskLoader must implement this to take care of loading their data. */ @Override protected void onStartLoading() { if(mWeatherData != null){ deliverResult(mWeatherData); }else{ mLoadingIndicator.setVisibility(View.VISIBLE); forceLoad(); } }
3. Cache the weather data in a member variable and deliver it in onStartLoading.
/** * This is the method of the AsyncTaskLoader that will load and parse the JSON data * from OpenWeatherMap in the background. * * @return Weather data from OpenWeatherMap as an array of Strings. * null if an error occurs */ @Override public String[] loadInBackground() { String locationQuery = SunshinePreferences .getPreferredWeatherLocation(MainActivity.this); URL weatherRequestUrl = NetworkUtils.buildUrl(locationQuery); try{ String jsonWeatherResponse = NetworkUtils .getResponseFromHttpUrl(weatherRequestUrl); String[] simpleJsonWeatherData = OpenWeatherJsonUtils .getSimpleWeatherStringsFromJson(MainActivity.this, jsonWeatherResponse); return simpleJsonWeatherData; }catch (Exception e){ e.printStackTrace(); return null; } } /** * Sends the result of the load to the registered listener. * * @param data The result of the load */ public void deliverResult(String [] data){ mWeatherData = data; super.deliverResult(data); } }; }
4. When the load is finished, show either the data or an error message if there is no data
/** * Called when a previously created loader has finished its load. * * @param loader The Loader that has finished. * @param data The data generated by the Loader. */ @Override public void onLoadFinished(Loader<String[]> loader, String[] data) { mLoadingIndicator.setVisibility(View.INVISIBLE); mForecastAdapter.setWeatherData(data); if(data == null){ showErrorMessage(); }else{ showWeatherDataView(); } } @Override public void onLoaderReset(Loader<String[]> loader) { /* * We aren't using this method in our example application, but we are required to Override * it to implement the LoaderCallbacks<String> interface */ }
5.Refactor the refresh functionality to work with our AsyncTaskLoader
/** * This method is used when we are resetting data, so that at one point in time during a * refresh of our data, you can see that there is no data showing. */ public void invailidataData(){ mForecastAdapter.setWeatherData(null); }
@Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); invailidataData(); getSupportLoaderManager().restartLoader(FORECAST_LOADER_ID, null, this); return true; } if (id == R.id.action_map) { openLocationInMap(); return true; } return super.onOptionsItemSelected(item); }
6. Remove any and all code from MainActivity that references FetchWeatherTask7
7. Remove the code for the AsyncTask and initialize the AsyncTaskLoader.
/* * This ID will uniquely identify the Loader. We can use it, for example, to get a handle * on our Loader at a later point in time through the support LoaderManager. */ int loaderId = FORECAST_LOADER_ID; /* * From MainActivity, we have implemented the LoaderCallbacks interface with the type of * String array. (implements LoaderCallbacks<String[]>) The variable callback is passed * to the call to initLoader below. This means that whenever the loaderManager has * something to notify us of, it will do so through this callback. */ LoaderManager.LoaderCallbacks<String[]> callbacks = MainActivity.this; /* * The second parameter of the initLoader method below is a Bundle. Optionally, you can * pass a Bundle to initLoader that you can then access from within the onCreateLoader * callback. In our case, we don't actually use the Bundle, but it's here in case we wanted * to. */ Bundle bundleForLoader = null; /* * Ensures a loader is initialized and active. If the loader doesn't already exist, one is * created and (if the activity/fragment is currently started) starts the loader. Otherwise * the last created loader is re-used. */ getSupportLoaderManager().initLoader(loaderId, bundleForLoader, callbacks);