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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android Loader机制全面详解及源码浅析

發(fā)布時(shí)間:2025/4/16 Android 66 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android Loader机制全面详解及源码浅析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原文出處:csdn@工匠若水,http://blog.csdn.net/yanbober/article/details/48861457

一、概述

在Android中任何耗時(shí)的操作都不能放在UI主線程中,所以耗時(shí)的操作都需要使用異步實(shí)現(xiàn)。同樣的,在ContentProvider中也可能存在耗時(shí)操作,這時(shí)也該使用異步操作,而3.0之后最推薦的異步操作就是Loader。它可以方便我們?cè)贏ctivity和Fragment中異步加載數(shù)據(jù),而不是用線程或AsyncTask,他的優(yōu)點(diǎn)如下:

  • 提供異步加載數(shù)據(jù)機(jī)制;
  • 對(duì)數(shù)據(jù)源變化進(jìn)行監(jiān)聽,實(shí)時(shí)更新數(shù)據(jù);
  • 在Activity配置發(fā)生變化(如橫豎屏切換)時(shí)不用重復(fù)加載數(shù)據(jù);
  • 適用于任何Activity和Fragment;

注意:由于在我們現(xiàn)在的多個(gè)項(xiàng)目中都大量的使用了Loader來處理數(shù)據(jù)加載(而且由于粗心跳過幾個(gè)坑,譬如Loader ID重復(fù)導(dǎo)致數(shù)據(jù)邏輯異常、多線程中restartLoader導(dǎo)致Loader拋出異常(最后保證都在UI線程中執(zhí)行即可)等),所以接下來我們進(jìn)行下使用及源碼淺析。

PPPS:前方高能,文章巨長(zhǎng),請(qǐng)做好心理準(zhǔn)備(您可以選擇通過左上角目錄點(diǎn)擊索引到感興趣的章節(jié)直接查看,或者,或者,或者直接高能往下看)。

二、基礎(chǔ)使用實(shí)例

該基礎(chǔ)實(shí)例講解完全來自于官方文檔,詳細(xì)可以點(diǎn)擊我查看英文原文。

既然接下來準(zhǔn)備要說說他的使用強(qiáng)大之處了,那不妨我們先來一張圖直觀的感性認(rèn)識(shí)下不用Loader(左)與用Loader(右)對(duì)我們開發(fā)者及代碼復(fù)雜度和框架的影響吧,如下:

Loader API概述說明

如下是我們開發(fā)中常用的一些Loader相關(guān)接口:

Class/InterfaceDescription
LoaderManager一個(gè)與Activity、Fragment關(guān)聯(lián)的抽象類,用于管理一個(gè)或多個(gè)Loader實(shí)例。每個(gè)Activity或Fragment只能有一個(gè)LoaderManager,而一個(gè)LoaderManager可以有多個(gè)Loader。
LoaderManager.LoaderCallbacks用于和LoaderManager交互的回調(diào)接口。譬如,可以使用onCreateLoader()創(chuàng)建一個(gè)新的Loader。
AsyncTaskLoader抽象的Loader,提供一個(gè)AsyncTask繼承實(shí)現(xiàn)。
CursorLoaderAsyncTaskLoader的子類,用于向ContentResover請(qǐng)求返回一個(gè)Cursor。該類以標(biāo)準(zhǔn)游標(biāo)查詢實(shí)現(xiàn)了Loader協(xié)議,使用后臺(tái)線程進(jìn)行查詢,使用這個(gè)Loader是從ContentProvider加載異步數(shù)據(jù)最好的方式。

在應(yīng)用中使用Loader

在我們開發(fā)的一個(gè)App里,使用Loader時(shí)常規(guī)的步驟包含如下一些操作需求:

  • 一個(gè)Activity或Fragment;
  • 一個(gè)LoaderManager實(shí)例;
  • 一個(gè)CursorLoader,從ContentProvider加載數(shù)據(jù);
  • 一個(gè)LoaderManager.LoaderCallbacks實(shí)現(xiàn),創(chuàng)建新Loader及管理已存在Loader;
  • 一個(gè)組織Loader數(shù)據(jù)的Adapter,如SimpleCursorAdapter;

下面我們看下具體流程。

啟動(dòng)一個(gè)Loader(initLoader)

一個(gè)Activity或Fragment中LoaderManager管理一個(gè)或多個(gè)Loader實(shí)例,每個(gè)Activity或Fragment只有一個(gè)LoaderManager,我們可以在Activity的onCreate()或Fragment的onActivityCreated()里初始化一個(gè)Loader。例如:

// Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this);

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

  • 第一個(gè)參數(shù)代表當(dāng)前Loader的ID;
  • 第二個(gè)參數(shù)代表提供給Loader構(gòu)造函數(shù)的參數(shù),可選;
  • 第三個(gè)參數(shù)代表LoaderManager.LoaderCallbacks的回調(diào)實(shí)現(xiàn);

上面initLoader()方法的調(diào)用確保了一個(gè)Loader被初始化和激活的狀態(tài),該方法的調(diào)運(yùn)有如下兩種結(jié)果:

  • 如果代表該Loader的ID已經(jīng)存在,則后面創(chuàng)建的Loader將直接復(fù)用已經(jīng)存在的;
  • 如果代表該Loader的ID不存在,initLoader()會(huì)觸發(fā)LoaderManager.LoaderCallbacks回調(diào)的onCreateLoader()方法創(chuàng)建一個(gè)Loader;

可以看見通過initLoader()方法可以將LoaderManager.LoaderCallbacks實(shí)例與Loader進(jìn)行關(guān)聯(lián),且當(dāng)Loader的狀態(tài)變化時(shí)就被回調(diào)。所以說,如果調(diào)用者正處于其開始狀態(tài)并且被請(qǐng)求的Loader已經(jīng)存在,且已產(chǎn)生了數(shù)據(jù),那么系統(tǒng)會(huì)立即調(diào)用onLoadFinished()(在initLoader()調(diào)用期間),所以你必須考慮到這種情況的發(fā)生。

當(dāng)然了,intiLoader()會(huì)返回一個(gè)創(chuàng)建的Loader,但是你不用獲取它的引用,因?yàn)長(zhǎng)oadeManager會(huì)自動(dòng)管理該Loader的生命周期,你只用在它回調(diào)提供的生命周期方法中做自己數(shù)據(jù)邏輯的處理即可。

重啟一個(gè)Loader(restartLoader)

通過上面initLoader()方法介紹我們可以知道initLoader調(diào)運(yùn)后要么得到一個(gè)ID已存在的Loader,要么創(chuàng)建一個(gè)新的Loader;但是有時(shí)我們想丟棄舊數(shù)據(jù)然后重新開始創(chuàng)建一個(gè)新Loader,這可怎么辦呢?別擔(dān)心,要丟棄舊數(shù)據(jù)調(diào)用restartLoader()即可。例如,SearchView.OnQueryTextListener的實(shí)現(xiàn)重啟了Loader,當(dāng)用戶查詢發(fā)生變化時(shí)Loader需要重啟,如下:

public boolean onQueryTextChanged(String newText) {// Called when the action bar search text has changed. Update// the search filter, and restart the loader to do a new query// with this filter.mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;getLoaderManager().restartLoader(0, null, this);return true; }

上面方法的參數(shù)啥的和再上面的init方法類似,就不再羅嗦了。

使用LoaderManager Callbacks

LoaderManager.LoaderCallbacks是LoaderManager的回調(diào)交互接口。LoaderManager.LoaderCallbacks包含如下三個(gè)方法:

  • onCreateLoader()
    實(shí)例化并返回一個(gè)新創(chuàng)建給定ID的Loader對(duì)象;
  • onLoadFinished()
    當(dāng)創(chuàng)建好的Loader完成了數(shù)據(jù)的load之后回調(diào)此方法;
  • onLoaderReset()
    當(dāng)創(chuàng)建好的Loader被reset時(shí)調(diào)用此方法,這樣保證它的數(shù)據(jù)無效;

onCreateLoader說明

當(dāng)你嘗試使用一個(gè)Loader(譬如通過initLoader()方法),它會(huì)檢查給定Loader的ID是否存在,如果不存在就觸發(fā)LoaderManager.LoaderCallbacks里的onCreateLoader()方法創(chuàng)建一個(gè)新Loader。創(chuàng)建新Loader實(shí)例典型的做法就是通過CursorLoader類創(chuàng)建,不過你也可以自定義一個(gè)繼承自Loader的子類來實(shí)現(xiàn)自己的Loader。

下面的例子中我們通過onCreateLoader()回調(diào)創(chuàng)建一個(gè)CursorLoader實(shí)例,使用CursorLoader的構(gòu)造方法創(chuàng)建實(shí)例時(shí)需要一些參數(shù)去查詢一個(gè)ContentProvider。具體參數(shù)如下:

參數(shù)說明
uri準(zhǔn)備獲取內(nèi)容的URI
projection要返回的列key list,null表示返回所有列,但是返回所有列很多時(shí)候會(huì)降低性能
selection要返回的行過濾,也就是SQL中的WHERE語句,null代表返回uri指定的所有行
selectionArgs用來替換上面selection中包含的"?"
sortOrder結(jié)果的行排序,也就是SQL中的ORDER BY,傳遞null則無序

.

// If non-null, this is the current filter the user has provided. String mCurFilter; ... public Loader<Cursor> onCreateLoader(int id, Bundle args) {// This is called when a new Loader needs to be created. This// sample only has one Loader, so we don't care about the ID.// First, pick the base URI to use depending on whether we are// currently filtering.Uri baseUri;if (mCurFilter != null) {baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,Uri.encode(mCurFilter));} else {baseUri = Contacts.CONTENT_URI;}// Now create and return a CursorLoader that will take care of// creating a Cursor for the data being displayed.String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("+ Contacts.DISPLAY_NAME + " != '' ))";return new CursorLoader(getActivity(), baseUri,CONTACTS_SUMMARY_PROJECTION, select, null,Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); }

onLoadFinished說明

當(dāng)創(chuàng)建好的Loader完成數(shù)據(jù)加載時(shí)回調(diào)此方法,我們要確保該方法在Loader釋放現(xiàn)有維持的數(shù)據(jù)之前被調(diào)用。在這里我們應(yīng)該移除所有對(duì)舊數(shù)據(jù)的使用(因?yàn)榕f數(shù)據(jù)不久就會(huì)被釋放),但是不用釋放舊數(shù)據(jù),因?yàn)長(zhǎng)oader會(huì)幫我們完成舊數(shù)據(jù)的釋放。

Loader一旦知道App不再使用舊數(shù)據(jù)就會(huì)釋放掉。例如,如果數(shù)據(jù)來自CursorLoader里的一個(gè)Cursor,我們不應(yīng)該自己在代碼中調(diào)用close()方法;如果一個(gè)Cursor正在被放置到一個(gè)CursorAdapter時(shí)我們應(yīng)當(dāng)使用swapCursor()進(jìn)行新數(shù)據(jù)交換,這樣正在被放置的舊的Cursor就不會(huì)被關(guān)掉,也就不會(huì)導(dǎo)致Adapter的加載異常。

// This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; ...public void onLoadFinished(Loader<Cursor> loader, Cursor data) {// Swap the new cursor in. (The framework will take care of closing the// old cursor once we return.)mAdapter.swapCursor(data); }

onLoaderReset說明

當(dāng)實(shí)例化好的Loader被重啟時(shí)該方法被回調(diào),這里會(huì)讓Loader的數(shù)據(jù)置于無效狀態(tài)。這個(gè)回調(diào)方法其實(shí)就是為了告訴我們啥時(shí)候數(shù)據(jù)要被釋放掉,所以我們應(yīng)該在這個(gè)時(shí)候移除對(duì)它的引用。如下移除實(shí)例:

// This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; ...public void onLoaderReset(Loader<Cursor> loader) {// This is called when the last Cursor provided to onLoadFinished()// above is about to be closed. We need to make sure we are no// longer using it.mAdapter.swapCursor(null); }

Loader使用實(shí)例實(shí)戰(zhàn)

下面這個(gè)實(shí)例是一個(gè)Fragment,模擬的是用ListView顯示通訊錄的實(shí)時(shí)匹配查詢結(jié)果,使用CursorLoader管理通訊錄Provider查詢。如下源碼,比較簡(jiǎn)單,注釋也很豐富了,所以不過多解釋:

public static class CursorLoaderListFragment extends ListFragmentimplements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {// This is the Adapter being used to display the list's data.SimpleCursorAdapter mAdapter;// If non-null, this is the current filter the user has provided.String mCurFilter;@Override public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);// Give some text to display if there is no data. In a real// application this would come from a resource.setEmptyText("No phone numbers");// We have a menu item to show in action bar.setHasOptionsMenu(true);// Create an empty adapter we will use to display the loaded data.mAdapter = new SimpleCursorAdapter(getActivity(),android.R.layout.simple_list_item_2, null,new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },new int[] { android.R.id.text1, android.R.id.text2 }, 0);setListAdapter(mAdapter);// Prepare the loader. Either re-connect with an existing one,// or start a new one.getLoaderManager().initLoader(0, null, this);}@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {// Place an action bar item for searching.MenuItem item = menu.add("Search");item.setIcon(android.R.drawable.ic_menu_search);item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);SearchView sv = new SearchView(getActivity());sv.setOnQueryTextListener(this);item.setActionView(sv);}public boolean onQueryTextChange(String newText) {// Called when the action bar search text has changed. Update// the search filter, and restart the loader to do a new query// with this filter.mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;getLoaderManager().restartLoader(0, null, this);return true;}@Override public boolean onQueryTextSubmit(String query) {// Don't care about this.return true;}@Override public void onListItemClick(ListView l, View v, int position, long id) {// Insert desired behavior here.Log.i("FragmentComplexList", "Item clicked: " + id);}// These are the Contacts rows that we will retrieve.static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {Contacts._ID,Contacts.DISPLAY_NAME,Contacts.CONTACT_STATUS,Contacts.CONTACT_PRESENCE,Contacts.PHOTO_ID,Contacts.LOOKUP_KEY,};public Loader<Cursor> onCreateLoader(int id, Bundle args) {// This is called when a new Loader needs to be created. This// sample only has one Loader, so we don't care about the ID.// First, pick the base URI to use depending on whether we are// currently filtering.Uri baseUri;if (mCurFilter != null) {baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,Uri.encode(mCurFilter));} else {baseUri = Contacts.CONTENT_URI;}// Now create and return a CursorLoader that will take care of// creating a Cursor for the data being displayed.String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("+ Contacts.DISPLAY_NAME + " != '' ))";return new CursorLoader(getActivity(), baseUri,CONTACTS_SUMMARY_PROJECTION, select, null,Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");}public void onLoadFinished(Loader<Cursor> loader, Cursor data) {// Swap the new cursor in. (The framework will take care of closing the// old cursor once we return.)mAdapter.swapCursor(data);}public void onLoaderReset(Loader<Cursor> loader) {// This is called when the last Cursor provided to onLoadFinished()// above is about to be closed. We need to make sure we are no// longer using it.mAdapter.swapCursor(null);} }

到此整個(gè)Loader基礎(chǔ)使用就介紹完了,關(guān)于Loader的高級(jí)功能,譬如自定義Loader等內(nèi)容這里先不貼代碼說明,因?yàn)樵谶@里一下子說完都會(huì)覺得蒙圈,而且接受難度也比較大,所以我們?cè)谏厦孢@些基礎(chǔ)鋪墊之后乘熱先來源碼淺析,有了源碼淺析把持住全局結(jié)構(gòu)后再去用Loader的高級(jí)用法就會(huì)覺得得心應(yīng)手許多。

三、源碼淺析

和上面的基本使用介紹一樣,關(guān)于Loader的源碼淺析過程會(huì)涉及到Activity、Fragment、LoaderManager、Loader、AsyncLoader、CursorLoader等類。所以我們分析的過程還是和以前一樣,依據(jù)使用順序進(jìn)行分析。

我們?cè)诜治鲋跋葋砜匆粋€(gè)Loader框架概要圖,如下:

通過上面圖和前面的基礎(chǔ)實(shí)例你會(huì)發(fā)現(xiàn)Loader的框架和各個(gè)類的職責(zé)都很明確。Activity和Fragment管理LoaderManager,LoaderManager管理Loader,Loader得到數(shù)據(jù)后觸發(fā)在LoaderManager中實(shí)現(xiàn)的Loader的callback接口,LoaderManager在接收到Loader的callback回傳調(diào)運(yùn)時(shí)觸發(fā)我們Activity或Fragment中實(shí)現(xiàn)的LoaderManager回調(diào)callback接口,就這樣就實(shí)現(xiàn)了Loader的所有功能,而我們平時(shí)寫代碼一般只用關(guān)心LoaderManager的callback實(shí)現(xiàn)即可;對(duì)于自定義Loader可能還需要關(guān)心AsyncTaskLoader子類的實(shí)現(xiàn)。

Activity及Fragment中LoadManager的管理淺析

首先我們都知道,在使用Loader的第一步就是在Activity或者Fragment中獲取LoaderManager實(shí)例,所以我們先來看下Activity和Fragment是如何管理這些LoaderManager的。

先來看看Fragment中的LoaderManager,如下:

final class FragmentState implements Parcelable {......LoaderManagerImpl mLoaderManager;boolean mLoadersStarted;boolean mCheckedForLoaderManager;......//fragment中獲取LoaderManager辦法public LoaderManager getLoaderManager() {//可以看見,一個(gè)Fragment只有一個(gè)LoaderManagerif (mLoaderManager != null) {return mLoaderManager;}if (mActivity == null) {throw new IllegalStateException("Fragment " + this + " not attached to Activity");}mCheckedForLoaderManager = true;//從Activity中獲取LoaderManager,傳入的mWho為當(dāng)前Fragment的識(shí)別key,然后create傳入true表示創(chuàng)建!!!!!!mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, true);return mLoaderManager;}public void onStart() {mCalled = true;if (!mLoadersStarted) {mLoadersStarted = true;if (!mCheckedForLoaderManager) {mCheckedForLoaderManager = true;//如果還沒調(diào)運(yùn)過getLoaderManager,那就嘗試獲取LoaderManager,傳入的create為false!!!!!mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);}//生命周期依附上LoaderManagerif (mLoaderManager != null) {mLoaderManager.doStart();}}}public void onDestroy() {mCalled = true;if (!mCheckedForLoaderManager) {mCheckedForLoaderManager = true;//如果還沒調(diào)運(yùn)過getLoaderManager,那就嘗試獲取LoaderManager,傳入的create為false!!!!!mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);}//生命周期依附上LoaderManagerif (mLoaderManager != null) {mLoaderManager.doDestroy();}}void performStart() {......mCalled = false;onStart();......//生命周期依附上LoaderManagerif (mLoaderManager != null) {mLoaderManager.doReportStart();}}void performStop() {......mCalled = false;onStop();......if (mLoadersStarted) {mLoadersStarted = false;if (!mCheckedForLoaderManager) {mCheckedForLoaderManager = true;//如果還沒調(diào)運(yùn)過getLoaderManager,那就嘗試獲取LoaderManager,傳入的create為false!!!!!mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);}if (mLoaderManager != null) {//生命周期依附上LoaderManagerif (mActivity == null || !mActivity.mChangingConfigurations) {mLoaderManager.doStop();} else {mLoaderManager.doRetain();}}}}void performDestroyView() {......mCalled = false;onDestroyView();......//生命周期依附上LoaderManagerif (mLoaderManager != null) {mLoaderManager.doReportNextStart();}} }

從上面可以看出,Fragment在其生命周期內(nèi)會(huì)控制LoaderManager(LoaderManager其實(shí)控制了Loader)的doStart、doDestroy等方法,也就是說我們?cè)贔ragment中只管通過getLoaderManager方法來獲取LoaderManager實(shí)例,然后使用就行,別的Fragment都會(huì)幫我們處理OK的。

接下來看看Activity中的LoaderManager,如下:

public class Activity extends ContextThemeWrapperimplements ... {//mAllLoaderManagers保存了Activity與Fragment的所有LoaderManagerArrayMap<String, LoaderManagerImpl> mAllLoaderManagers;LoaderManagerImpl mLoaderManager;......//Activity中獲取LoaderManager實(shí)例的方法public LoaderManager getLoaderManager() {//可以看見,一個(gè)Activity只有一個(gè)LoaderManagerif (mLoaderManager != null) {return mLoaderManager;}mCheckedForLoaderManager = true;//咦?這不就是上面Fragment的getLoaderManager中調(diào)運(yùn)的那個(gè)activity中的getLoaderManager嗎,只是和這里的參數(shù)不一樣而已mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);return mLoaderManager;}//Activity與Fragment獲取LoaderManager實(shí)例的真正方法!!LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {//可見一個(gè)Activity維護(hù)一個(gè)mAllLoaderManagers的MAPif (mAllLoaderManagers == null) {mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>();}//嘗試從緩存mAllLoaderManagers的MAP中獲取已經(jīng)實(shí)例化的LoaderManager實(shí)例LoaderManagerImpl lm = mAllLoaderManagers.get(who);if (lm == null) {if (create) {//如果沒有找到并且需要實(shí)例化create(切記這個(gè)create參數(shù)是很重要的),就調(diào)運(yùn)LoaderManagerImpl構(gòu)造方法實(shí)例化一個(gè)LoaderManager對(duì)象,然后存入緩存mAllLoaderManagers的MAP中lm = new LoaderManagerImpl(who, this, started);mAllLoaderManagers.put(who, lm);}} else {lm.updateActivity(this);}return lm;}void invalidateFragment(String who) {if (mAllLoaderManagers != null) {LoaderManagerImpl lm = mAllLoaderManagers.get(who);if (lm != null && !lm.mRetaining) {//生命周期依附上LoaderManagerlm.doDestroy();mAllLoaderManagers.remove(who);}}}final void performStop() {if (mLoadersStarted) {mLoadersStarted = false;//生命周期依附上LoaderManagerif (mLoaderManager != null) {//mChangingConfigurations表示如果當(dāng)前發(fā)生了配置變化則為true,否則為false!!!!!!!重點(diǎn),Loader特性之一if (!mChangingConfigurations) {//當(dāng)前Activity的stop不是由配置變化引起則直接調(diào)用LoaderManager的doStop()方法!!!!!!mLoaderManager.doStop();} else {//當(dāng)前Activity配置變化,所以需要保存當(dāng)前的loaderManager,在Activity恢復(fù)時(shí)恢復(fù)這個(gè)LoaderManager!!!!!!mLoaderManager.doRetain();}}}......}final void performDestroy() {......onDestroy();//生命周期依附上LoaderManagerif (mLoaderManager != null) {mLoaderManager.doDestroy();}......}protected void onCreate(@Nullable Bundle savedInstanceState) {if (mLastNonConfigurationInstances != null) {//從mLastNonConfigurationInstances中恢復(fù)mAllLoaderManagers(mLastNonConfigurationInstances是從onAttach中恢復(fù)的),Activity配置變化時(shí)會(huì)走這里!!!!mAllLoaderManagers = mLastNonConfigurationInstances.loaders;}......mCalled = true;}final void performStart() {......if (mAllLoaderManagers != null) {final int N = mAllLoaderManagers.size();LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];for (int i=N-1; i>=0; i--) {loaders[i] = mAllLoaderManagers.valueAt(i);}//生命周期依附上LoaderManagerfor (int i=0; i<N; i++) {LoaderManagerImpl lm = loaders[i];//調(diào)用LoaderManager.finishRetain()以及doReportStart()方法來恢復(fù)LoaderManager的狀態(tài)!!!!!lm.finishRetain();lm.doReportStart();}}mActivityTransitionState.enterReady(this);}//該方法會(huì)被ActivityThread類調(diào)用,且調(diào)運(yùn)時(shí)機(jī)早于performDestroy()方法!!!!!!NonConfigurationInstances retainNonConfigurationInstances() {......NonConfigurationInstances nci = new NonConfigurationInstances();......//配置變化時(shí)保存mAllLoaderManagers!!!!!!nci.loaders = mAllLoaderManagers;return nci;} }

通過上面的分析可以發(fā)現(xiàn),Activity其實(shí)真正的管理了Activity及Fragment的LoaderManager(Fragment也會(huì)管理一部分自己LoaderManager的周期),而LoaderManager又管理了Loader,可以發(fā)現(xiàn)他們各自的管理范圍都是十分的清晰明了的。

LoadManager及其實(shí)現(xiàn)類LoadManagerImpl的淺析

上面分析Activity及Fragment中獲取LoaderManager實(shí)例時(shí)已經(jīng)知道,我們獲取的LoaderManager實(shí)例其實(shí)就是LoaderManagerImpl對(duì)象,而LoaderManagerImpl又是LoaderManager類的子類,所以接下來我們來分析這兩個(gè)父子類。

先看下抽象父類LoaderManager,如下

public abstract class LoaderManager {//LoaderManager的回調(diào)接口定義public interface LoaderCallbacks<D> {public Loader<D> onCreateLoader(int id, Bundle args);public void onLoadFinished(Loader<D> loader, D data);public void onLoaderReset(Loader<D> loader);}//下面這些方法沒必要再細(xì)說了,上面介紹過的public abstract <D> Loader<D> initLoader(int id, Bundle args,LoaderManager.LoaderCallbacks<D> callback);public abstract <D> Loader<D> restartLoader(int id, Bundle args,LoaderManager.LoaderCallbacks<D> callback);//會(huì)觸發(fā)回調(diào)的onLoaderReset方法public abstract void destroyLoader(int id);public abstract <D> Loader<D> getLoader(int id);public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);public static void enableDebugLogging(boolean enabled) {LoaderManagerImpl.DEBUG = enabled;} }

可以看見LoaderManager抽象類只是定義了一些規(guī)范接口而已,那么接著我們看下抽象類LoaderManager的實(shí)現(xiàn)類LoaderManagerImpl,如下:

class LoaderManagerImpl extends LoaderManager {static final String TAG = "LoaderManager";static boolean DEBUG = false;//保存當(dāng)前存活的Loaderfinal SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(0);//保存已經(jīng)運(yùn)行完的Loaderfinal SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(0);final String mWho;Activity mActivity;boolean mStarted;boolean mRetaining;boolean mRetainingStarted;//是否正在創(chuàng)建Loader,多線程中同時(shí)調(diào)運(yùn)創(chuàng)建會(huì)導(dǎo)致異常boolean mCreatingLoader;//Loader的封裝類final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,Loader.OnLoadCanceledListener<Object> {final int mId;final Bundle mArgs;LoaderManager.LoaderCallbacks<Object> mCallbacks;Loader<Object> mLoader;boolean mHaveData;boolean mDeliveredData;Object mData;boolean mStarted;//mRetaining標(biāo)記Activity配置變化時(shí)保持當(dāng)前Loader,不用銷毀;和上面分析Activity的LoaderManager的retainNonConfigurationInstances方法關(guān)聯(lián)!!!!!!boolean mRetaining;boolean mRetainingStarted;boolean mReportNextStart;boolean mDestroyed;boolean mListenerRegistered;LoaderInfo mPendingLoader;//LoaderInfo構(gòu)造方法public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {mId = id;mArgs = args;mCallbacks = callbacks;}//啟動(dòng)一個(gè)Loadervoid start() {//配置改變恢復(fù)則不用啟動(dòng),用原來的if (mRetaining && mRetainingStarted) {mStarted = true;return;}//如果已經(jīng)啟動(dòng),則不用再restart了if (mStarted) {return;}mStarted = true;//如果當(dāng)前封裝中mLoader為空并且通過構(gòu)造方法的mCallbacks不為空則回調(diào)onCreateLoader方法創(chuàng)建Loaderif (mLoader == null && mCallbacks != null) {mLoader = mCallbacks.onCreateLoader(mId, mArgs);}if (mLoader != null) {if (mLoader.getClass().isMemberClass()&& !Modifier.isStatic(mLoader.getClass().getModifiers())) {//如果當(dāng)前創(chuàng)建的Loader對(duì)象是一個(gè)非靜態(tài)內(nèi)部類則拋異常!!!!!!throw new IllegalArgumentException("Object returned from onCreateLoader must not be a non-static inner member class: "+ mLoader);}if (!mListenerRegistered) {//注冊(cè)Loader的監(jiān)聽方法mLoader.registerListener(mId, this);mLoader.registerOnLoadCanceledListener(this);mListenerRegistered = true;}//調(diào)運(yùn)Loader的startLoading方法mLoader.startLoading();}}//Activity的配置改變時(shí)進(jìn)行標(biāo)志位的設(shè)置,以便可以保存,配合上面Activity的分析!!!!!!void retain() {mRetaining = true;......}//Activity配置變化后重啟后如果有數(shù)據(jù)則通知回調(diào)方法,配合上面Activity的分析!!!!!!void finishRetain() {......if (mStarted && mHaveData && !mReportNextStart) {callOnLoadFinished(mLoader, mData);}}//配合上面Activity的分析!!!!!!void reportStart() {......}//停止Loadervoid stop() {mStarted = false;if (!mRetaining) {//如果不是Activity配置變化,即不用保存則注銷掉這些回調(diào)if (mLoader != null && mListenerRegistered) {......}}}//取消掉Loadervoid cancel() {......}//銷毀掉Loadervoid destroy() {......if (mCallbacks != null && mLoader != null && mHaveData && needReset) {......try {//在destroy時(shí)如果有數(shù)據(jù)存在則調(diào)用callback的onLoaderReset方法!!!!!!mCallbacks.onLoaderReset(mLoader);} finally {......}}......if (mLoader != null) {//注銷監(jiān)聽方法if (mListenerRegistered) {......}//close Cursor等重置操作mLoader.reset();}if (mPendingLoader != null) {mPendingLoader.destroy();}}//Loader被取消時(shí)回調(diào)該方法@Overridepublic void onLoadCanceled(Loader<Object> loader) {......LoaderInfo pending = mPendingLoader;//執(zhí)行最新的Loaderif (pending != null) {mPendingLoader = null;mLoaders.put(mId, null);destroy();installLoader(pending);}}//加載完成時(shí)回調(diào)@Overridepublic void onLoadComplete(Loader<Object> loader, Object data) {......//執(zhí)行最新的Loaderif (pending != null) {mPendingLoader = null;mLoaders.put(mId, null);destroy();installLoader(pending);return;}if (mData != data || !mHaveData) {mData = data;mHaveData = true;if (mStarted) {callOnLoadFinished(loader, data);}}......}//調(diào)用onLoadFinishedvoid callOnLoadFinished(Loader<Object> loader, Object data) {if (mCallbacks != null) {......try {//回調(diào)onLoadFinished方法mCallbacks.onLoadFinished(loader, data);} finally {......}mDeliveredData = true;}}}//!!!!!!真正LoaderManagerImpl的構(gòu)造方法LoaderManagerImpl(String who, Activity activity, boolean started) {mWho = who;mActivity = activity;mStarted = started;}//更新當(dāng)前Activity引用void updateActivity(Activity activity) {mActivity = activity;}//私有的創(chuàng)建Loader方法private LoaderInfo createLoader(int id, Bundle args,LoaderManager.LoaderCallbacks<Object> callback) {LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);//回調(diào)callback的onCreateLoader方法得到Loader對(duì)象Loader<Object> loader = callback.onCreateLoader(id, args);//把得到的Loader對(duì)象包裝成LoaderInfo對(duì)象info.mLoader = (Loader<Object>)loader;return info;}//包裝了創(chuàng)建Loader與install方法,并將mCreatingLoader標(biāo)記置位private LoaderInfo createAndInstallLoader(int id, Bundle args,LoaderManager.LoaderCallbacks<Object> callback) {try {mCreatingLoader = true;//調(diào)運(yùn)上面的私有創(chuàng)建方法創(chuàng)建LoaderInfo對(duì)象LoaderInfo info = createLoader(id, args, callback);//把創(chuàng)建的LoaderInfo對(duì)象傳入installLoader方法installLoader(info);return info;} finally {mCreatingLoader = false;}}void installLoader(LoaderInfo info) {//將創(chuàng)建的LoaderInfo對(duì)象存入mLoaders的Map中mLoaders.put(info.mId, info);if (mStarted) {//如果Activity已經(jīng)started,則啟動(dòng)LoaderInfo的start方法info.start();}}//public的方法,創(chuàng)建一個(gè)Loader,前面介紹過的@SuppressWarnings("unchecked")public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {//如果多線程中正在有創(chuàng)建的則拋出異常(寫代碼要注意這種情況,尤其是跑Monkey容易拋出,解決辦法就是保證都在統(tǒng)一線程中執(zhí)行!!!!!!)if (mCreatingLoader) {throw new IllegalStateException("Called while creating a loader");}//從現(xiàn)有的Map中嘗試獲取指定ID的LoaderInfo對(duì)象LoaderInfo info = mLoaders.get(id);if (info == null) {//發(fā)現(xiàn)不存在就調(diào)運(yùn)上面的createAndInstallLoader創(chuàng)建一個(gè)info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);} else {//否則還用當(dāng)前的Loader,只是重新賦值了callBack而已info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;}if (info.mHaveData && mStarted) {//已經(jīng)有數(shù)據(jù),直接調(diào)運(yùn)LoaderInfo的callOnLoadFinishedinfo.callOnLoadFinished(info.mLoader, info.mData);}//返回Loader對(duì)象return (Loader<D>)info.mLoader;}//重新創(chuàng)造Loader,前面介紹過的@SuppressWarnings("unchecked")public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {if (mCreatingLoader) {//如果多線程中正在有創(chuàng)建的則拋出異常(寫代碼要注意這種情況,尤其是跑Monkey容易拋出,解決辦法就是保證都在統(tǒng)一線程中執(zhí)行!!!!!!)throw new IllegalStateException("Called while creating a loader");}LoaderInfo info = mLoaders.get(id);if (info != null) {LoaderInfo inactive = mInactiveLoaders.get(id);if (inactive != null) {if (info.mHaveData) {//發(fā)現(xiàn)是已經(jīng)運(yùn)行完的Loader且已經(jīng)存在的Loader有數(shù)據(jù)則destroy掉運(yùn)行完的Loaderinactive.mDeliveredData = false;inactive.destroy();info.mLoader.abandon();mInactiveLoaders.put(id, info);} else {if (!info.mStarted) {//有相同id的Loader還沒start則destory掉mLoaders.put(id, null);info.destroy();} else {//有一個(gè)相同id的Loader正在加載數(shù)據(jù),但是還沒加載完,調(diào)用它的cancel()方法通知取消加載info.cancel();if (info.mPendingLoader != null) {info.mPendingLoader.destroy();info.mPendingLoader = null;}//創(chuàng)建一個(gè)指定id的Loader同時(shí)賦給mPendingLoader,因?yàn)檫@個(gè)時(shí)候已經(jīng)有一個(gè)Loader正在加載數(shù)據(jù),而且我們已經(jīng)調(diào)用了其cancel()方法來通知取消加載info.mPendingLoader = createLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);//返回創(chuàng)建的Loaderreturn (Loader<D>)info.mPendingLoader.mLoader;}}} else {//終止已存在的Loaderinfo.mLoader.abandon();mInactiveLoaders.put(id, info);}}//重新創(chuàng)建Loader返回info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);return (Loader<D>)info.mLoader;}//銷毀指定id的Loaderpublic void destroyLoader(int id) {if (mCreatingLoader) {throw new IllegalStateException("Called while creating a loader");}//不解釋,單純的destoryint idx = mLoaders.indexOfKey(id);if (idx >= 0) {LoaderInfo info = mLoaders.valueAt(idx);mLoaders.removeAt(idx);info.destroy();}idx = mInactiveLoaders.indexOfKey(id);if (idx >= 0) {LoaderInfo info = mInactiveLoaders.valueAt(idx);mInactiveLoaders.removeAt(idx);info.destroy();}......}//獲取指定id的Loader對(duì)象@SuppressWarnings("unchecked")public <D> Loader<D> getLoader(int id) {if (mCreatingLoader) {throw new IllegalStateException("Called while creating a loader");}//優(yōu)先獲取LoaderInfo中的mPendingLoaderLoaderInfo loaderInfo = mLoaders.get(id);if (loaderInfo != null) {if (loaderInfo.mPendingLoader != null) {return (Loader<D>)loaderInfo.mPendingLoader.mLoader;}return (Loader<D>)loaderInfo.mLoader;}return null;}...... }

我勒個(gè)去!好長(zhǎng),好累!通過上面粗略的分析你會(huì)發(fā)現(xiàn)和我們上面基礎(chǔ)實(shí)例介紹LoaderManager的方法時(shí)描述的一樣,每個(gè)方法都有自己的特點(diǎn),發(fā)揮著各自的作用,LoaderManager的實(shí)質(zhì)是將Loader對(duì)象轉(zhuǎn)換為L(zhǎng)oaderInfo來進(jìn)行管理,也就是管理了所有的Loader對(duì)象。

Loader及其實(shí)現(xiàn)類的淺析

上面分析了Activity及Fragment管理了LoaderManager的相關(guān)方法,LoaderManager管理了Loader的相關(guān)方法,那么接下來我們就來看看這個(gè)被管理的終極目標(biāo)Loader是咋回事,還有他的子類咋回事。

先來看看我畫的一張關(guān)系圖,如下:

我去,這圖現(xiàn)在看可能有些嚇人,我們還是先來慢慢分析一下再說吧。

Loader基類源碼淺析

我們先來看看這個(gè)Loader基類吧,該類核心方法及內(nèi)部類結(jié)構(gòu)圖如下:

代碼分析如下:

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;//數(shù)據(jù)源變化監(jiān)聽器(觀察者模式),實(shí)現(xiàn)了ContentObserver類public final class ForceLoadContentObserver extends ContentObserver {public ForceLoadContentObserver() {super(new Handler());}@Overridepublic boolean deliverSelfNotifications() {return true;}@Overridepublic void onChange(boolean selfChange) {//實(shí)質(zhì)是調(diào)運(yùn)Loader的forceLoad方法onContentChanged();}}//Loader加載完成接口,當(dāng)加載完成時(shí)Loader通知loaderManager,loaderManager再回調(diào)我們initLoader方法的callbackpublic interface OnLoadCompleteListener<D> {public void onLoadComplete(Loader<D> loader, D data);}//LoaderManager中監(jiān)聽cancel,同上類似public interface OnLoadCanceledListener<D> {public void onLoadCanceled(Loader<D> loader);}//構(gòu)造方法public Loader(Context context) {//mContext持有Application的Context,防止泄露內(nèi)存等mContext = context.getApplicationContext();}//加載完成時(shí)回調(diào)傳遞加載數(shù)據(jù)結(jié)果,實(shí)質(zhì)是對(duì)OnLoadCompleteListener接口方法的封裝public void deliverResult(D data) {if (mListener != null) {mListener.onLoadComplete(this, data);}}//類似同上,對(duì)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) {mListener = listener;mId = id;}public void unregisterListener(OnLoadCompleteListener<D> listener) {mListener = null;}public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {mOnLoadCanceledListener = listener;}public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {mOnLoadCanceledListener = null;}public boolean isStarted() {return mStarted;}public boolean isAbandoned() {return mAbandoned;}public boolean isReset() {return mReset;}//開始加載數(shù)據(jù)時(shí)LoaderManager會(huì)調(diào)用該方法public final void startLoading() {//設(shè)置標(biāo)記mStarted = true;mReset = false;mAbandoned = false;onStartLoading();}//真正開始加載數(shù)據(jù)的地方******空方法,子類實(shí)現(xiàn)!!!!!!protected void onStartLoading() {}//取消Loader的方法public boolean cancelLoad() {return onCancelLoad();}//真正取消的地方******,子類實(shí)現(xiàn)!!!!!!return false表示取消失敗(因?yàn)橐淹瓿苫蛭撮_始)protected boolean onCancelLoad() {return false;}//強(qiáng)制重新Loader,放棄舊數(shù)據(jù)public void forceLoad() {onForceLoad();}//真正重新Loader的地方******空方法,子類實(shí)現(xiàn)!!!!!!protected void onForceLoad() {}//同上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數(shù)據(jù)變化的一些標(biāo)記處理public boolean takeContentChanged() {boolean res = mContentChanged;mContentChanged = false;mProcessingChange |= res;return res;}public void commitContentChanged() {mProcessingChange = false;}public void rollbackContentChanged() {if (mProcessingChange) {mContentChanged = true;}}//上面ForceLoadContentObserver內(nèi)部類的onChange方法調(diào)運(yùn)public void onContentChanged() {if (mStarted) {forceLoad();} else {mContentChanged = true;}}//一些方便調(diào)試的方法public String dataToString(D data)public String toString()public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) }

通過上面粗略的分析可以發(fā)現(xiàn),Loader基類無非也就是一個(gè)方法接口的定義類,組織預(yù)留了一些方法供LoaderManager去調(diào)運(yùn)處理,同時(shí)需要子類實(shí)現(xiàn)其提供的一些onXXX方法,以便LoaderManager調(diào)運(yùn)Loader的方法時(shí)可以觸發(fā)Loader子類的實(shí)現(xiàn)邏輯。

AsyncTaskLoader抽象子類源碼淺析

上面既然說了Loader類的作用主要是規(guī)定接口,同時(shí)供LoaderManager管理,那LoaderManager管理的Loader自然需要做一些事情,也就是說我們需要繼承Loader實(shí)現(xiàn)一些邏輯操作。然而好在系統(tǒng)API已經(jīng)幫我們實(shí)現(xiàn)了一些簡(jiǎn)單的封裝實(shí)現(xiàn),我們這里就先來看下Loader的直接子類AsyncTaskLoader吧,先來看下該抽象子類的方法及內(nèi)部類粗略圖,如下:

代碼分析如下:

public abstract class AsyncTaskLoader<D> extends Loader<D> {static final String TAG = "AsyncTaskLoader";static final boolean DEBUG = false;//LoadTask內(nèi)部類是對(duì)AsyncTask的封裝,實(shí)現(xiàn)了Runnable接口final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {......@Overrideprotected D doInBackground(Void... params) {try {//AsyncTask的子線程中執(zhí)行AsyncTaskLoader的onLoadInBackground方法!!!!重點(diǎn)D data = AsyncTaskLoader.this.onLoadInBackground();//把執(zhí)行結(jié)果數(shù)據(jù)D返回到UI線程return data;} catch (OperationCanceledException ex) {if (!isCancelled()) {throw ex;}return null;}}/* Runs on the UI thread */@Overrideprotected void onPostExecute(D data) {//AsyncTask子線程執(zhí)行完畢后回調(diào)AsyncTaskLoader的dispatchOnLoadComplete方法AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);}/* Runs on the UI thread */@Overrideprotected void onCancelled(D data) {//取消AsyncTask時(shí)調(diào)運(yùn)AsyncTaskLoader.this.dispatchOnCancelled(this, data);}//Runnable的實(shí)現(xiàn)方法@Overridepublic void run() {waiting = false;AsyncTaskLoader.this.executePendingTask();}......}private final Executor mExecutor;volatile LoadTask mTask;volatile LoadTask mCancellingTask;long mUpdateThrottle;long mLastLoadCompleteTime = -10000;Handler mHandler;//public構(gòu)造方法public AsyncTaskLoader(Context context) {this(context, AsyncTask.THREAD_POOL_EXECUTOR);}/** {@hide} 無法被外部調(diào)運(yùn)的構(gòu)造方法 */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();//取消當(dāng)前的LoadercancelLoad();//新建task并執(zhí)行mTask = new LoadTask();executePendingTask();}@Overrideprotected boolean onCancelLoad() {......}public void onCanceled(D data) {}//LoadTask的Runnable方法run中執(zhí)行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;}}//真正的觸發(fā)執(zhí)行AsyncTask方法mTask.executeOnExecutor(mExecutor, (Void[]) null);}}void dispatchOnCancelled(LoadTask task, D data) {onCanceled(data);if (mCancellingTask == task) {rollbackContentChanged();mLastLoadCompleteTime = SystemClock.uptimeMillis();mCancellingTask = null;//觸發(fā)Loader的接口方法onLoadCanceled,在LoaderManager中實(shí)現(xiàn)deliverCancellation();executePendingTask();}}void dispatchOnLoadComplete(LoadTask task, D data) {if (mTask != task) {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;//觸發(fā)Loader的接口方法onLoadComplete,在LoaderManager中實(shí)現(xiàn)deliverResult(data);}}}//需要子類實(shí)現(xiàn)!!!!!在子線程中執(zhí)行public abstract D loadInBackground();//LoadTask(AsyncTask的子線程中回調(diào))中調(diào)運(yùn)protected D onLoadInBackground() {return loadInBackground();}//LoadTask(AsyncTask的onCancelLoad中回調(diào))調(diào)運(yùn)public void cancelLoadInBackground() {}public boolean isLoadInBackgroundCanceled() {return mCancellingTask != null;}//鎖標(biāo)記處理public void waitForLoader() {LoadTask task = mTask;if (task != null) {task.waitForLoader();}} }

可以看見上面繼承Loader的AsyncTaskLoader其實(shí)質(zhì)是提供了一個(gè)基于AsyncTask工作機(jī)制的Loader(子類LoadTask繼承AsyncTask< Void, Void, D >,并且實(shí)現(xiàn)了Runable接口,功能十分強(qiáng)大。),但是不可直接用,因?yàn)槠錇閍bstract抽象類,所以我們需要繼承實(shí)現(xiàn)它才可以使用,然而好在系統(tǒng)API已經(jīng)幫我們提供了他現(xiàn)成的子類CursorLoader,但CursorLoader同時(shí)也限制了Loader的泛型數(shù)據(jù)為Cursor類型。當(dāng)然了,我們?nèi)绻胍狶oader自己的類型數(shù)據(jù)那也很簡(jiǎn)單—繼承實(shí)現(xiàn)AsyncTaskLoader即可,后面會(huì)給出例子的。

CursorLoader子類源碼淺析

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

具體代碼分析如下:

//繼承自AsyncTaskLoader,數(shù)據(jù)類型為Cursor的Loader異步加載實(shí)現(xiàn)類 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 最核心的實(shí)現(xiàn)方法,在這里查詢獲取數(shù)據(jù) */@Overridepublic Cursor loadInBackground() {synchronized (this) {if (isLoadInBackgroundCanceled()) {throw new OperationCanceledException();}mCancellationSignal = new CancellationSignal();}try {//不過多解釋,耗時(shí)的查詢操作Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,mSelectionArgs, mSortOrder, mCancellationSignal);if (cursor != null) {try {// Ensure the cursor window is filled.cursor.getCount();//給Cursor設(shè)置觀察者;ContentProvider通知Cursor的觀察者數(shù)據(jù)發(fā)生了改變,Cursor通知CursorLoader的觀察者數(shù)據(jù)發(fā)生了改變,CursorLoader通過ContentProvider重新加載新的數(shù)據(jù)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();}}public CursorLoader(Context context) {super(context);mObserver = new ForceLoadContentObserver();}public CursorLoader(Context context, Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {super(context);//新建一個(gè)當(dāng)前類(Loader)的內(nèi)部類對(duì)象,數(shù)據(jù)庫變化時(shí)調(diào)運(yùn)ForceLoadContentObserver的onChange方法,onChange調(diào)運(yùn)Loader的onContentChanged方法,onContentChanged調(diào)運(yùn)Loader的forceLoad方法mObserver = new ForceLoadContentObserver();mUri = uri;mProjection = projection;mSelection = selection;mSelectionArgs = selectionArgs;mSortOrder = sortOrder;}@Overrideprotected void onStartLoading() {if (mCursor != null) {deliverResult(mCursor);}if (takeContentChanged() || mCursor == null) {forceLoad();}}@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;} }

可以發(fā)現(xiàn),CursorLoader的封裝大大簡(jiǎn)化了應(yīng)用開發(fā)者代碼的復(fù)雜度;它完全就是一個(gè)異步的數(shù)據(jù)庫查詢?nèi)鹗寇姷?#xff0c;沒有啥特別需要分析的地方,所以不再過多說明。

Loaders相關(guān)源碼淺析總結(jié)

通過上面我們的源碼分析和分析前那副圖可以總結(jié)如下結(jié)論:

一次完整的數(shù)據(jù)加載流程為Activity調(diào)用LoaderManager的doStart()方法,然后LoaderManager調(diào)用Loader的startLoading()方法,然后Loader調(diào)運(yùn)AsyncTaskLoader的doingBackground()方法進(jìn)行耗時(shí)數(shù)據(jù)加載,然后AsyncTaskLoader回調(diào)LoaderManager的complete數(shù)據(jù)加載完成方法,接著LoaderManager回調(diào)我們?cè)贏ctivity中實(shí)現(xiàn)的callback中的onLoadFinish()方法。

Acivity和Fragment的生命周期主動(dòng)管理了LoaderManager,每個(gè)Activity用一個(gè)ArrayMap的mAllLoaderManager來保存當(dāng)前Activity及其附屬Frament的唯一LoaderManager;在Activity配置發(fā)生變化時(shí),Activity在destory前會(huì)保存mAllLoaderManager,當(dāng)Activity再重新創(chuàng)建時(shí),會(huì)在Activity的onAttcach()、onCreate()、performStart()方法中恢復(fù)mAllLoaderManager。

LoaderManager給Activity提供了管理自己的一些方法;同時(shí)主動(dòng)管理了對(duì)應(yīng)的Loader,它把每一個(gè)Loader封裝為L(zhǎng)oadInfo對(duì)象,同時(shí)它負(fù)責(zé)主動(dòng)調(diào)運(yùn)管理Loader的startLoading()、stopLoading()、,forceLoad()等方法。

由于整個(gè)Activity和Fragment主動(dòng)管理了Loader,所以關(guān)于Loader的釋放(譬如CursorLoader的Cursor關(guān)閉等)不需要我們?nèi)藶樘幚?#xff0c;Loader框架會(huì)幫我們很好的處理的;同時(shí)特別注意,對(duì)于CursorLoader,當(dāng)我們數(shù)據(jù)源發(fā)生變化時(shí)Loader框架會(huì)通過ContentObserver調(diào)用onContentChanged的forceLoad方法重新請(qǐng)求數(shù)據(jù)進(jìn)行回調(diào)刷新。

好了,至此你會(huì)發(fā)現(xiàn)Loader真的很牛叉,No!應(yīng)該是Google的工程師真的很牛叉,架構(gòu)真的很贊,值得推薦。

四、應(yīng)用層開發(fā)之Loader進(jìn)階實(shí)戰(zhàn)

上面對(duì)于Loader的基礎(chǔ)使用及源碼框架都進(jìn)行了簡(jiǎn)單分析,有了上面的鋪墊我們?cè)倩剡^頭來看看我們開發(fā)中的一些高級(jí)技巧,通過這些高級(jí)技巧不僅是對(duì)前面源碼分析的實(shí)例驗(yàn)證,也是對(duì)自己知識(shí)的積累。

ContentPorvider情況下的CurSorLoader自動(dòng)刷新

在我們使用CurSorLoader時(shí)大家都會(huì)考慮一種情況的處理—–當(dāng)數(shù)據(jù)庫發(fā)生變化時(shí)如何自動(dòng)刷新當(dāng)前UI。呵呵,我們先來說說這個(gè)原理,數(shù)據(jù)庫在數(shù)據(jù)改變時(shí)通過ContentPorvider和ContentResolver發(fā)出通知,接著ContentProvider通知Cursor的觀察者數(shù)據(jù)發(fā)生了變化,然后Cursor通知CursorLoader的觀察者數(shù)據(jù)發(fā)生了變化,接著CursorLoader通過ContentProvider加載新數(shù)據(jù),完事調(diào)用CursorAdapter的changeCursor()用新數(shù)據(jù)替換舊數(shù)據(jù)顯示。

這個(gè)過程具體的實(shí)現(xiàn)步驟如下:

  • 對(duì)獲取的Cursor數(shù)據(jù)設(shè)置需要監(jiān)聽的URI(即,在ContentProvider的query()方法或者Loader的loadingBackground()方法中調(diào)用Cursor的setNotificationUri()方法);

  • 在ContentProvider的insert()、update()、delete()等方法中調(diào)用ContentResolver的notifyChange()方法;

通過上面兩步我們就能享受CurSorLoader的自動(dòng)數(shù)據(jù)刷新功能了;可以發(fā)現(xiàn),所謂的CurSorLoader自動(dòng)刷新無非就是觀察者模式的框架而已,所以不再過多說明。

特別注意:

有些人覺得為了方便可能會(huì)將上面第一步對(duì)于Cursor設(shè)置監(jiān)聽直接寫在了ContentProvider的query()方法中,如下:

@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {SQLiteDatabase database = sqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(EmailContent.CONTACT_TABLE, projection,selection,selectionArgs, null, null, sortOrder);//設(shè)置NotificationUri監(jiān)聽cursor.setNotificationUri(contentResolver, EmailContent.MESSAGE);return cursor; }

這里要提醒的是,這種寫法在某些場(chǎng)合下是不值得推薦的(譬如大規(guī)模上千次并發(fā)平凡的調(diào)運(yùn)query操作場(chǎng)合),因?yàn)樾蕵O低,他會(huì)頻繁的通過Binder進(jìn)行通信,導(dǎo)致system_server不停的調(diào)運(yùn)GC操作,以至于會(huì)使系統(tǒng)卡頓。

PS:因?yàn)槲乙郧疤^一次這個(gè)坑,平時(shí)使用應(yīng)用沒啥問題,但是當(dāng)進(jìn)行壓力測(cè)試時(shí)卻發(fā)現(xiàn)LogCat一直在不停的打印GC,同時(shí)導(dǎo)致當(dāng)前系統(tǒng)卡頓,殺掉應(yīng)用后系統(tǒng)就不卡了,所以基本懷疑問題就出在了應(yīng)用中,于是通過很多辦法去查找(譬如dempsys content去查看個(gè)數(shù)),最終發(fā)現(xiàn)罪魁禍?zhǔn)资沁@個(gè)監(jiān)聽頻繁調(diào)運(yùn)導(dǎo)致的,隨將其挪到loadingBackground中不再卡頓。

不使用ContentPorvider且自定義Loader的情況下自動(dòng)刷新

我們目前的項(xiàng)目其實(shí)都使用了ContentPorvider實(shí)現(xiàn),所以就是上面講的那些情況。但是你一定會(huì)問,如果我們應(yīng)用的數(shù)據(jù)不用于應(yīng)用間共享,使用ContentProvider那得多麻煩啊?我先告訴你,是很麻煩,但是Android提供的CursorLoader的API必須使用ContentProvider才能實(shí)現(xiàn)數(shù)據(jù)加載和自動(dòng)刷新。

這時(shí)候你指定會(huì)說,那還說個(gè)屁!哎,別急,你看看下面這段代碼是否會(huì)有所感觸呢,如下:

public NoProviderLoader extends AsyncTaskLoader {......ForceLoadContentObserver mObserver = new ForceLoadContentObserver();......@Overridepublic Cursor loadInBackground() {SQLiteDatabase database = sqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(table, columns, selection, selectionArgs, groupBy, having, orderBy);if (cursor != null) {//最重要的兩行代碼!!!!!!cursor.registerContentObserver(mObserver);//給Cursor設(shè)置觀察者cursor.setNotificationUri(getContext().getContentResolver(), otificationUri);//給Cursor設(shè)置要觀察的URI}return cursor;}...... }

咦?是不是上面代碼很奇怪,異步操作的方法中沒有使用ContentProvider,而是直接讀取了數(shù)據(jù)庫。握草!這不就是我們剛剛想要的需求么,它沒有使用ContentProvider提供Cursor數(shù)據(jù),同時(shí)實(shí)現(xiàn)了數(shù)據(jù)變化自動(dòng)更新功能。

簡(jiǎn)單解釋下上面代碼的原理吧,我們自定義的NoProviderLoader中定義的ForceLoadContentObserver是Loader的一個(gè)內(nèi)部類,上面源碼分析已經(jīng)解釋過了,當(dāng)數(shù)據(jù)變化時(shí)會(huì)調(diào)運(yùn)該類的onChange()方法,實(shí)質(zhì)是調(diào)運(yùn)了Loader的forceLoad()方法,所以能夠自動(dòng)刷新,不多解釋了。

Loader自定義之AsyncTaskLoader衍生

可能看到這里你更加會(huì)舉一反三的反駁一句了,上面搞了半天都是和數(shù)據(jù)庫Cursor相關(guān)的東東,難道Loader就不能異步處理別的數(shù)據(jù)結(jié)構(gòu)么?答案是能,因?yàn)槟憧赡芤呀?jīng)注意到了Loader和AsyncTaskLoader都是泛型類;既然這樣,那我們找貓畫虎一把唄,仿照CursorLoader自定義一個(gè)自己的異步加載試試,具體實(shí)現(xiàn)如下(哈哈,想了又想,這里還是直接給出官方的自定義AsyncTaskLoader好點(diǎn),畢竟權(quán)威些,詳細(xì)點(diǎn)我查看官方自定義實(shí)現(xiàn)Demo):

官方對(duì)于查詢已安裝App列表的Loader實(shí)現(xiàn),支持新App安裝后自動(dòng)刷新的功能,實(shí)現(xiàn)如下:

/*** This class holds the per-item data in our Loader.*/ public static class AppEntry {public AppEntry(AppListLoader loader, ApplicationInfo info) {mLoader = loader;mInfo = info;mApkFile = new File(info.sourceDir);}public ApplicationInfo getApplicationInfo() {return mInfo;}public String getLabel() {return mLabel;}public Drawable getIcon() {if (mIcon == null) {if (mApkFile.exists()) {mIcon = mInfo.loadIcon(mLoader.mPm);return mIcon;} else {mMounted = false;}} else if (!mMounted) {// If the app wasn't mounted but is now mounted, reload// its icon.if (mApkFile.exists()) {mMounted = true;mIcon = mInfo.loadIcon(mLoader.mPm);return mIcon;}} else {return mIcon;}return mLoader.getContext().getResources().getDrawable(android.R.drawable.sym_def_app_icon);}@Override public String toString() {return mLabel;}void loadLabel(Context context) {if (mLabel == null || !mMounted) {if (!mApkFile.exists()) {mMounted = false;mLabel = mInfo.packageName;} else {mMounted = true;CharSequence label = mInfo.loadLabel(context.getPackageManager());mLabel = label != null ? label.toString() : mInfo.packageName;}}}private final AppListLoader mLoader;private final ApplicationInfo mInfo;private final File mApkFile;private String mLabel;private Drawable mIcon;private boolean mMounted; }/*** Perform alphabetical comparison of application entry objects.*/ public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {private final Collator sCollator = Collator.getInstance();@Overridepublic int compare(AppEntry object1, AppEntry object2) {return sCollator.compare(object1.getLabel(), object2.getLabel());} };/*** Helper for determining if the configuration has changed in an interesting* way so we need to rebuild the app list.*/ public static class InterestingConfigChanges {final Configuration mLastConfiguration = new Configuration();int mLastDensity;boolean applyNewConfig(Resources res) {int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE|ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {mLastDensity = res.getDisplayMetrics().densityDpi;return true;}return false;} }/*** Helper class to look for interesting changes to the installed apps* so that the loader can be updated.*/ public static class PackageIntentReceiver extends BroadcastReceiver {final AppListLoader mLoader;public PackageIntentReceiver(AppListLoader loader) {mLoader = loader;IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);filter.addAction(Intent.ACTION_PACKAGE_REMOVED);filter.addAction(Intent.ACTION_PACKAGE_CHANGED);filter.addDataScheme("package");mLoader.getContext().registerReceiver(this, filter);// Register for events related to sdcard installation.IntentFilter sdFilter = new IntentFilter();sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);mLoader.getContext().registerReceiver(this, sdFilter);}@Override public void onReceive(Context context, Intent intent) {// Tell the loader about the change.mLoader.onContentChanged();} }/*** A custom Loader that loads all of the installed applications.*/ public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();final PackageManager mPm;List<AppEntry> mApps;PackageIntentReceiver mPackageObserver;public AppListLoader(Context context) {super(context);// Retrieve the package manager for later use; note we don't// use 'context' directly but instead the save global application// context returned by getContext().mPm = getContext().getPackageManager();}/*** This is where the bulk of our work is done. This function is* called in a background thread and should generate a new set of* data to be published by the loader.*/@Override public List<AppEntry> loadInBackground() {// Retrieve all known applications.List<ApplicationInfo> apps = mPm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES |PackageManager.GET_DISABLED_COMPONENTS);if (apps == null) {apps = new ArrayList<ApplicationInfo>();}final Context context = getContext();// Create corresponding array of entries and load their labels.List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());for (int i=0; i<apps.size(); i++) {AppEntry entry = new AppEntry(this, apps.get(i));entry.loadLabel(context);entries.add(entry);}// Sort the list.Collections.sort(entries, ALPHA_COMPARATOR);// Done!return entries;}/*** Called when there is new data to deliver to the client. The* super class will take care of delivering it; the implementation* here just adds a little more logic.*/@Override public void deliverResult(List<AppEntry> apps) {if (isReset()) {// An async query came in while the loader is stopped. We// don't need the result.if (apps != null) {onReleaseResources(apps);}}List<AppEntry> oldApps = mApps;mApps = apps;if (isStarted()) {// If the Loader is currently started, we can immediately// deliver its results.super.deliverResult(apps);}// At this point we can release the resources associated with// 'oldApps' if needed; now that the new result is delivered we// know that it is no longer in use.if (oldApps != null) {onReleaseResources(oldApps);}}/*** Handles a request to start the Loader.*/@Override protected void onStartLoading() {if (mApps != null) {// If we currently have a result available, deliver it// immediately.deliverResult(mApps);}// Start watching for changes in the app data.if (mPackageObserver == null) {mPackageObserver = new PackageIntentReceiver(this);}// Has something interesting in the configuration changed since we// last built the app list?boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());if (takeContentChanged() || mApps == null || configChange) {// If the data has changed since the last time it was loaded// or is not currently available, start a load.forceLoad();}}/*** Handles a request to stop the Loader.*/@Override protected void onStopLoading() {// Attempt to cancel the current load task if possible.cancelLoad();}/*** Handles a request to cancel a load.*/@Override public void onCanceled(List<AppEntry> apps) {super.onCanceled(apps);// At this point we can release the resources associated with 'apps'// if needed.onReleaseResources(apps);}/*** Handles a request to completely reset the Loader.*/@Override protected void onReset() {super.onReset();// Ensure the loader is stoppedonStopLoading();// At this point we can release the resources associated with 'apps'// if needed.if (mApps != null) {onReleaseResources(mApps);mApps = null;}// Stop monitoring for changes.if (mPackageObserver != null) {getContext().unregisterReceiver(mPackageObserver);mPackageObserver = null;}}/*** Helper function to take care of releasing resources associated* with an actively loaded data set.*/protected void onReleaseResources(List<AppEntry> apps) {// For a simple List<> there is nothing to do. For something// like a Cursor, we would close it here.} }

不用多說,上面Loader為Google出品,強(qiáng)大的不得了,我們完全可以仿寫這個(gè)例子實(shí)現(xiàn)自己的請(qǐng)求。

如下為官方對(duì)該自定義Loader調(diào)運(yùn)的Demo代碼:

public static class AppListAdapter extends ArrayAdapter<AppEntry> {private final LayoutInflater mInflater;public AppListAdapter(Context context) {super(context, android.R.layout.simple_list_item_2);mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);}public void setData(List<AppEntry> data) {clear();if (data != null) {addAll(data);}}/*** Populate new items in the list.*/@Override public View getView(int position, View convertView, ViewGroup parent) {View view;if (convertView == null) {view = mInflater.inflate(R.layout.list_item_icon_text, parent, false);} else {view = convertView;}AppEntry item = getItem(position);((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());((TextView)view.findViewById(R.id.text)).setText(item.getLabel());return view;} }public static class AppListFragment extends ListFragmentimplements OnQueryTextListener, OnCloseListener,LoaderManager.LoaderCallbacks<List<AppEntry>> {// This is the Adapter being used to display the list's data.AppListAdapter mAdapter;// The SearchView for doing filtering.SearchView mSearchView;// If non-null, this is the current filter the user has provided.String mCurFilter;@Override public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);// Give some text to display if there is no data. In a real// application this would come from a resource.setEmptyText("No applications");// We have a menu item to show in action bar.setHasOptionsMenu(true);// Create an empty adapter we will use to display the loaded data.mAdapter = new AppListAdapter(getActivity());setListAdapter(mAdapter);// Start out with a progress indicator.setListShown(false);// Prepare the loader. Either re-connect with an existing one,// or start a new one.getLoaderManager().initLoader(0, null, this);}public static class MySearchView extends SearchView {public MySearchView(Context context) {super(context);}// The normal SearchView doesn't clear its search text when// collapsed, so we will do this for it.@Overridepublic void onActionViewCollapsed() {setQuery("", false);super.onActionViewCollapsed();}}@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {// Place an action bar item for searching.MenuItem item = menu.add("Search");item.setIcon(android.R.drawable.ic_menu_search);item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);mSearchView = new MySearchView(getActivity());mSearchView.setOnQueryTextListener(this);mSearchView.setOnCloseListener(this);mSearchView.setIconifiedByDefault(true);item.setActionView(mSearchView);}@Override public boolean onQueryTextChange(String newText) {// Called when the action bar search text has changed. Since this// is a simple array adapter, we can just have it do the filtering.mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;mAdapter.getFilter().filter(mCurFilter);return true;}@Override public boolean onQueryTextSubmit(String query) {// Don't care about this.return true;}@Overridepublic boolean onClose() {if (!TextUtils.isEmpty(mSearchView.getQuery())) {mSearchView.setQuery(null, true);}return true;}@Override public void onListItemClick(ListView l, View v, int position, long id) {// Insert desired behavior here.Log.i("LoaderCustom", "Item clicked: " + id);}@Override public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {// This is called when a new Loader needs to be created. This// sample only has one Loader with no arguments, so it is simple.return new AppListLoader(getActivity());}@Override public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) {// Set the new data in the adapter.mAdapter.setData(data);// The list should now be shown.if (isResumed()) {setListShown(true);} else {setListShownNoAnimation(true);}}@Override public void onLoaderReset(Loader<List<AppEntry>> loader) {// Clear the data in the adapter.mAdapter.setData(null);} }

強(qiáng)大的一逼!這下滿技能,不解釋,自己看。

進(jìn)階總結(jié)

通過前面基礎(chǔ)實(shí)例、源碼分析、進(jìn)階演示你會(huì)發(fā)現(xiàn)Loader的真的非常好用,非常牛逼,牛逼的我不想再解釋啥了,自己體會(huì)吧。

PS:之前看見微博上有人討論AsyncTaskLoader與AsyncTask的區(qū)別,這下徹底明朗了,看完源碼我們?cè)倩剡^頭來總結(jié)性的說說他們二者區(qū)別,如下:

class優(yōu)勢(shì)劣勢(shì)
AsyncTaskLoader會(huì)自動(dòng)刷新數(shù)據(jù)變化;會(huì)自動(dòng)處理Activiy配置變化造成的影響;適合處理純數(shù)據(jù)加載不能實(shí)時(shí)通知UI刷新;不能在onLoadFinished時(shí)主動(dòng)切換生命周期(譬如replace Fragment)
AsyncTask可以與UI實(shí)時(shí)交互及replace操作不會(huì)自動(dòng)處理Activiy配置變化造成的影響

 
好了,該撕逼的也撕了,該裝逼的也裝了,該分析的也分析了,該學(xué)習(xí)的也學(xué)到了,接下來就是看自己如何帶著Loader去叱詫風(fēng)云了。

總結(jié)

以上是生活随笔為你收集整理的Android Loader机制全面详解及源码浅析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 少妇熟女视频一区二区三区 | 老司机成人免费视频 | 欧美人与性动交a欧美精品 日韩免费高清视频 | 午夜激情亚洲 | 日韩精品一二三四区 | 午夜合集| 国产高潮国产高潮久久久 | 三级全黄做爰在线观看 | 亚洲爆乳无码精品aaa片蜜桃 | 亚洲一区二区观看播放 | 国产精品国产三级国产播12软件 | 国产精品999 | 欧美日韩国产一区二区三区在线观看 | 国产午夜精品在线观看 | 中文字幕欧美一区 | 欧美乱大交xxxxx春色视频 | 中文字幕高清在线播放 | 操校花视频| 亚洲一区自拍偷拍 | 欧洲视频一区 | 精品人妻一区二区色欲产成人 | 亚洲区一区二区 | 日日夜夜免费 | av一区在线观看 | 少妇高潮视频 | 日韩av网址在线观看 | 爱爱高潮视频 | 欧美变态绿帽cuckold | 久久av影视 | 日本韩国欧美中文字幕 | 亚洲黄网在线观看 | 国产农村老头老太视频 | 天海翼一区二区 | 美女被啪羞羞粉色视频 | 亚洲欧美中文日韩在线观看 | 国产123在线 | 日日狠狠久久偷偷四色综合免费 | 欧美精品黄色片 | 91一区二区| 爱爱15p| youjizz欧美 | av尤物| 黄色一级生活片 | 成人午夜视频免费 | 欧美国产精品一二三 | 精品女厕偷拍一区二区 | 蜜臀久久99精品久久久画质超高清 | 制服丝袜中文字幕在线 | 天天草综合 | 波多野结衣av在线免费观看 | 亚洲 欧美 综合 | 丁香花免费高清完整在线播放 | 四川丰满妇女毛片四川话 | 99视频国产精品免费观看a | 免费在线看黄色片 | 亚洲xx在线| 色999在线观看 | 9i在线看片成人免费 | 强制憋尿play黄文尿奴 | 69av片 | 国产精品久久免费视频 | 国产精品二区一区二区aⅴ 一卡二卡三卡在线观看 | 91极品视觉盛宴 | 性xxxx另类xxⅹ | 村上里沙番号 | 日韩欧美中文字幕在线视频 | 十八岁世界在线观看高清免费韩剧 | 无套内谢的新婚少妇国语播放 | 亚洲最大av网| 99情趣网| 久久精品性爱视频 | 91看片视频 | 免费日韩一区 | 办公室荡乳欲伦交换bd电影 | 日韩精品免费在线 | 欧美黄色一区二区三区 | 邻居少妇张开腿让我爽了在线观看 | www一区 | 天天激情综合 | 欧美a级在线观看 | 日韩精品――色哟哟 | 亚洲视频自拍偷拍 | av资源新版在线天堂 | 特级a毛片 | 99爱爱视频| 欧美xxxx中国| av日日操 | 精品黑人一区二区三区观看时间 | 中文字幕在线日亚洲9 | 国产中文久久 | 成人午夜激情网 | 国产一级二级在线观看 | 国产又色又爽无遮挡免费动态图 | 兔费看少妇性l交大片免费 日韩高清不卡 | 日本人妻一区 | 欧美色久| 欧美女优在线观看 | 日韩av综合 | 人人妻人人澡人人爽国产一区 |