Android 3.0引入的異步加載機(jī)制Loader
Loader是谷歌在Android 3.0引入的異步加載機(jī)制,能夠?qū)?shù)據(jù)異步加載并顯示到Activity或Fragment上,使用者不需要對數(shù)據(jù)的生命周期進(jìn)行管理,而是交給Loader機(jī)制來管理。
使用Loader的優(yōu)點(diǎn)
假如我們需要從網(wǎng)絡(luò)上獲取數(shù)據(jù),通常的做法是使用子線程Thread+Handler或者是使用AsyncTask來處理。
Thread+Handler方法實(shí)現(xiàn)起來簡單直觀,不過會麻煩點(diǎn),需要自己實(shí)現(xiàn)Handler子類,創(chuàng)建線程,還要管理Handler的生命周期。
AsyncTask實(shí)現(xiàn)起來會簡單些,無需自己管理線程和Handler。但是要管理AsyncTask的生命周期,要對Activity退出時(shí)的情況進(jìn)行處理。否則可能會出現(xiàn)異?;騼?nèi)存泄露。
使用Loader無需關(guān)心線程和Handler的創(chuàng)建和銷毀,也無需自己管理數(shù)據(jù)整個(gè)的生命周期,Loader機(jī)制會自動(dòng)幫我們處理好。我們唯一要處理的就是數(shù)據(jù)本身。
Loader使用的步驟:
創(chuàng)建FragmentActivity或Fragment 持有LoaderManager的實(shí)例實(shí)現(xiàn)Loader,用來加載數(shù)據(jù)源返回的數(shù)據(jù)實(shí)現(xiàn)LoaderManager.LoaderCallbacks接口實(shí)現(xiàn)數(shù)據(jù)的展示提供數(shù)據(jù)的數(shù)據(jù)源,如ContentProvider,服務(wù)器下發(fā)的數(shù)據(jù)等 幾個(gè)相關(guān)的類 LoaderManager
管理Loader實(shí)例,并使之和FragmentActiivty或Fragment關(guān)聯(lián)上
一個(gè)Activity或Fragment有一個(gè)唯一的LoaderManager實(shí)例
一個(gè)LoaderManager實(shí)例可以管理多個(gè)Loader實(shí)例
可以在FragmentActivity或Fragmeng中使用getSupportLoaderManager()獲取到LoaderManager實(shí)例
可以使用 initLoader() 或 restartLoader() 方法開始進(jìn)行數(shù)據(jù)的加載
//0,為唯一的ID,可以為任意整數(shù),為Loader的唯一標(biāo)識 //null,為Bundle類型,可以向Loader傳遞構(gòu)造參數(shù) //LoaderCallbacks,LoaderManager對Loader各事件的調(diào)用,參考下面講到的 LoaderManager.LoaderCallbacks getSupportLoaderManager().initLoader(0, null, new LoaderCallbacks<D>());
LoaderManager.LoaderCallbacks
LoaderManager對Loader各種情況的回調(diào)接口,包含三個(gè)回調(diào)方法
onCreateLoader(int,Bundle)
在這里需要自己創(chuàng)建Loader對象,int 為Loader的唯一標(biāo)識,Bundle為Loader的構(gòu)造參數(shù),可為空
...
new LoaderManager.LoaderCallbacks<String>() {
@Override
public Loader<String> onCreateLoader(int id, Bundle args) {
return new MyLoader();
}
...
}
onLoadFinished(Loader<D>,D)
當(dāng)LoaderManager加載完數(shù)據(jù)時(shí)回調(diào)此方法,在這里用UI展示數(shù)據(jù)給用戶。D為泛型,根據(jù)實(shí)際情況設(shè)置為所需的數(shù)據(jù)類型。和initLoader()LoaderCallbacks<D>參數(shù)中的的泛型為同一類型
new LoaderManager.LoaderCallbacks<String>() {
...
@Override
public void onLoadFinished(Loader<String> loader, String data) {
show(data);
}
...
}
onLoaderReset(Loader<D>)
當(dāng)之前創(chuàng)建的Loader實(shí)例被重置的時(shí)候會回調(diào)此方法,此時(shí)需要對相關(guān)的數(shù)據(jù)進(jìn)行清除處理
new LoaderManager.LoaderCallbacks<String>() {
...
@Override
public void onLoaderReset(Loader<String> loader) {
show(null);
}
...
}
Loader
從數(shù)據(jù)源獲取數(shù)據(jù),并對數(shù)據(jù)進(jìn)行加載,為抽象類,需要自己實(shí)現(xiàn)子類
或使用官方已經(jīng)實(shí)現(xiàn)的兩個(gè)子類
AsyncTaskLoader(繼承此類的時(shí)候會遇到一個(gè)坑,見下面的分析)
處理異步獲取數(shù)據(jù) CursorLoader
處理ContentProvider返回的數(shù)據(jù) 實(shí)現(xiàn)AsyncTaskLoader遇到的一個(gè)坑
首先自定義一個(gè) MyAsyncTaskLoader,繼承AsyncTaskLoader,會發(fā)現(xiàn)需要實(shí)現(xiàn)參數(shù)為Context的構(gòu)造方法和實(shí)現(xiàn) loadInBackground() 抽象方法
//繼承AsyncTaskLoader類,里面的泛型為返回的數(shù)據(jù)的類型,這里設(shè)為String
public class MyAsyncTaskLoader extends AsyncTaskLoader<String>{
public MyAsyncTaskLoader(Context context) {
super(context);
}
@Override
public String loadInBackground() {
//模擬加載
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//返回獲取到的數(shù)據(jù)
return new String("MyAsyncTaskLoader Test Result");
}
}
創(chuàng)建FragmentActivity
public class BaseActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.base_activity_layout);
// addFragment();
log("onCreate");
loadData();
}
protected void loadData(){
Log.e(getClassName(),"call");
getSupportLoaderManager().initLoader(0, null, this);
}
protected String getClassName(){
return getClass().getSimpleName();
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
Log.e(getClassName(),"onCreateLoader");
return new MyAsyncTaskLoader(BaseActivity.this);
}
@Override
public void onLoadFinished(Loader loader, Object data) {
Log.e(getClassName(),"data:"+data);
}
@Override
public void onLoaderReset(Loader loader) {
}
}
當(dāng)運(yùn)行的時(shí)候發(fā)現(xiàn)日志值打印了onCreate,call,onCreateLoader,而預(yù)期中的 MyAsyncTaskLoader Test Result 并沒有輸出,也就是說 onLoadFinished 并未被回調(diào)。調(diào)試發(fā)現(xiàn) MyAsyncTaskLoader 中的 loadInBackground() 方法也未執(zhí)行。
這個(gè)是怎么回事呢?
那么只好查看源碼了,這里所使用的都是 support-v4 的包。
查看 AsyncTaskLoader 源碼發(fā)現(xiàn) loadInBackground() 方法的確為 abstract 類型,其被調(diào)用的地方是在一個(gè)叫做 LoadTask 的內(nèi)部類中。
//可以把 ModernAsyncTask 看做 AsyncTask
final class LoadTask extends ModernAsyncTask<Void, Void, D> implements Runnable {
....
@Override
protected D doInBackground(Void... params) {
...
D data = AsyncTaskLoader.this.onLoadInBackground();
...
}
.....
}
并且作為AsyncTaskLoader的一個(gè)全局變量。
public abstract class AsyncTaskLoader<D> extends Loader<D> {
....
volatile LoadTask mTask;
....
}
mTask 實(shí)例化和被執(zhí)行的地方在 onForceLoad() 方法里
...
@Override
protected void onForceLoad() {
...
mTask = new LoadTask();
...
executePendingTask();
}
...
void executePendingTask() {
...
if (mUpdateThrottle > 0) {
...
mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
return;
}
}
...
mTask.executeOnExecutor(mExecutor, (Void[]) null);
}
}
mHandler.postAtTime 或者是 mTask.executeOnExecutor 這兩個(gè)地方就是執(zhí)行 TaskLoader 的地方,并會調(diào)用到 doInBackground() 方法。
那么到這里我們可以猜測我們自定義的 MyAsyncLoader 的 loadInBackground() 未被執(zhí)行,那么 onForceLoad() 也應(yīng)該未被執(zhí)行。
沿著這條線索查找看看這個(gè) onForceLoad() 是在哪里被調(diào)用的。發(fā)現(xiàn)其是在AsyncLoader 的父類 Loader 中的 forceLoad() 中被調(diào)用
public class Loader{
...
public void forceLoad() {
onForceLoad();
}
...
}
然后又看到注釋發(fā)現(xiàn),此方法只能在 loader 開始的時(shí)候調(diào)用,還是找不到什么頭緒。

突然想到好像 CursorLoader 沒有這個(gè)問題,那么看看它是不是有調(diào)用 forceLoad(),找了下,發(fā)現(xiàn)還果然有!是在 onStartLoading() 這個(gè)方法里,并且只有這里調(diào)用!
public class CursorLoader extends AsyncTaskLoader<Cursor> {
...
@Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
...
}
那么我模仿下這個(gè)看看是不是真的能行,MyAsyncLoader 的代碼修改如下:
//繼承AsyncTaskLoader類,里面的泛型為返回的數(shù)據(jù)的類型,這里設(shè)為String
public class MyAsyncTaskLoader extends AsyncTaskLoader<String>{
public MyAsyncTaskLoader(Context context) {
super(context);
}
//添加了這段代碼
@Override
protected void onStartLoading() {
forceLoad();
}
@Override
public String loadInBackground() {
//模擬加載
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//返回獲取到的數(shù)據(jù)
return new String("MyAsyncTaskLoader Test Result");
}
}
運(yùn)行后發(fā)現(xiàn)真的能夠輸出了!看來問題是解決了。
最后一行為輸出的結(jié)果
問題是解決了,但是還是有一個(gè)疑問,這個(gè) onStartLoading()是在哪里被調(diào)用的呢?看來還是得看看源碼。
從 getSupportLoaderManager().initLoader(0, null, this) 開始分析,發(fā)現(xiàn)最后是會調(diào)用到 onStartLoading()。
簡記如下,可自己對照著源碼查看:
LoaderManager的實(shí)現(xiàn)類為LoaderManagerImpl init()方法里面創(chuàng)建 LoaderInfo info info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback); 進(jìn)入 createAndInstallLoader 方法 mCallbacks.onLoadFinished(loader, data); 進(jìn)入 onLoadFinished 方法 createLoader(id, args, callback) 進(jìn)入 createLoader 方法 installLoader(info); 進(jìn)入 installLoader 方法 info.start(); 進(jìn)入 start 方法 mLoader.startLoading(); 進(jìn)入 startLoading 方法 onStartLoading();
相關(guān)文章
Android 開發(fā)使用Activity實(shí)現(xiàn)加載等待界面功能示例
這篇文章主要介紹了Android 開發(fā)使用Activity實(shí)現(xiàn)加載等待界面功能,結(jié)合實(shí)例形式詳細(xì)分析了Android基于Activity實(shí)現(xiàn)加載等待界面布局與功能操作技巧,需要的朋友可以參考下2020-05-05
Android之PreferenceActivity應(yīng)用詳解(2)
看到很多書中都沒有對PreferenceActivity做介紹,而我正好又在項(xiàng)目中用到,所以就把自己的使用的在這總結(jié)一下,也方便日后查找2012-11-11
Android開發(fā)實(shí)現(xiàn)跟隨手指的小球效果示例
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)跟隨手指的小球效果,涉及Android圖形繪制、事件響應(yīng)、界面布局等相關(guān)操作技巧,需要的朋友可以參考下2019-04-04
Android顯示系統(tǒng)SurfaceFlinger詳解
本文詳細(xì)講解了Android顯示系統(tǒng)SurfaceFlinger,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12
Android游戲開發(fā)學(xué)習(xí)之引擎用法實(shí)例詳解
這篇文章主要介紹了Android游戲開發(fā)學(xué)習(xí)之引擎用法,較為詳細(xì)的分析了Android游戲開發(fā)中所常用的JBox2D引擎功能及相關(guān)使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
Android實(shí)現(xiàn)多級樹形菜單并支持多選功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)多級樹形菜單并支持多選功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07
Android開發(fā)中DatePicker日期與時(shí)間控件實(shí)例代碼
本文通過實(shí)例代碼給大家介紹了Android開發(fā)中DatePicker日期與時(shí)間控件,代碼簡單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-08-08
詳解如何在Android studio中更新sdk版本和build-tools版本
這篇文章主要介紹了如何在Android studio中更新sdk版本和build-tools版本,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Android本地驗(yàn)證碼的簡易實(shí)現(xiàn)方法(防止暴力登錄)
驗(yàn)證馬真是無處不在啊,主要作用是防止惡意暴力破解登錄,這篇文章主要介紹了Android本地驗(yàn)證碼的簡易實(shí)現(xiàn)方法(防止暴力登錄),需要的朋友可以參考下2017-04-04
Android Studio 3.x版本 的輸入法遇到的坑及解決方案
前些天把AndroidStudio從2.3.3升級到3.0,遇到了不少坑,其中一個(gè)巨坑就是輸入中文不提示的問題,下面給大家分享Android Studio 3.x版本的輸入法遇到的坑及解決方案,一起看看吧2017-11-11

