一.基本概念 1.LoaderManager LoaderManager用來負責管理與Activity或者Fragment聯繫起來的一個或多個Loaders對象. 每個Activity或者Fragment都有唯一的一個LoaderManager實例(通過getLoaderManager()方法獲得) ...
一.基本概念
1.LoaderManager
LoaderManager用來負責管理與Activity或者Fragment聯繫起來的一個或多個Loaders對象.
每個Activity或者Fragment都有唯一的一個LoaderManager實例(通過getLoaderManager()方法獲得),用來啟動,停止,保持,重啟,關閉它的Loaders,這些功能可通過調用initLoader()/restartLoader()/destroyLoader()方法來實現.
LoaderManager並不知道數據如何裝載以及何時需要裝載.相反,它只需要控制它的Loaders們開始,停止,重置他們的Load行為,在配置變換或數據變化時保持loaders們的狀態,並使用介面來返回load的結果.
2.Loader
Loades負責在一個單獨線程中執行查詢,監控數據源改變,當探測到改變時將查詢到的結果集發送到註冊的監聽器上.Loader是一個強大的工具,具有如下特點
(1)它封裝了實際的數據載入.
Activity或Fragment不再需要知道如何載入數據.它們將該任務委托給了Loader,Loader在後臺執行查詢要求並且將結果返回給Activity或Fragment.
(2)客戶端不需要知道查詢如何執行.Activity或Fragment不需要擔心查詢如何在獨立的線程中執行,Loder會自動執行這些查詢操作.
這種方式不僅減少了代碼複雜度同事也消除了線程相關bug的潛在可能.
(3)它是一種安全的事件驅動方式.
Loader檢測底層數據,當檢測到改變時,自動執行並載入最新數據.
這使得使用Loader變得容易,客戶端可以相信Loader將會自己自動更新它的數據.
Activity或Fragment所需要做的就是初始化Loader,並且對任何反饋回來的數據進行響應.除此之外,所有其他的事情都由Loader來解決.
二.非同步Loader的實現原理
(1)執行非同步載入的任務.為了確保在一個獨立線程中執行載入操作,Loader的子類必須繼承AsyncTaskLoader<D>而不是Loader<D>類.
AsyncTaskLoader<D>是一個抽象Loader,它提供了一個AsyncTask來做它的執行操作.
當定義子類時,通過實現抽象方法loadInBackground方法來實現非同步task.該方法將在一個工作線程中執行數據載入操作.
(2)在一個註冊監聽器中接收載入完成返回的結果.
對於每個Loader來說,LoaderManager註冊一個OnLoadCompleteListener<D>,該對象將通過調用onLoadFinished(Loader<D> loader, D result)方法使Loader將結果傳送給客戶端.
Loader通過調用Loader#deliverResult(D result),將結果傳遞給已註冊的監聽器.
三.Loader三種不同狀態.
已啟動: 處於已啟動狀態的Loader會執行載入操作,併在任何時間將結果傳遞到監聽器中.已啟動的Loader將會監聽數據改變,當檢測到改變時執行新的載入. 一旦啟動,Loader將一直 處在已啟動狀態,一直到轉換到已停止和重置,這是唯一一種onLoadFinished永遠都會調用的狀態。
已停止: 處於已停止狀態的Loader將會繼續監聽數據改變,但是不會將結果返回給客戶端.在已停止狀態,Loader可能被啟動或者重啟.
重置: 當Loader處於重置狀態時,將不會執行新的載入操作,也不會發送新的結果,也不會檢測數據變化.
當一個Loader進入重置狀態,它必須解除對應的數據引用,方便垃圾回收(客戶端也必須確定,在Loader無效之後,移除了所有對該數據的引用).
通常,重置Loader不會兩次調用.然而,在某些情況下他們可能會啟動,所以如果必要的話,它們必須能夠適時重置.
四.接收Loader數據改變的通知.
必須有一個觀察者接受數據源改變的通知.
Loader必須實現這些Observer其中之一(ContentObserver,BroadcastReceiver等),來檢測底層數據源的改變.
當檢測到數據改變,觀察者必須調用Loader#onContentChanged().在該方法中執行兩種不同操作:
(1)如果Loader已經處於啟動狀態,就會執行一個新的載入操作;
(2)設置一個flag標識數據源有改變,這樣當Loader再次啟動時,就知道應該重新載入數據了.
五.CursorLoader實現LoaderManager.LoaderCallbacks介面方法.介面聲明及使用如下:
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);
}
onCreateLoader方法將在創建Loader時候調用,此時需要提供查詢的配置,如監聽一個URI.
這個方法會在loader初始化的時調用,即調用下麵的代碼時調用:
getLoaderManager().initLoader(id, bundle, loaderCallbacks);
initLoader函數原型為:
<D> Loader<D> android.app.LoaderManager.initLoader(int id, Bundle bundle, LoaderCallbacks<D> loaderCallbacks);
第1個參數loader的ID,可自定義一個常量值,便於實現多個Loader;
第2個參數一般置null;
第3個參數是實現了LoaderManager.LoaderCallbacks實現類對象.
onLoadFinished方法,在Loader完成任務後調用,一般在此讀取結果.
onLoaderReset方法是在配置發生變化時調用,一般調用下麵的代碼後調用:
getLoaderManager().restartLoader(id, bundle, loaderCallbacks);
restartLoader方法參數同initLoader,重新初始化loader之後,需要用來釋放對前面loader查詢到的結果引用.
六.一個具體監聽通話記錄的示例
在Activity中onCreate調用
CallLogsLoaderListener callLogsCallback = new CallLogsLoaderListener(this);
getLoaderManager().initLoader(0, null, callLogsCallback);
在onDestroy調用
getLoaderManager().destroyLoader(0);
配置許可權
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
具體的類實現
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 CallLogsLoaderListener(Context context) { mContext = context; } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { CursorLoader loader = new CursorLoader(mContext, CallLog.Calls.CONTENT_URI, null, null, null, null) { @Override public Cursor loadInBackground() { Cursor cursor = super.loadInBackground(); if (cursor == null) return null; CallLogsCursor callLogsCursor = new CallLogsCursor(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.Calls.CACHED_NAME); int numberIndex = cursor.getColumnIndex(CallLog.Calls.NUMBER); int typeIndex = cursor.getColumnIndex(CallLog.Calls.TYPE); int dateIndex = cursor.getColumnIndex(CallLog.Calls.DATE); int durationIndex = cursor.getColumnIndex(CallLog.Calls.DURATION); // 從游標的最後索引往前查詢,因為最新的通話記錄在最後 for (cursor.moveToLast(); !cursor.isBeforeFirst(); cursor .moveToPrevious()) { // 姓名 String name = cursor.getString(nameIndex); // 號碼 String number = cursor.getString(numberIndex); // 類型 int type = cursor.getInt(typeIndex); // 日期 long date = cursor.getLong(dateIndex); // 通話時長 int duration = cursor.getInt(durationIndex); Log.d("debug", "name=" + name + ", number=" + number + ", type=" + type + ", date=" + date + ", duration=" + duration); } } } }