LoaderManager使用详解(三)---实现Loaders
生活随笔
收集整理的這篇文章主要介紹了
LoaderManager使用详解(三)---实现Loaders
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這篇文字將介紹Loader<D>類,并且介紹自定義Loader的實現。這是本系列的第三篇文章。
一:Loaders之前世界 二:了解LoaderManager 三:實現Loaders 四:實例:AppListLoader
重中之重,如果你還沒有讀過前面兩篇文章,我建議你在深入之前先讀一讀那兩篇文章。先簡短的 總結一下這篇博客覆蓋了什么內容。Loader之前的世界(第一篇)描述了Android3.0之前的數據載入方法和在UI主線程中執行的冗長的查詢操 作。這些UI非友好的API導致了應用響應變差。總總情況就是了解LoaderManager(第二篇)的寫作動機。這篇文章介紹了 LoaderManager類,并且講到了它在異步載入數據中所扮演的角色。LoaderManager在Activity和Fragment的聲明周期 中管理Loaders,并且在配置變化時保持已載入的數據(譯者注:避免了Activity重啟導致數據重載入)。
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的行為:
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的時候有很多需要注意。子類必須實現 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>返回給客戶端:
[java] view plaincopypublic?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將從相應性能、所需代碼量中收益。我希望 通過把它們的細節列舉出來,可以減小它的學習曲線。
一: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>返回給客戶端:
[java] view plaincopy
總結
我希望本文對你有用,并且通過它可以很好的理解Loaders和LoaderManager 如何協同工作來執行異步任務,自動更新查詢結果。記住,Loader是你的朋友。。。如果你使用它們,你的app將從相應性能、所需代碼量中收益。我希望 通過把它們的細節列舉出來,可以減小它的學習曲線。
附注
1. 你不需要擔心為你的Loader注冊監聽器,除非你不準備跟LoaderManager協同使用。LoaderManager擔任的就是 “listener”的角色,并將Loader返回的任何結果傳給LoaderCallbacks#LoadFinished方法。 2. Loader也有可能處于“abandoned”狀態(譯者注:丟棄狀態?)。這個是一個可選的中間狀態,處于停止狀態和重置狀態之間。為了更簡明的理解,再這里不討論丟棄狀態。也就是說,以我的經驗來看,通常并無必要實現onAbandon()方法。轉載于:https://www.cnblogs.com/Free-Thinker/p/4378909.html
總結
以上是生活随笔為你收集整理的LoaderManager使用详解(三)---实现Loaders的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 热烈庆贺清明小长假的到来
- 下一篇: [Android] TableLayou