Loader : CursorLoader - CursorAdapter - AsyncTask

Functionality Overview






ForecastFragment

Loader reads ContentProvider data into cursor & callbacks swap it into Adapter for display:
    cursor = getContentResolver().query("content://com.example.shreekant.sunshine.app/weather/<Pune>?date=<normalizedDate>");
    mForecastAdapter = CursorAdapter(cursor);
    CursorLoader("content://com.example.shreekant.sunshine.app/weather/<Pune>?date=<normalizedDate>");


FetchWeatherTask

Reads Json, adds data to ContentProvider:
    http://api.openweathermap.org/data/2.5/forecast/daily?q=Pune&mode=json&units=metric&cnt=7
    getContentResolver().bulkInsert()


WeatherProvider

ContentProvider implements "content://" interface over SQLiteOpenHelper
    See Content Provider

WeatherContract

DB table and field names referred by all
    See Query Projection & Contract




Loader - CursorLoader - CursorAdapter - AsyncTask [- Content Resolver/Provider - Database support]


Loader performs asynchronous loading of data. While Loaders are active they monitor the source of their data and deliver new results when the contents change.

// Creates CursorLoader to load Cursor with data fetched from Uri, and
// takes care of showing it in views (either directly or thru CursorAdapter)
public class ForecastFragment extends Fragment
        implements LoaderManager.LoaderCallbacks<Cursor> {

    private static final int FORECAST_LOADER_ID = 0;
    private ForecastAdapter mForecastAdapter;

    private static final String[] FORECAST_COLUMNS = {
            // ID needs to be fully qualified as a join is used
            WeatherContract.WeatherEntry.TABLE_NAME + "." + WeatherContract.WeatherEntry._ID,
            WeatherContract.WeatherEntry.COLUMN_DATE,
            WeatherContract.WeatherEntry.COLUMN_SHORT_DESC,
            :
    };
    // These indices are tied to FORECAST_COLUMNS.  If FORECAST_COLUMNS changes, these must change.
    static final int COL_WEATHER_ID = 0;
    static final int COL_WEATHER_DATE = 1;
    static final int COL_WEATHER_DESC = 2;
    :

    // LoaderManager.LoaderCallbacks
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // Create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.

        // CursorLoader implements the Loader protocol in a standard way for querying cursors, building
        // on AsyncTaskLoader to perform the cursor query on a background thread so that
        // it does not block the application's UI.

        Uri weatherForLocationStartDateUri = WeatherContract.WeatherEntry.buildWeatherLocationWithStartDate(
                Utility.getPreferredLocation(getActivity()),
                System.currentTimeMillis());
        return new CursorLoader(getActivity(),
                weatherForLocationStartDateUri,
                FORECAST_COLUMNS,   // projection
                null,               // selection
                null,               // selection params
                null);              // sort order
        // This takes care of bulk of work needed to fetch data thru Uri, load it into Cursor in background.
    }

    // LoaderManager.LoaderCallbacks
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // This is called when a previously created loader has finished its load.
        // Swap the new cursor in the CursorAdapter.
        mForecastAdapter.swapCursor(data);
    OR
        // For a simple view (that doesn't need Adapter):
        detailTextView.setText(data.getString(..));
    }

    // LoaderManager.LoaderCallbacks
    public void onLoaderReset(Loader<Cursor> loader) {
        // This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
        // We need to make sure we are no longer using it.
        mForecastAdapter.swapCursor(null);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getLoaderManager().initLoader(FORECAST_LOADER_ID, null, this); // this class itself implements LoaderManager.LoaderCallbacks
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
 
        // The CursorAdapter will take data from our cursor and populate the ListView.
        mForecastAdapter = new ForecastAdapter(getActivity(), null, 0);
        ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast);
        listView.setAdapter(mForecastAdapter);
        :
    OR
        // For a simple view (that doesn't need Adapter):
        Nothing needs to be done!
    }

    private void updateWeather() {
        FetchWeatherTask weatherTask = new FetchWeatherTask(getActivity());
        String location = Utility.getPreferredLocation(getActivity());
        weatherTask.execute(location);
    }

    @Override
    public void onStart() {
        updateWeather();
    }

    public void onLocationChanged() {
//        updateWeather();
        // Starts a new or restarts an existing Loader in this manager, registers the callbacks to it,
        // and (if the activity/fragment is currently started) starts loading it.
        getLoaderManager().restartLoader(FORECAST_LOADER_ID, null, this);
    }

} // ForecastFragment


// Exposes a list (of weather forecasts) from Cursor ListView
public class ForecastAdapter extends CursorAdapter {

    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View view = LayoutInflater.from(context).inflate(R.layout.list_item_forecast, parent, false);
        return view;
    }

    // fill-in the views with the contents of the cursor.
    public void bindView(View view, Context context, Cursor cursor) {
        (TextView)view.setText(convertCursorRowToUXFormat(cursor));
    }

} // CursorAdapter


// Read, parse Json and add (bulkinsert) records into database
public class FetchWeatherTask extends AsyncTask<String, Void, Void> {

    protected Void doInBackground(String... params) {
        Uri builtUri = Uri.parse("http://api.openweathermap.org/data/2.5/forecast/daily?").buildUpon()
                .appendQueryParameter("q", params[0])
                .appendQueryParameter("mode", format)
                .appendQueryParameter("units", units)
                .appendQueryParameter("cnt", Integer.toString(numDays))
                .build();
        URL url = new URL(builtUri.toString());
        urlConnection = (HttpURLConnection) url.openConnection();
        :
        inserted = mContext.getContentResolver().bulkInsert(WeatherEntry.CONTENT_URI, cvArray);
    }
} // FetchWeatherTask




No comments:

Post a Comment