LoaderManager使用詳解(三)---實現Loaders

來源:互聯網
上載者:User

這篇文字將介紹Loader<D>類,並且介紹自訂Loader的實現。這是本系列的第三篇文章。
一:Loaders之前世界二:瞭解LoaderManager三:實現Loaders四:執行個體:AppListLoader
重中之重,如果你還沒有讀過前面兩篇文章,我建議你在深入之前先讀一讀那兩篇文章。先簡短的總結一下這篇部落格覆蓋了什麼內容。Loader之前的世界(第一篇)描述了Android3.0之前的資料載入方法和在UI主線程中執行的冗長的查詢操作。這些UI非友好的API導致了應用響應變差。總總情況就是瞭解LoaderManager(第二篇)的寫作動機。這篇文章介紹了LoaderManager類,並且講到了它在非同步載入資料中所扮演的角色。LoaderManager在Activity和Fragment的聲明周期中管理Loaders,並且在配置變化時保持已載入的資料(譯者註:避免了Activity重啟導致資料重載入)。
Loader基礎
Loades負責在一個單獨線程中執行查詢,監控資料來源改變,當探測到改變時將查詢到的結果集發送到註冊的監聽器上(通常是LoaderManager)。下面這些特性使Loaders成為AndroidSDK中強大的工具:
1. 它封裝了實際的資料載入。Activity/Fragment不再需要知道如何載入資料。實際上,Activity/Fragment將該任務委託給了Loader,它在後台執行查詢要求並且將結果返回給Activity/Fragment。
2. 用戶端不需要知道查詢如何執行。Activity/Fragment不需要擔心查詢如何在獨立的線程中執行,Loder會自動執行這些查詢操作。這種方式不僅減少了代碼複雜度同事也消除了線程相關bug的潛在可能。
3. 它是為安全的事件驅動方式。Loader檢測底層資料,當檢測到改變時,自動執行新的載入擷取最新資料。這使得使用Loader變得容易,用戶端可以相信Loader將會自己自動更新它的資料。Activity/Fragment所需要做的就是初始化Loader,並且對任何反饋回來的資料進行響應。除此之外,所有其他的事情都由Loader來解決。
Loaders是一個比較進階的話題,可能需要更多時間來使用它。在下一節中,我們會從分析它的四個定義的特性來開始。
Loader由什麼組成?
總共有四個特性最終決定了一個Loader的行為:
1. 執行非同步載入的任務。為了確保在一個獨立線程中執行載入操作,Loader的子類必須繼承AsyncTaskLoader<D>而不是Loader<D>類。AsyncTaskLoader<D>是一個抽象Loader,它提供了一個AsyncTask來做它的執行操作。當定義子類時,通過實現抽象方法loadInBackground方法來實現非同步task。該方法將在一個背景工作執行緒中執行資料載入操作。
2. 在一個註冊監聽器中接收載入完成返回的結果(見附註1)。對於每個Loader來說,LoaderManager註冊一個OnLoadCompleteListener<D>,該對象將通過調用onLoadFinished(Loader<D> loader, D result)方法使Loader將結果傳送給用戶端。Loader通過調用Loader#deliverResult(D result),將結果傳遞給登入的監聽器們。
3. 三種不同狀態(見附註2)。任何Loader將處於三種狀態之中,已啟動、已停止、重啟:a. 處於已啟動狀態的Loader會執行載入操作,並在任何時間將結果傳遞到監聽器中。已啟動的Loader將會監聽資料改變,當檢測到改變時執行新的載入。一旦啟動,Loader將一直處在已啟動狀態,一直到轉換到已停止和重啟。這是唯一一種onLoadFinished永遠不會調用的狀態。
b. 處於已停止狀態的Loader將會繼續監聽資料改變,但是不會將結果返回給用戶端。在已停止狀態,Loader可能被啟動或者重啟。c. 當Loader處於重啟狀態時,將不會執行新的載入操作,也不會發送新的結果集,也不會檢測資料變化。當一個Loader進入重啟狀態,它必須解除對應的資料引用,方便記憶體回收(同樣地,用戶端必須確定,在Loader無效之後,移除了所有該資料的引用)。通常,重啟Loader不會兩次調用;然而,在某些情況下他們可能會啟動,所以如果必要的話,它們必須能夠適時重啟。
4. 有一個觀察者接受資料來源改變的通知。Loader必須實現這些Observer其中之一(比如ContentObserver,BroadcastReceiver等),來檢測底層資料來源的改變。當檢測到資料改變,觀察者必須調用Loader#onContentChanged()。在該方法中執行兩種不同操作:a. 如果Loader已經處於啟動狀態,就會執行一個新的載入操作; b. 設定一個flag標識資料來源有改變,這樣當Loader再次啟動時,就知道應該重新載入資料了。
到目前為止,你應該基本知道了Loader如何工作了。如果沒有的話,我建議你先放一放,稍後再重新讀一遍(讀一下這篇文檔,)。也就是說,讓我們從實際代碼入手,寫寫看。
實現Loader
就如我之前陳述的那樣,在實現自訂Loader的時候有很多需要注意。子類必須實現loadInBackground()方法,必須覆寫onStartLoading(), onStoppLoading(),onReset(),onCanceled()和deliverResult(D results)來實現一個完整功能的Loader。覆寫這些方法非常重要,LoaderManager將會在Activity/Fragment不同聲明周期調用不同的方法。例如,當一個Activity第一次啟動,它將會讓LoaderManager在Activity#onStart()中啟動它所擁有的每個Loaders。如果一個Loader沒有啟動,LoaderManager將會調用startLoading()方法,該方法將Loader進入已啟動狀態並且立即調用Loader的onStartLoading()方法。也就是說,LoaderManager在後台所做的大量工作都是基於Loader正確實現的基礎上,所以不要小看實現這些方法的重要性。
下面的代碼就是Loader典型實現的樣板。SampleLoader查詢結果為一個包含SampleItem對象的列表,並且將查詢結果清單List<SampleItem>返回給用戶端:

public class SampleLoader extends AsyncTaskLoader<List<SampleItem>> {  // We hold a reference to the Loader’s data here.  private List<SampleItem> mData;  public SampleLoader(Context ctx) {    // Loaders may be used across multiple Activitys (assuming they aren't    // bound to the LoaderManager), so NEVER hold a reference to the context    // directly. Doing so will cause you to leak an entire Activity's context.    // The superclass constructor will store a reference to the Application    // Context instead, and can be retrieved with a call to getContext().    super(ctx);  }  /****************************************************/  /** (1) A task that performs the asynchronous load **/  /****************************************************/  @Override  public List<SampleItem> loadInBackground() {    // This method is called on a background thread and should generate a    // new set of data to be delivered back to the client.    List<SampleItem> data = new ArrayList<SampleItem>();    // TODO: Perform the query here and add the results to 'data'.    return data;  }  /********************************************************/  /** (2) Deliver the results to the registered listener **/  /********************************************************/  @Override  public void deliverResult(List<SampleItem> data) {    if (isReset()) {      // The Loader has been reset; ignore the result and invalidate the data.      releaseResources(data);      return;    }    // Hold a reference to the old data so it doesn't get garbage collected.    // We must protect it until the new data has been delivered.    List<SampleItem> oldData = mData;    mData = data;    if (isStarted()) {      // If the Loader is in a started state, deliver the results to the      // client. The superclass method does this for us.      super.deliverResult(data);    }    // Invalidate the old data as we don't need it any more.    if (oldData != null && oldData != data) {      releaseResources(oldData);    }  }  /*********************************************************/  /** (3) Implement the Loader’s state-dependent behavior **/  /*********************************************************/  @Override  protected void onStartLoading() {    if (mData != null) {      // Deliver any previously loaded data immediately.      deliverResult(mData);    }    // Begin monitoring the underlying data source.    if (mObserver == null) {      mObserver = new SampleObserver();      // TODO: register the observer    }    if (takeContentChanged() || mData == null) {      // When the observer detects a change, it should call onContentChanged()      // on the Loader, which will cause the next call to takeContentChanged()      // to return true. If this is ever the case (or if the current data is      // null), we force a new load.      forceLoad();    }  }  @Override  protected void onStopLoading() {    // The Loader is in a stopped state, so we should attempt to cancel the     // current load (if there is one).    cancelLoad();    // Note that we leave the observer as is. Loaders in a stopped state    // should still monitor the data source for changes so that the Loader    // will know to force a new load if it is ever started again.  }  @Override  protected void onReset() {    // Ensure the loader has been stopped.    onStopLoading();    // At this point we can release the resources associated with 'mData'.    if (mData != null) {      releaseResources(mData);      mData = null;    }    // The Loader is being reset, so we should stop monitoring for changes.    if (mObserver != null) {      // TODO: unregister the observer      mObserver = null;    }  }  @Override  public void onCanceled(List<SampleItem> data) {    // Attempt to cancel the current asynchronous load.    super.onCanceled(data);    // The load has been canceled, so we should release the resources    // associated with 'data'.    releaseResources(data);  }  private void releaseResources(List<SampleItem> data) {    // For a simple List, there is nothing to do. For something like a Cursor, we     // would close it in this method. All resources associated with the Loader    // should be released here.  }  /*********************************************************************/  /** (4) Observer which receives notifications when the data changes **/  /*********************************************************************/   // NOTE: Implementing an observer is outside the scope of this post (this example  // uses a made-up "SampleObserver" to illustrate when/where the observer should   // be initialized).     // The observer could be anything so long as it is able to detect content changes  // and report them to the loader with a call to onContentChanged(). For example,  // if you were writing a Loader which loads a list of all installed applications  // on the device, the observer could be a BroadcastReceiver that listens for the  // ACTION_PACKAGE_ADDED intent, and calls onContentChanged() on the particular   // Loader whenever the receiver detects that a new application has been installed.  // Please don’t hesitate to leave a comment if you still find this confusing! :)  private SampleObserver mObserver;}

總結
我希望本文對你有用,並且通過它可以很好的理解Loaders和LoaderManager如何協同工作來執行非同步任務,自動更新查詢結果。記住,Loader是你的朋友。。。如果你使用它們,你的app將從相應效能、所需代碼量中收益。我希望通過把它們的細節列舉出來,可以減小它的學習曲線。
附註1. 你不需要擔心為你的Loader註冊監聽器,除非你不準備跟LoaderManager協同使用。LoaderManager擔任的就是“listener”的角色,並將Loader返回的任何結果傳給LoaderCallbacks#LoadFinished方法。2. Loader也有可能處於“abandoned”狀態(譯者註:丟棄狀態?)。這個是一個可選的中間狀態,處於停止狀態和重設狀態之間。為了更簡明的理解,再這裡不討論丟棄狀態。也就是說,以我的經驗來看,通常並無必要實現onAbandon()方法。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.