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