日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android Loader 异步加载详解一:基础概念

發布時間:2024/9/30 Android 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android Loader 异步加载详解一:基础概念 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/70241844
本文出自【趙彥軍的博客】

Android Loader 異步加載詳解一:基礎概念
Android Loader 異步加載詳解二:探尋Loader內部機制

前言

Android 3.0 中引入了 Loader (加載器),支持輕松在 Activity 或片段中異步加載數據。 加載器具有以下特征:

  • 可用于每個 Activity 和 Fragment。
  • 支持異步加載數據。
  • 監控其數據源并在內容變化時傳遞新結果。
  • 在某一配置更改后重建加載器時,會自動重新連接上一個加載器的游標。 因此,它們無需重新查詢其數據。

Loader API概述說明

如下是我們開發中常用的一些Loader相關接口:

Class/InterfaceDescription
LoaderManager一個與Activity、Fragment關聯的抽象類,用于管理一個或多個Loader實例。每個Activity或Fragment只能有一個LoaderManager,而一個LoaderManager可以有多個Loader。
LoaderManager.LoaderCallbacks用于和LoaderManager交互的回調接口。譬如,可以使用onCreateLoader()創建一個新的Loader。
AsyncTaskLoader抽象的Loader,提供一個AsyncTask繼承實現。
CursorLoaderAsyncTaskLoader的子類,用于向ContentResover請求返回一個Cursor。該類以標準游標查詢實現了Loader協議,使用后臺線程進行查詢,使用這個Loader是從ContentProvider加載異步數據最好的方式。

啟動一個 Loader

initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback)

可以看見上面的initLoader()方法有三個參數:

  • 第一個參數代表當前Loader的ID;
  • 第二個參數代表提供給Loader構造函數的參數,可選;
  • 第三個參數代表LoaderManager.LoaderCallbacks的回調實現;

上面initLoader()方法的調用確保了一個Loader被初始化和激活的狀態,該方法的調運有如下兩種結果:

  • 如果代表該Loader的ID已經存在,則后面創建的Loader將直接復用已經存在的;
  • 如果代表該Loader的ID不存在,initLoader()會觸發LoaderManager.LoaderCallbacks回調的onCreateLoader()方法創建一個Loader;

可以看見通過initLoader()方法可以將LoaderManager.LoaderCallbacks實例與Loader進行關聯,且當Loader的狀態變化時就被回調。所以說,如果調用者正處于其開始狀態并且被請求的Loader已經存在,且已產生了數據,那么系統會立即調用onLoadFinished()(在initLoader()調用期間),所以你必須考慮到這種情況的發生。

當然了,intiLoader()會返回一個創建的Loader,但是你不用獲取它的引用,因為LoadeManager會自動管理該Loader的生命周期,你只用在它回調提供的生命周期方法中做自己數據邏輯的處理即可。

Loader 基類的源碼分析

Loader 是最底層的代碼邏輯封裝,沒有具體的業務實現的部分。這個有點像我們平時寫的 BaseActivity 一樣。現在我們來對這個基類的源碼做一個簡單的分析。

public class Loader<D> {int mId;OnLoadCompleteListener<D> mListener;OnLoadCanceledListener<D> mOnLoadCanceledListener;Context mContext;boolean mStarted = false;boolean mAbandoned = false;boolean mReset = true;boolean mContentChanged = false;boolean mProcessingChange = false;//數據源變化監聽器(觀察者模式),實現了ContentObserver類public final class ForceLoadContentObserver extends ContentObserver {public ForceLoadContentObserver() {super(new Handler());}@Overridepublic boolean deliverSelfNotifications() {return true;}@Overridepublic void onChange(boolean selfChange) {//實質是調運Loader的forceLoad方法onContentChanged();}}//Loader加載完成接口,當加載完成時Loader通知loaderManager,loaderManager再回調我們initLoader方法的callbackpublic 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);}//LoaderManager中監聽cancel,同上類似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);}//構造方法public Loader(Context context) {//mContext持有Application的Context,防止泄露內存等mContext = context.getApplicationContext();}//加載完成時回調傳遞加載數據結果,實質是對OnLoadCompleteListener接口方法的封裝public void deliverResult(D data) {if (mListener != null) {mListener.onLoadComplete(this, data);}}//類似同上,對OnLoadCanceledListener的方法的封裝public void deliverCancellation() {if (mOnLoadCanceledListener != null) {mOnLoadCanceledListener.onLoadCanceled(this);}}public Context getContext() {return mContext;}public int getId() {return mId;}public void registerListener(int id, OnLoadCompleteListener<D> listener) {if (mListener != null) {throw new IllegalStateException("There is already a listener registered");}mListener = listener;mId = id;}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;}public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {if (mOnLoadCanceledListener != null) {throw new IllegalStateException("There is already a listener registered");}mOnLoadCanceledListener = listener;}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;}public boolean isStarted() {return mStarted;}public boolean isAbandoned() {return mAbandoned;}public boolean isReset() {return mReset;}//開始加載數據時LoaderManager會調用該方法//必須在 main thread 線程調用public final void startLoading() {mStarted = true;mReset = false;mAbandoned = false;onStartLoading();}//真正開始加載數據的地方******空方法,子類實現!!!!!!protected void onStartLoading() {}//取消Loader的方法public boolean cancelLoad() {return onCancelLoad();}//真正取消的地方******,子類實現!!!!!!return false表示取消失敗(因為已完成或未開始)protected boolean onCancelLoad() {return false;}//強制重新Loader,放棄舊數據public void forceLoad() {onForceLoad();}//真正重新Loader的地方******空方法,子類實現!!!!!!protected void onForceLoad() {}//停止 Loading ,具體實現交給子類//必須在 main 線程調用public void stopLoading() {mStarted = false;onStopLoading();}//空實現protected void onStopLoading() {}//同上public void abandon() {mAbandoned = true;onAbandon();}//同上protected void onAbandon() {}//同上public void reset() {onReset();mReset = true;mStarted = false;mAbandoned = false;mContentChanged = false;mProcessingChange = false;}//同上protected void onReset() {}//Loader數據變化的一些標記處理public boolean takeContentChanged() {boolean res = mContentChanged;mContentChanged = false;mProcessingChange |= res;return res;}public void commitContentChanged() {mProcessingChange = false;}public void rollbackContentChanged() {if (mProcessingChange) {onContentChanged();}}//上面ForceLoadContentObserver內部類的onChange方法調運public void onContentChanged() {if (mStarted) {forceLoad();} else {// This loader has been stopped, so we don't want to load// new data right now... but keep track of it changing to// refresh later if we start again.mContentChanged = true;}}//一些方便調試的方法public String dataToString(D data) {StringBuilder sb = new StringBuilder(64);DebugUtils.buildShortClassTag(data, sb);sb.append("}");return sb.toString();}//一些方便調試的方法@Overridepublic String toString() {StringBuilder sb = new StringBuilder(64);DebugUtils.buildShortClassTag(this, sb);sb.append(" id=");sb.append(mId);sb.append("}");return sb.toString();}//一些方便調試的方法public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {writer.print(prefix); writer.print("mId="); writer.print(mId);writer.print(" mListener="); writer.println(mListener);if (mStarted || mContentChanged || mProcessingChange) {writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);writer.print(" mContentChanged="); writer.print(mContentChanged);writer.print(" mProcessingChange="); writer.println(mProcessingChange);}if (mAbandoned || mReset) {writer.print(prefix); writer.print("mAbandoned="); writer.print(mAbandoned);writer.print(" mReset="); writer.println(mReset);}}

通過上面粗略的分析可以發現,Loader基類無非也就是一個方法接口的定義類,組織預留了一些方法供LoaderManager去調運處理,同時需要子類實現其提供的一些onXXX方法,以便LoaderManager調運Loader的方法時可以觸發Loader子類的實現邏輯。

AsyncTaskLoader 運用實例詳解

上面我們說了 Loader 只是一個基類,那么要實現具體的業務類,必須有 子類繼承Loader ,并且實現 Loader 里面的空方法。幸運的是,系統已經幫我們實現了一個子類 , 它就是 AsyncTaskLoader 。

AsyncTaskLoader 源碼分析:

public abstract class AsyncTaskLoader<D> extends Loader<D> {static final String TAG = "AsyncTaskLoader";static final boolean DEBUG = false;//LoadTask內部類是對AsyncTask的封裝,實現了Runnable接口final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {private final CountDownLatch mDone = new CountDownLatch(1);boolean waiting;//AsyncTask的子線程中執行AsyncTaskLoader的onLoadInBackground方法!!!!重點@Overrideprotected D doInBackground(Void... params) {try {D data = AsyncTaskLoader.this.onLoadInBackground();//把執行結果數據D返回到UI線程return data;} catch (OperationCanceledException ex) {if (!isCancelled()) {throw ex;}return null;}}//AsyncTask子線程執行完畢后在主線程回調AsyncTaskLoader的dispatchOnLoadComplete方法/* Runs on the UI thread */@Overrideprotected void onPostExecute(D data) {try {AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);} finally {mDone.countDown();}}/* Runs on the UI thread */@Overrideprotected void onCancelled(D data) {if (DEBUG) Log.v(TAG, this + " onCancelled");try {//取消AsyncTask時調用AsyncTaskLoader.this.dispatchOnCancelled(this, data);} finally {mDone.countDown();}}//Runnable的實現方法/* Runs on the UI thread, when the waiting task is posted to a handler.* This method is only executed when task execution was deferred (waiting was true). */@Overridepublic void run() {waiting = false;AsyncTaskLoader.this.executePendingTask();}/* Used for testing purposes to wait for the task to complete. */public void waitForLoader() {try {mDone.await();} catch (InterruptedException e) {// Ignore}}}private final Executor mExecutor;volatile LoadTask mTask;volatile LoadTask mCancellingTask;long mUpdateThrottle;long mLastLoadCompleteTime = -10000;Handler mHandler;//public構造方法 public AsyncTaskLoader(Context context) {this(context, AsyncTask.THREAD_POOL_EXECUTOR);}/** {@hide} 無法被外部調運的構造方法 */public AsyncTaskLoader(Context context, Executor executor) {super(context);mExecutor = executor;}public void setUpdateThrottle(long delayMS) {mUpdateThrottle = delayMS;if (delayMS != 0) {mHandler = new Handler();}}@Overrideprotected void onForceLoad() {super.onForceLoad();//取消當前的Loader執行cancelLoad();//新建task并執行mTask = new LoadTask();if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);executePendingTask();}@Overrideprotected boolean onCancelLoad() {if (DEBUG) Log.v(TAG, "onCancelLoad: mTask=" + mTask);if (mTask != null) {if (mCancellingTask != null) {if (mTask.waiting) {mTask.waiting = false;mHandler.removeCallbacks(mTask);}mTask = null;return false;} else if (mTask.waiting) {// There is a task, but it is waiting for the time it should// execute. We can just toss it.if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");mTask.waiting = false;mHandler.removeCallbacks(mTask);mTask = null;return false;} else {boolean cancelled = mTask.cancel(false);if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);if (cancelled) {mCancellingTask = mTask;cancelLoadInBackground();}mTask = null;return cancelled;}}return false;}/*** Called if the task was canceled before it was completed. Gives the class a chance* to clean up post-cancellation and to properly dispose of the result.** @param data The value that was returned by {@link #loadInBackground}, or null* if the task threw {@link OperationCanceledException}.*/public void onCanceled(D data) {}//LoadTask的Runnable方法run中執行void executePendingTask() {if (mCancellingTask == null && mTask != null) {if (mTask.waiting) {mTask.waiting = false;mHandler.removeCallbacks(mTask);}if (mUpdateThrottle > 0) {long now = SystemClock.uptimeMillis();if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {// Not yet time to do another load.mTask.waiting = true;mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);return;}}//真正的觸發執行AsyncTask方法mTask.executeOnExecutor(mExecutor, (Void[]) null);}}void dispatchOnCancelled(LoadTask task, D data) {onCanceled(data);if (mCancellingTask == task) {rollbackContentChanged();mLastLoadCompleteTime = SystemClock.uptimeMillis();mCancellingTask = null;//觸發Loader的接口方法onLoadCanceled,在LoaderManager中實現deliverCancellation();executePendingTask();}}void dispatchOnLoadComplete(LoadTask task, D data) {if (mTask != task) {if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");dispatchOnCancelled(task, data);} else {if (isAbandoned()) {// This cursor has been abandoned; just cancel the new data.onCanceled(data);} else {commitContentChanged();mLastLoadCompleteTime = SystemClock.uptimeMillis();mTask = null;if (DEBUG) Log.v(TAG, "Delivering result");//觸發Loader的接口方法onLoadComplete,在LoaderManager中實現deliverResult(data);}}}//需要子類實現!!!!!在子線程中執行public abstract D loadInBackground();//LoadTask(AsyncTask的子線程中回調)中調運protected D onLoadInBackground() {return loadInBackground();}//LoadTask(AsyncTask的onCancelLoad中回調)調運public void cancelLoadInBackground() {}public boolean isLoadInBackgroundCanceled() {return mCancellingTask != null;}//鎖標記處理public void waitForLoader() {LoadTask task = mTask;if (task != null) {task.waitForLoader();}}@Overridepublic void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {super.dump(prefix, fd, writer, args);if (mTask != null) {writer.print(prefix); writer.print("mTask="); writer.print(mTask);writer.print(" waiting="); writer.println(mTask.waiting);}if (mCancellingTask != null) {writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);writer.print(" waiting="); writer.println(mCancellingTask.waiting);}if (mUpdateThrottle != 0) {writer.print(prefix); writer.print("mUpdateThrottle=");TimeUtils.formatDuration(mUpdateThrottle, writer);writer.print(" mLastLoadCompleteTime=");TimeUtils.formatDuration(mLastLoadCompleteTime,SystemClock.uptimeMillis(), writer);writer.println();}} }

可以看見上面繼承Loader的AsyncTaskLoader其實質是提供了一個基于AsyncTask工作機制的Loader(子類LoadTask繼承AsyncTask< Void, Void, D >,并且實現了Runable接口,功能十分強大。),但是不可直接用,因為其為abstract抽象類,所以我們需要繼承實現它才可以使用,然而好在系統API已經幫我們提供了他現成的子類CursorLoader,但CursorLoader同時也限制了Loader的泛型數據為Cursor類型。當然了,我們如果想要Loader自己的類型數據那也很簡單—繼承實現AsyncTaskLoader即可,后面會給出例子的。

CursorLoader子類源碼淺析

有了上面繼承自Loader的抽象AsyncTaskLoader,接下來我們就來看看SDK為我們提供的抽象AsyncTaskLoader實現類CursorLoader,我們先來粗略看看該類的方法圖,如下:

//CursorLoader 繼承 AsyncTaskLoader , 數據類型為Cursor的Loader異步加載實現類 public class CursorLoader extends AsyncTaskLoader<Cursor> {//ContentObserver的子類ForceLoadContentObserverfinal ForceLoadContentObserver mObserver;Uri mUri;String[] mProjection;String mSelection;String[] mSelectionArgs;String mSortOrder;Cursor mCursor;CancellationSignal mCancellationSignal;/* Runs on a worker thread *///最核心的實現方法,在這里查詢獲取數據 @Overridepublic Cursor loadInBackground() {synchronized (this) {if (isLoadInBackgroundCanceled()) {throw new OperationCanceledException();}mCancellationSignal = new CancellationSignal();}try {//不過多解釋,耗時的查詢操作Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,mSelectionArgs, mSortOrder, mCancellationSignal);if (cursor != null) {try {// Ensure the cursor window is filled.cursor.getCount();//給Cursor設置觀察者;ContentProvider通知Cursor的觀察者數據發生了改變,Cursor通知CursorLoader的觀察者數據發生了改變,CursorLoader通過ContentProvider重新加載新的數據cursor.registerContentObserver(mObserver);} catch (RuntimeException ex) {cursor.close();throw ex;}}return cursor;} finally {synchronized (this) {mCancellationSignal = null;}}}@Overridepublic void cancelLoadInBackground() {super.cancelLoadInBackground();synchronized (this) {if (mCancellationSignal != null) {mCancellationSignal.cancel();}}}/* Runs on the UI thread */@Overridepublic void deliverResult(Cursor cursor) {if (isReset()) {// An async query came in while the loader is stoppedif (cursor != null) {cursor.close();}return;}Cursor oldCursor = mCursor;mCursor = cursor;if (isStarted()) {super.deliverResult(cursor);}if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {oldCursor.close();}}/*** Creates an empty unspecified CursorLoader. You must follow this with* calls to {@link #setUri(Uri)}, {@link #setSelection(String)}, etc* to specify the query to perform.*/public CursorLoader(Context context) {super(context);mObserver = new ForceLoadContentObserver();}/*** Creates a fully-specified CursorLoader. See* {@link ContentResolver#query(Uri, String[], String, String[], String)* ContentResolver.query()} for documentation on the meaning of the* parameters. These will be passed as-is to that call.*/public CursorLoader(Context context, Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {super(context);//新建一個當前類(Loader)的內部類對象,數據庫變化時調運ForceLoadContentObserver的onChange方法,onChange調運Loader的onContentChanged方法,onContentChanged調運Loader的forceLoad方法mObserver = new ForceLoadContentObserver();mUri = uri;mProjection = projection;mSelection = selection;mSelectionArgs = selectionArgs;mSortOrder = sortOrder;}/*** Starts an asynchronous load of the contacts list data. When the result is ready the callbacks* will be called on the UI thread. If a previous load has been completed and is still valid* the result may be passed to the callbacks immediately.** Must be called from the UI thread*/@Overrideprotected void onStartLoading() {if (mCursor != null) {deliverResult(mCursor);}if (takeContentChanged() || mCursor == null) {forceLoad();}}/*** Must be called from the UI thread*/@Overrideprotected void onStopLoading() {// Attempt to cancel the current load task if possible.cancelLoad();}@Overridepublic void onCanceled(Cursor cursor) {if (cursor != null && !cursor.isClosed()) {cursor.close();}}@Overrideprotected void onReset() {super.onReset();// Ensure the loader is stoppedonStopLoading();if (mCursor != null && !mCursor.isClosed()) {mCursor.close();}mCursor = null;}public Uri getUri() {return mUri;}public void setUri(Uri uri) {mUri = uri;}public String[] getProjection() {return mProjection;}public void setProjection(String[] projection) {mProjection = projection;}public String getSelection() {return mSelection;}public void setSelection(String selection) {mSelection = selection;}public String[] getSelectionArgs() {return mSelectionArgs;}public void setSelectionArgs(String[] selectionArgs) {mSelectionArgs = selectionArgs;}public String getSortOrder() {return mSortOrder;}public void setSortOrder(String sortOrder) {mSortOrder = sortOrder;}@Overridepublic void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {super.dump(prefix, fd, writer, args);writer.print(prefix); writer.print("mUri="); writer.println(mUri);writer.print(prefix); writer.print("mProjection=");writer.println(Arrays.toString(mProjection));writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);writer.print(prefix); writer.print("mSelectionArgs=");writer.println(Arrays.toString(mSelectionArgs));writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged);} }

可以發現,CursorLoader的封裝大大簡化了應用開發者代碼的復雜度;它完全就是一個異步的數據庫查詢瑞士軍刀,沒有啥特別需要分析的地方,所以不再過多說明。

參考資料

官方文檔

使用CursorLoader執行查詢任務

Android應用Loaders全面詳解及源碼淺析

Android之Loader介紹

總結

以上是生活随笔為你收集整理的Android Loader 异步加载详解一:基础概念的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。