https://developer.android.com/reference/android/content/AsyncTaskLoader.html
什么是loader loader是干什么的
Loader一個執行異步數據加載的類。在Loader處于活動狀態時,應該監視其數據源并在內容發生更改時提供新的結果。
Loader有兩個子類,AsyncTaskLoader和CursorLoader
在本次簡單實例中我們用的就是CursorLoader進行加載數據。對Cursor進行操作,建議使用CursorLoader。
有時CursorLoader不能滿足我們的需求,需要我們自定義Loader,一般情況下,我們不直接集成Loader,而是繼承AsyncTaskLoader。
子類通常必須至少實現onStartLoading(),onStopLoading(),onForceLoad()和onReset()。
- 在單獨的線程中加載數據,并不會阻塞UI線程
- 監聽數據的變化
- Activity配置發生變化如屏幕橫豎屏切換,不會在重新加載數據。
什么是LoaderManager
LoaderManager是專門管理Loader的管理器,一個Activity或者Fragment只能有一個LoaderManager,一個LoaderManager可以管理多個Loader,LoaderManager管理Loader的各種事件。
Loader的簡單使用
public class LoaderDemoActivity extends AppCompatActivity {
private ListView listView;
private SimpleCursorAdapter adapter;
private CursorLoader loader;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loader_demo);
initView();
}
private void initView() {
listView = (ListView) findViewById(R.id.lv_loader_demo);
adapter = new SimpleCursorAdapter(this, R.layout.item_loader_demo, null, new String[]{MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.DATA}, new int[]{R.id.tv_loader_music, R.id.tv_loader_who_music, R.id.tv_loader_path}, 0);
listView.setAdapter(adapter);
getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Toast.makeText(LoaderDemoActivity.this, "創建Loader", Toast.LENGTH_SHORT).show();
loader = new CursorLoader(LoaderDemoActivity.this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
return loader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Toast.makeText(LoaderDemoActivity.this, "Loader加載完成", Toast.LENGTH_SHORT).show();
adapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
Toast.makeText(LoaderDemoActivity.this, "Loader重置", Toast.LENGTH_SHORT).show();
adapter.swapCursor(null);
}
});
}
}
在Activity中創建一個Loader,首先調用getLoaderManager方法,獲取LoaderManager,在調用initLoader方法會返回一個Loader。
- initLoader
確保Loader已經初始化,并且處于活動狀態,如果Loader不存在,會創建一個加載器,如果存在會復用之前的加載器。initLoader有三個參數:
- id 區分不同的Loader
- Bundle 一個可選的參數
- LoaderManager.LoaderCallbacks<D> callback 回調接口
LoaderManager.LoaderCallbacks<D> callback回調存在三個方法:
- onCreateLoader 實例化并根據給定的id返回一個新的Loader
- onLoadFinished 在加載完成時調用 data是由Loader生成的數據
- onLoaderReset 表示正在重置,在這個方法中應該刪除對數據的引用。
Loader類源碼分析
接口
- public final class ForceLoadContentObserver extends ContentObserver
public final class ForceLoadContentObserver extends ContentObserver {
public ForceLoadContentObserver() {
super(new Handler());
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(boolean selfChange) {
onContentChanged();
}
}
deliverSelfNotifications 如果返回true,觀察者將自我接受更改通知
ContentObserver的一個實現類,負責將他鏈接到Loader,當觀察者告訴它數據已經改變時,Loader會重新加載數據,通常不需要自己使用,當cursor的數據發生改變時,CursorLoader會使用它來執行更新。onChange 當觀察的內容發生改變時才被調用。子類應該重寫來處理內容更改的邏輯。
public interface OnLoadCompleteListener<D>
public interface OnLoadCompleteListener<D> {
/**
* Called on the thread that created the Loader when the load is complete.
*
* @param loader the loader that completed the load
* @param data the result of the load
*/
public void onLoadComplete(Loader<D> loader, D data);
}
OnLoadCompleteListener是一個接口,用于發現loader何時加載完數據。不需要我們來實現,是由LoaderManager實現。
唯一方法 onLoadComplete 在加載完成時,調用在創建Loader的線程,data就是加載的結果。
- OnLoadCanceledListener
public interface OnLoadCanceledListener<D> {
/**
* Called on the thread that created the Loader when the load is canceled.
*
* @param loader the loader that canceled the load
*/
public void onLoadCanceled(Loader<D> loader);
}
用于在加載數據完之前檢查加載器何時取消。也是,不需要我們來實現,LoaderManager會處理。onLoadCanceled唯一實現方法,在此方法調用時,應該釋放數據。
方法
- deliverResult
public void deliverResult(D data) {
if (mListener != null) {
mListener.onLoadComplete(this, data);
}
}
將加載的結果返回給注冊的監聽器。只能由子類調用。必須從進程的主線程中調用。
- deliverCancellation
public void deliverCancellation() {
if (mOnLoadCanceledListener != null) {
mOnLoadCanceledListener.onLoadCanceled(this);
}
}
通知已經注冊的Loader.OnLoadCanceledListener接口Loader已經取消。只能由子類調用。必須從進程的主線程中調用。
- getContext getId
/**
* @return an application context retrieved from the Context passed to the constructor.
*/
public Context getContext() {
return mContext;
}
/**
* @return the ID of this loader
*/
public int getId() {
return mId;
}
返回上下文
返回Loader的ID
- registerListener 和 unregisterListener
/**
* Registers a class that will receive callbacks when a load is complete.
* The callback will be called on the process's main thread so it's safe to
* pass the results to widgets.
*
* <p>Must be called from the process's main thread.
*/
public void registerListener(int id, OnLoadCompleteListener<D> listener) {
if (mListener != null) {
throw new IllegalStateException("There is already a listener registered");
}
mListener = listener;
mId = id;
}
/**
* Remove a listener that was previously added with {@link #registerListener}.
*
* Must be called from the process's main thread.
*/
public void unregisterListener(OnLoadCompleteListener<D> listener) {
if (mListener == null) {
throw new IllegalStateException("No listener register");
}
if (mListener != listener) {
throw new IllegalArgumentException("Attempting to unregister the wrong listener");
}
mListener = null;
}
- registerListener:注冊一個將在加載完成時接受回調的類。必須主線程中調用
- unregisterListener:移除監聽器
- registerOnLoadCanceledListener 和 unregisterOnLoadCanceledListener
/**
* Registers a listener that will receive callbacks when a load is canceled.
* The callback will be called on the process's main thread so it's safe to
* pass the results to widgets.
*
* Must be called from the process's main thread.
*
* @param listener The listener to register.
*/
public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
if (mOnLoadCanceledListener != null) {
throw new IllegalStateException("There is already a listener registered");
}
mOnLoadCanceledListener = listener;
}
/**
* Unregisters a listener that was previously added with
* {@link #registerOnLoadCanceledListener}.
*
* Must be called from the process's main thread.
*
* @param listener The listener to unregister.
*/
public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
if (mOnLoadCanceledListener == null) {
throw new IllegalStateException("No listener register");
}
if (mOnLoadCanceledListener != listener) {
throw new IllegalArgumentException("Attempting to unregister the wrong listener");
}
mOnLoadCanceledListener = null;
}
- registerOnLoadCanceledListener:注冊一個將在加載被取消時接收回調的偵聽器。
- unregisterOnLoadCanceledListener:取消注冊的registerOnLoadCanceledListener(Loader.OnLoadCanceledListener)的偵聽器
- isStarted 和 isAbandoned和isReset
/**
* Return whether this load has been started. That is, its {@link #startLoading()}
* has been called and no calls to {@link #stopLoading()} or
* {@link #reset()} have yet been made.
*/
public boolean isStarted() {
return mStarted;
}
/**
* Return whether this loader has been abandoned. In this state, the
* loader <em>must not</em> report any new data, and <em>must</em> keep
* its last reported data valid until it is finally reset.
*/
public boolean isAbandoned() {
return mAbandoned;
}
/**
* Return whether this load has been reset. That is, either the loader
* has not yet been started for the first time, or its {@link #reset()}
* has been called.
*/
public boolean isReset() {
return mReset;
}
- isStarted:返回這個Loader是否已經被加載。如果返回true表示已經調用了startLoading方法,并且沒有調用stopLoading方法或者reset方法。
- isAbandoned:返回這個Loader是否被放棄。如果返回true,Loader不能發送任何新的數據,并且必須保持上次發送的數據有效值。知道調用reset。
- isReset:返回這個Loader是否被重置。也就是說,加載程序尚未第一次啟動,或者調用了reset()。
startLoading
public final void startLoading() {
mStarted = true;
mReset = false;
mAbandoned = false;
onStartLoading();
}
當相關的Activity或者Fragment正在啟動時,LoaderManager通常會自動調用這個方法,與LoaderManager一起使用時,不要自己調用這個方法,否則會和LoaderManager發生沖突。loader會異步加載數據,當結果準備就緒時,回調會發生在主線程。如果之前的數據加載完成,并且有效,則結果立即傳遞給回調,Loader將監聽數據源,如果有更改,則提供新的數據。調用stopLoading()將停止傳遞回調。
這會更新Loader的內部狀態,以便isStarted()和isReset()將返回正確的值,然后調用實現的onStartLoading()。
必須從進程的主線程中調用。
cancelLoad
public boolean cancelLoad() {
return onCancelLoad();
}
嘗試取消當前加載任務。
取消的操作不是立即執行,因為數據加載在后臺進程。如果loader當前正在進行加載,此時調用這個方法要求取消加載,在這種情況,一旦后臺進程完成加載工作,其余的狀態都會被清除。如果在此期間另一個Loader請求加載,它會一直保持取消加載完成。
此方法有個boolean返回值,如果任務無法取消,返回false,通常是因為loader在后臺已經正常完成了,也有可能沒有調用startLoading。如果返回true,任務仍在繼續運行,當任務完成時將調用Loader.OnLoadCanceledListener。
- onCancelLoad
protected boolean onCancelLoad() {
return false;
}
子類必須實現這個方法,處理取消的邏輯。
- forceLoad 和 onForceLoad
public void forceLoad() {
onForceLoad();
}
/**
* Subclasses must implement this to take care of requests to {@link #forceLoad()}.
* This will always be called from the process's main thread.
*/
protected void onForceLoad() {
}
- forceLoad 強制進行異步加載。和startLoading不同,忽略以前的數據,加載一個新的數據。通常只有加載程序啟動時才應該調用這個方法,也就是說isStart返回true。
- 子類必須實現這個方法來處理forceLoad的請求。
- stopLoading 和 onStopLoading
public void stopLoading() {
mStarted = false;
onStopLoading();
}
/**
* Subclasses must implement this to take care of stopping their loader,
* as per {@link #stopLoading()}. This is not called by clients directly,
* but as a result of a call to {@link #stopLoading()}.
* This will always be called from the process's main thread.
*/
protected void onStopLoading() {
}
- stopLoading
通常來說會被Loader Manager自動調用,當相關的Activity或者Fragment停止時,不需要自己調用,否則會和LoaderManager發生沖突。
停止傳遞更新,知道下一此調用startLoading。在此時實現不應該數據無效,客戶端仍然可以使用上一次發送的數據。但是如果數據發生改變,就不會發送新的數據。仍然會監聽數據源更改,但是不發送新數據給客戶端,直到startLoading被調用。
這將更新Loader的內部狀態,以便isStarted()將返回正確的值,然后調用實現的onStopLoading()。
- onStopLoading:子類必須實現這個來停止它們的加載器。這不是由客戶端直接調用,而是由stopLoading()調用結果。這將始終從進程的主線程中調用。
- abandon 和 onAbandon
public void abandon() {
mAbandoned = true;
onAbandon();
}
protected void onAbandon() {
}
abandon: 在LoaderManager重新啟動Loader時,通常會自動調用這個方法,和Loader Manager一起使用時,不能自己調用方法,否則會和LoaderManager發生沖突,告訴Loader,它正在被放棄,這是在reset之前,讓他保留當前的數據,但不發送任何新數據。
onAbandon:子類必須重新實現被放棄方法,這個是在onRest方法之前可選的中間狀態,這意味著客戶端對來自加載器的數據不在關注,此時Loader不需要在發送新的數據。但是Loader必須保證上次數據是有效的,直到onReset被調用,可以使用isAbandoned方法檢索當前的廢棄狀態。
- reset
public void reset() {
onReset();
mReset = true;
mStarted = false;
mAbandoned = false;
mContentChanged = false;
mProcessingChange = false;
}
protected void onReset() {
}
reset
這個方法通常是在Loader正在銷毀時被LoaderManager自動調用的,和LoaderManager一起使用時,自己不要去調用這個方法,否則會和LoaderManager發生沖突。
這個方法會重置Loader的狀態,在這一點上,應該釋放所有的資源,因為可用不會再次調用這個方法。
也有可能會調用startLoading方法,此時loader必須可以再次運行。
此方法會更新內部的狀態,以便isStarted()和isReset()將返回正確的值,然后調用實現的onReset()。onReset
子類要想重置Loader必須實現此方法,
- takeContentChanged
/**
* Take the current flag indicating whether the loader's content had
* changed while it was stopped. If it had, true is returned and the
* flag is cleared.
*/
public boolean takeContentChanged() {
boolean res = mContentChanged;
mContentChanged = false;
mProcessingChange |= res;
return res;
}
獲取當前的標志,查看Loader加載的內容是否在停止前發生了改變,如果有,返回true,,并清除標志位。
- commitContentChanged
public void commitContentChanged()
mProcessingChange = false;
}
根據takeContentChanged的返回值,提交已經處理完的更改的內容。與rollbackContentChanged()一起使用來處理加載被取消的情況。 當你處理一個Loader而不被取消的時候調用它。
- rollbackContentChanged
public void rollbackContentChanged() {
if (mProcessingChange) {
onContentChanged();
}
}
通知已放棄由takeContentChanged()返回的內容更改的處理,并且想要回滾到還有待處理內容更改的狀態。 這是為了處理在數據被傳送回加載程序之前由于內容改變而導致的數據加載已被取消的情況。
- onContentChanged
public void onContentChanged() {
if (mStarted) {
forceLoad();
} else {
// This loader has been stopped, so we don't want to load
//Loader已經停止了,所以我們不加載
// new data right now... but keep track of it changing to
//新的數據現在...但跟蹤它改變
// refresh later if we start again.
//如果再一次啟動,等待數據刷新
mContentChanged = true;
}
}
Loader.ForceLoadContentObserver檢測到有更改時調用, 默認實現檢查Loader是否當前啟動; 如果是,它只是簡單地調用forceLoad(); 否則,它設置一個標志,以便takeContentChanged()返回true。