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