LoaderManager and CursorLoader usage, cursorloader
I. Basic Concepts
1. LoaderManager
LoaderManager is used to manage one or more Loaders objects associated with the Activity or Fragment.
Each Activity or Fragment has a unique LoaderManager instance (obtained through the getLoaderManager () method) used to start, stop, maintain, restart, and disable its Loaders, these functions can be implemented by calling the initLoader ()/restartLoader ()/destroyLoader () method.
LoaderManager does not know how to load and when to load data. instead, it only needs to control its Loaders to start, stop, reset their Load behavior, and maintain the loaders state during configuration changes or data changes, and use the interface to return the load result.
2. Loader
Loades is responsible for executing queries in a separate thread to monitor changes to the data source. When a change is detected, the queried result set is sent to the registered listener. loader is a powerful tool with the following features:
(1) It encapsulates the actual data loading.
Activity or Fragment no longer need to know how to load data. They delegate the task to Loader, Loader executes query requirements in the background and returns the results to Activity or Fragment.
(2) the client does not need to know how to execute the query. Activity or Fragment, and does not need to worry about how to execute the query in an independent thread. Loder will automatically execute these query operations.
This method not only reduces Code complexity, but also eliminates the potential possibility of thread-related bugs.
(3) It is a secure event-driven approach.
Loader detects the underlying data. When it detects a change, it automatically executes and loads the latest data.
This makes it easy to use Loader. The client can believe that Loader will automatically update its data.
What Activity or Fragment needs to do is to initialize the Loader and respond to any feedback data. In addition, all other things are solved by the Loader.
II. Implementation principle of asynchronous Loader
(1) execute the asynchronous loading task. To ensure that the loading operation is performed in an independent thread, the Loader subclass must inherit the AsyncTaskLoader <D> rather than the Loader <D> class.
AsyncTaskLoader <D> is an abstract Loader that provides an AsyncTask to perform its operations.
When defining child classes, asynchronous tasks are implemented by implementing the abstract method loadInBackground. This method is used to load data in a work thread.
(2) receive the returned results after loading in a register listener.
For each Loader, LoaderManager registers an OnLoadCompleteListener <D>. this object will send the result to the client by calling the onLoadFinished (Loader <D> loader, D result) method.
Loader passes the result to the registered listener by calling Loader # deliverResult (D result.
Iii. Loader three different states.
Started: a Loader in the started state will perform the load operation and pass the result to the listener at any time. the started Loader will listen for data changes and execute new loads when the changes are detected. once started, the Loader will remain in the started State until it is switched to stopped and reset. This is the only State that onLoadFinished will always call.
Stopped: The Loader in the stopped status will continue to listen for data changes, but will not return the results to the client. In the stopped status, the Loader may be started or restarted.
Reset: When the Loader is in the reset status, no new loading operation will be executed, no new results will be sent, and no data changes will be detected.
When a Loader enters the reset status, it must release the corresponding data reference to facilitate garbage collection (the client must also determine that after the Loader is invalid, all references to the data are removed ).
In general, Loader resetting won't be called twice. However, in some cases they may start, so they must be able to reset in due time if necessary.
4. Receive notifications of Loader data changes.
An observer must be notified of changes to the data source.
Loader must implement one of these observers (ContentObserver, BroadcastReceiver, etc.) to detect changes to the underlying data source.
When data changes are detected, the observer must call Loader # onContentChanged (). Two different operations are performed in this method:
(1) If the Loader is already started, a new loading operation will be executed;
(2) Set a flag to identify that the data source has changed, so that when the Loader starts again, it will know that the data should be reloaded.
5. CursorLoader implements the LoaderManager. LoaderCallbacks interface method. The interface declaration and usage are as follows:
Public interface LoaderCallbacks <D> {
Public Loader <D> onCreateLoader (int id, Bundle args );
Public void onLoadFinished (Loader <D> loader, D data );
Public void onLoaderReset (Loader <D> loader );
}
The onCreateLoader method is called when the Loader is created. You need to provide the query configuration, for example, listening to a URI.
This method is called during loader initialization, that is, when the following code is called:
GetLoaderManager (). initLoader (id, bundle, loaderCallbacks );
The initLoader function is prototype:
<D> Loader <D> android. app. LoaderManager. initLoader (int id, Bundle bundle, LoaderCallbacks <D> loaderCallbacks );
ID of the loader with 1st parameters. A constant value can be customized to facilitate multiple loaders;
2nd parameters are generally set to null;
The 3rd parameters implement the LoaderManager. LoaderCallbacks implementation class object.
The onLoadFinished method is called after the Loader completes the task. Generally, the result is read here.
The onLoaderReset method is called when the configuration changes. Generally, the following code is called:
GetLoaderManager (). restartLoader (id, bundle, loaderCallbacks );
The parameters of the restartLoader method are the same as those of initLoader. After the loader is reinitialized, it must be used to release reference to the results queried by the previous loader.
6. Example of a specific listener call record
OnCreate call in Activity
CallLogsLoaderListener callLogsCallback = new CallLogsLoaderListener (this );
GetLoaderManager (). initLoader (0, null, callLogsCallback );
Call in onDestroy
GetLoaderManager (). destroyLoader (0 );
Configure permissions
<Uses-permission android: name = "android. permission. WRITE_CALL_LOG"/>
<Uses-permission android: name = "android. permission. READ_CALL_LOG"/>
Specific class implementation
Import android. app. loaderManager; import android. content. context; import android. content. cursorLoader; import android. content. loader; import android. database. cursor; import android. database. cursorWrapper; import android. OS. bundle; import android. provider. callLog; import android. util. log; public class CallLogsLoaderListener implements LoaderManager. loaderCallbacks <Cursor> {private Context mContext; public C AllLogsLoaderListener (Context context) {mContext = context; }@ Override public Loader <Cursor> onCreateLoader (int id, Bundle args) {CursorLoader loader = new CursorLoader (mContext, CallLog. CILS. CONTENT_URI, null) {@ Override public Cursor loadInBackground () {Cursor cursor = super. loadInBackground (); if (cursor = null) return null; CallLogsCursor callLogsCursor = new CallLogsC Ursor (cursor); return callLogsCursor ;}}; return loader ;}@ Override public void onLoadFinished (Loader <Cursor> loader, Cursor data) {}@ Override public void onLoaderReset (Loader <Cursor> loader) {} public class CallLogsCursor extends CursorWrapper {public CallLogsCursor (Cursor cursor) {super (cursor); int nameIndex = cursor. getColumnIndex (CallLog. CILS. CACHED_NAME); int numberIndex = cursor. get ColumnIndex (CallLog. CILS. NUMBER); int typeIndex = cursor. getColumnIndex (CallLog. CILS. TYPE); int dateIndex = cursor. getColumnIndex (CallLog. CILS. DATE); int durationIndex = cursor. getColumnIndex (CallLog. CILS. DURATION); // query forward from the last index of the cursor, because the latest call record is in the last for (cursor. moveToLast ();! Cursor. isBeforeFirst (); cursor. moveToPrevious () {// name String name = cursor. getString (nameIndex); // number String number = cursor. getString (numberIndex); // type int type = cursor. getInt (typeIndex); // date long date = cursor. getLong (dateIndex); // call duration int duration = cursor. getInt (durationIndex); Log. d ("debug", "name =" + name + ", number =" + number + ", type =" + type + ", date =" + date + ", duration = "+ duration );}}}}