2016年1月6日 星期三

cursor loader

LoaderManager with CursorLoader
The loader run in a Async Task,when the database data chnage, cursor loader auto update the Cursor

The loder will auto re connect to the last create Cursor,after the loder recreated,for example rotate the phone

only use contentProvider can use the CursorLoader


A loader register a Loder ID with the Loder Manager. It cab live beyond the life cycle of the activity and fragment that associte with.


Loader moniter the data sourcer, and deiver new result to coursor whe ncontent change.


The different between content provider that run in AsyncTask and AsyncTaskloader


when the Mashine rotate, the asyntask  do in Background will execute again


in asynctask loader,although we rotate the the machine before onLoad Finish, the LoadinBackfround will not execute again, kepp loading. However, if the load finish before the activity restart(machine rotate),the AsyncTask loader will load again


Cursor Loader.
Cursor almost same to the AsyncTask loader.But:


when Load finish before the activity restart. When the activity restart,the curosr smmideately send to the UI, no need to load again


why we use  Loader?


Inthe original design in our APP:
The UI thrread start a AsyncTask .than the task thread use the ContentResolver by contentProvider to get the data.


New Design:


Use async task to load the weather form the internate, and than write to the SqliteDb by weather provider


Read:by cursor loader


Create a cousor loader in three easy step
1.create a loder ID
2.Fill in loder callback
3.init loader with loader manager


1.create a loder ID
private static final int FORECAST_LOADER = 0;


2.Fill in loder callback
implements LoaderManager.LoaderCallbacks<Cursor> Firist!!


onCreateLoader create and return cursorLoder


public Loader onCreateLoader(int id, Bundle args) {
  String loactionsetting = Utility.getPreferredLocation(getActivity());
  String sortOrder = WeatherContract.WeatherEntry.COLUMN_DATE + " ASC";
  Uri weatherForURI = WeatherContract.WeatherEntry.buildWeatherLocationWithStartDate(loactionsetting, System.currentTimeMillis());


  return new CursorLoader(getActivity(), weatherForURI, null, null, null, sortOrder);
}


onLoadFinished call when the load finish and data is finish


if the cursor adapter need to use the cursor, just call adapter.swapCursor is ok


mForecastAdapter.swapCursor(data);


onLoaderReset is called when the Loader is destoryed,so we need to call swapCursor (null) at here


mForecastAdapter.swapCursor(null);


3.init loader with loader manager


public void onActivityCreated(Bundle savedInstanceState) {
  
getLoaderManager().initLoader(FORECAST_LOADER, savedInstanceState, this);


  super.onActivityCreated(savedInstanceState);}





Using Projection
at the before,we query all the column in the database,but we can doit more effecient. That is onlu query the column we need.So we neeed to seat the Projection at the cursor loader.


First,we set the column we need in the string array,


private static final String[] FORECAST_COLUMNS = {
      // In this case the id needs to be fully qualified with a table name, since
      // the content provider joins the location & weather tables in the background
      // (both have an _id column)
      // On the one hand, that's annoying.  On the other, you can search the weather table
      // using the location set by the user, which is only in the Location table.
      // So the convenience is worth it.
      WeatherContract.WeatherEntry.TABLE_NAME + "." + WeatherContract.WeatherEntry._ID,
      WeatherContract.WeatherEntry.COLUMN_DATE,
      WeatherContract.WeatherEntry.COLUMN_SHORT_DESC,
      WeatherContract.WeatherEntry.COLUMN_MAX_TEMP,
      WeatherContract.WeatherEntry.COLUMN_MIN_TEMP,
      WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING,
      WeatherContract.WeatherEntry.COLUMN_WEATHER_ID,
      WeatherContract.LocationEntry.COLUMN_COORD_LAT,
      WeatherContract.LocationEntry.COLUMN_COORD_LONG
};


important!!!!  WeatherContract.WeatherEntry.TABLE_NAME + "." + WeatherContract.WeatherEntry._ID, need ,because  we use join ,show we need to specifice the ID we use,that is  WeatherContract.WeatherEntry.TABLE_NAME + "." + WeatherContract.WeatherEntry._ID


in content provider:


we set:


sWeatherByLocationSettingQueryBuilder.setTables(
      WeatherContract.WeatherEntry.TABLE_NAME + " INNER JOIN " +
              WeatherContract.LocationEntry.TABLE_NAME +
              " ON " + WeatherContract.WeatherEntry.TABLE_NAME +
              "." + WeatherContract.WeatherEntry.COLUMN_LOC_KEY +
              " = " + WeatherContract.LocationEntry.TABLE_NAME +
              "." + WeatherContract.LocationEntry._ID);
than


we nedd to set the index:


static final int COL_WEATHER_ID = 0;
static final int COL_WEATHER_DATE = 1;
static final int COL_WEATHER_DESC = 2;
static final int COL_WEATHER_MAX_TEMP = 3;
static final int COL_WEATHER_MIN_TEMP = 4;
static final int COL_LOCATION_SETTING = 5;
static final int COL_WEATHER_CONDITION_ID = 6;
static final int COL_COORD_LAT = 7;
static final int COL_COORD_LONG = 8;


we use these when we get the data from the cursor.


onItemClick for cursor adapter and list view
we can use
Cursor cursor = (Cursor) adapterView.getItemAtPosition(position);
to get back the cursor in the cursor adapter.


Making your ContentProvider Accessible
<provider
  android:name="com.example.android.sunshine.app.data.WeatherProvider"
  android:authorities="com.example.android.sunshine.app"
  android:exported="true"/>


Android 之ContentProvider 之 Permission权限设置




Internal provider that we can use

http://developer.android.com/intl/zh-tw/reference/android/provider/package-summary.html



The flow of the cursor loader



1.getSupportLoaderManager().initLoader(_id, null, this);
2.onCreateLoader
3.onLoadFinished

**_id is the loder ID

Normally,is 1>2>3

However, if 2 is already finished,and call the 1 afain and the _id is the same,than 2 will not run,
1>3

if the loder ID is change, 1>2>3