Android開發(fā)者都經(jīng)歷過APP UI開發(fā)不當(dāng) 會(huì)造成overDraw,導(dǎo)致APP UI渲染過慢袖迎,但是很多人卻沒聽過overLoad,overLoad一般是由于開發(fā)者在主線程操作耗時(shí)操作腺晾,導(dǎo)致程序變慢 甚至出現(xiàn)的anr的現(xiàn)象燕锥,那么android早已為這種現(xiàn)象提供完美的解決方案,就是今天給大家說的Loader機(jī)制悯蝉。
一 Loader
Android的裝載器(loader)是從Android 3.0新引入的API , 主要完成單線程耗時(shí)數(shù)據(jù)異步裝載功能归形,并在數(shù)據(jù)有更新自動(dòng)通知UI刷新的作用。業(yè)內(nèi)也叫加載器鼻由,裝載機(jī)暇榴。
Loader用途
Loader一般用在Activity和fragment異步加載數(shù)據(jù),無需重新啟動(dòng)一個(gè)線程來執(zhí)行數(shù)據(jù)加載蕉世,異步加載可以用asyncTask, 但是loader自帶數(shù)據(jù)結(jié)果監(jiān)聽機(jī)制蔼紧,可以方便優(yōu)雅的進(jìn)行UI更新。
作用和優(yōu)點(diǎn):
提供異步加載數(shù)據(jù)功能狠轻;
對(duì)數(shù)據(jù)源變化進(jìn)行監(jiān)聽奸例,實(shí)時(shí)更新數(shù)據(jù);
在Activity配置發(fā)生變化(如橫豎屏切換)時(shí)不避免數(shù)據(jù)重復(fù)加載向楼;
適用于任何Activity和Fragment查吊;
加載耗時(shí)數(shù)據(jù)常用方式
android開發(fā)者都知道不能再UI線程里去執(zhí)行耗時(shí)操作,甚至在4.0里已經(jīng)無法在主線程里去訪問網(wǎng)絡(luò)湖蜕,那么一般加載耗時(shí)操作有以下辦法逻卖。
1 2B加載
2 普通加載
3 文藝加載
為何說1和2是不可取呢,我們從Loader源碼看起昭抒,文章結(jié)束會(huì)知道答案评也。
二 Loader實(shí)現(xiàn)
Loader源碼在android.content下面虚茶,可見它的份量有多重,loader機(jī)制包括LoaderManager仇参,Loader嘹叫,LoaderCallbacks三部分,
LoaderManager 來管理我們的laoder實(shí)例诈乒,獲取罩扇,初始化,重啟一個(gè)loader,
Loader 來執(zhí)行我們的異步操作怕磨,有開始喂饥,完成,后臺(tái)加載中等接口實(shí)現(xiàn)
LoaderCallbacks 來執(zhí)行我們的loader回調(diào)肠鲫,主要是綁定分發(fā)Loader员帮,完成加載,重置數(shù)據(jù)等导饲。
流程如下圖:
1 LoaderManager
LoaderManager是抽象類捞高,負(fù)責(zé)管理一組Loader,主要定義執(zhí)行Loader的一些抽象方法,類結(jié)構(gòu)如下圖:
從上圖看以看出渣锦,Ta里面主要初始化loader,獲取 重啟,銷毀一個(gè)loader硝岗,也包含一個(gè)內(nèi)部成員變量LoaderCallback回調(diào),主要方便我們?cè)谏蠈訉懟卣{(diào)實(shí)現(xiàn)累操作袋毙,但真正是由他的實(shí)現(xiàn)類LoaderManagerImpl去完成操作的,
LoaderManagerImpl 記錄著一組LoaderInfo信息型檀,(ps:Activity也同等擁有activityInfo一樣,只不過記錄是Activityrecord來完成听盖,其實(shí)谷歌很多源碼都是相同的)胀溺,并且持有LoaderManager.LoaderCallbacks, mLoader等成員皆看,負(fù)責(zé)對(duì)Loader和LoaderCallbacks的對(duì)應(yīng)回調(diào)仓坞,內(nèi)部基于觀察者模式實(shí)現(xiàn),源碼不在解讀;
2 Loader
Loader是具體來操作任務(wù)的類悬蔽,負(fù)責(zé)去調(diào)用不同渠道的數(shù)據(jù)接口扯躺,比如數(shù)據(jù)庫(kù),contentProvider, 文件等蝎困。
從大致的UML圖我可以了解loader持有一個(gè)內(nèi)部觀察者录语,和一些注冊(cè)注銷觀者的內(nèi)部方法,并且已經(jīng)暴露出來的加載操作的狀態(tài)步驟的方法禾乘,包括加載中澎埠,取消加載,強(qiáng)制加載始藕,內(nèi)容發(fā)生改變等蒲稳,
在平常的開發(fā)中氮趋,谷歌為我們提供了laoder的子類,AsyncTaskLoader江耀,CursorLoader等子類剩胁, 源碼不在介紹,現(xiàn)在說下他們的不同點(diǎn)祥国。CursorLoader也是AsyncTaskLoader的子類昵观,主要負(fù)責(zé)數(shù)據(jù)庫(kù)查詢的異步加載,AsyncTaskLoader可用來所有異步加載舌稀。
2.1 AsyncTaskLoader
AsyncTaskLoader繼承了Loader, 除了擁有l(wèi)oader的功能啊犬,還有executePendingTask(), dispatchOnCancelled()壁查,onLoadInBackground()等方法觉至,最神奇的是他擁有AsyncTask的實(shí)例,并且實(shí)現(xiàn)Runnable,這是他能進(jìn)行異步的原因所在睡腿。對(duì)AsyncTask不熟悉的請(qǐng)自我補(bǔ)腦语御,看如下代碼,
筆者看了源碼嫉到,AsyncTaskLoader擁有AsyncTask沃暗,在自身實(shí)例化后開啟一個(gè)線程,自我進(jìn)行executePendingTask()何恶,此方法里其實(shí)就在執(zhí)行asyncTask的mTask.executeOnExecutor(mExecutor,(Void[])null);?來實(shí)現(xiàn)AsyncTaskLoader的自我監(jiān)聽機(jī)制,當(dāng)然自身輪詢和通信是離不開Handler的 因?yàn)檎麄€(gè)android的通訊就是建立在Handler(底層binder)基礎(chǔ)上嚼黔,這里不再分析细层。
2.2 CursorLoader
CursorLoader是AsyncTaskLoader的子類,內(nèi)部持有ForceLoadContentObserver變量唬涧,觀察者來實(shí)現(xiàn)對(duì)數(shù)據(jù)源的數(shù)據(jù)更新疫赎,執(zhí)行加載數(shù)據(jù)操作,最重要的當(dāng)然是離不開查詢操作碎节,內(nèi)部主要代碼:
三 怎么使用loader
1 啟動(dòng)一個(gè)Loader
Activity初始化在oncreate()初始化捧搞,一個(gè)Activity或Fragment中LoaderManager管理一個(gè)或多個(gè)Loader實(shí)例,每個(gè)Activity或Fragment只有一個(gè)LoaderManager狮荔,我們可以在Activity的onCreate()或Fragment的onActivityCreated()里初始化一個(gè)Loader胎撇。例如:
getLoaderManager().initLoader(0, null, new DataLoaderCallback());
可以看見上面的initLoader()方法有三個(gè)參數(shù):
第一個(gè)參數(shù)代表當(dāng)前Loader的ID ,用來區(qū)分哪個(gè)loader;
第二個(gè)參數(shù)代表提供給Loader構(gòu)造函數(shù)的參數(shù)殖氏,Bundle對(duì)象類型 晚树,可選;
第三個(gè)參數(shù)代表LoaderManager.LoaderCallbacks的回調(diào)實(shí)現(xiàn) 需要我自我實(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ù)邏輯的處理即可锻全。
2 實(shí)現(xiàn)LoaderManager.Callbacks回調(diào)
LoaderManager.LoaderCallbacks是LoaderManager的回調(diào)交互接口。LoaderManager.LoaderCallbacks包含以下三個(gè)方法:
onCreateLoader()
實(shí)例化并返回一個(gè)新創(chuàng)建給指定ID的Loader對(duì)象录煤;第一啟動(dòng)時(shí)調(diào)用
onLoadFinished()
load完成之后回調(diào)此方法鳄厌;每次都調(diào)用
onLoaderReset()
當(dāng)創(chuàng)建好的Loader被reset時(shí)調(diào)用此方法,會(huì)清空已綁定數(shù)據(jù)妈踊,此時(shí)CreatLoader會(huì)重新執(zhí)行
3 Loader使用實(shí)例
1》 初始化loader
getLoaderManager().initLoader(0, null, new DataLoaderCallback());
2》實(shí)現(xiàn)callback接口了嚎,注冊(cè)自我監(jiān)聽回調(diào)
當(dāng)然你也可以用來綁定谷歌提供的CursorLoader ,在Loader創(chuàng)建的時(shí)候被調(diào)用,這里使用一個(gè)ContentProvider獲取數(shù)據(jù)廊营,所以使用CursorLoader返回?cái)?shù)據(jù)
3》 繼承Loader歪泳,構(gòu)造自我的數(shù)據(jù)綁定,和數(shù)據(jù)適配
在這里我們模擬了構(gòu)造一組數(shù)據(jù)露筒,當(dāng)然你也可以在loadInBackgruond去讀文件呐伞,訪問網(wǎng)絡(luò),查詢數(shù)據(jù)庫(kù)
4 拓展
1》 用來自動(dòng)刷新ContentPorvider
在我們使用CurSorLoader時(shí)大家都會(huì)考慮一種情況的處理—–當(dāng)數(shù)據(jù)庫(kù)發(fā)生變化時(shí)如何自動(dòng)刷新當(dāng)前UI慎式,數(shù)據(jù)庫(kù)在數(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()方法刑枝;
通過上面兩步我們就能實(shí)現(xiàn)CurSorLoader的自動(dòng)數(shù)據(jù)刷新功能了香嗓;可以發(fā)現(xiàn),所謂的CurSorLoader自動(dòng)刷新也是對(duì)文章開頭說的觀察者模式装畅,所以不再過多說明靠娱。
2》不使用ContentPorvider的自動(dòng)刷新
四Loaders相關(guān)源碼流程:
通過上面我們的源碼分析和分析前那副圖可以總結(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的釋放(譬如Cursor要要主動(dòng)關(guān)閉游標(biāo)的等抑党,文件流要置空等)不需要我們?nèi)藶樘幚恚琇oader會(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)刷新特铝。
五 總結(jié)
通過前面基礎(chǔ)實(shí)例、源碼分析壹瘟、拓展你會(huì)發(fā)現(xiàn)Loader很強(qiáng)大鲫剿,例如在普通展現(xiàn)某個(gè)android手機(jī)有多少應(yīng)用程序,加載已安裝app時(shí)候稻轨,其實(shí)loader就能排上用場(chǎng)灵莲。
詳細(xì)見谷歌對(duì)Loader介紹:
https://developer.android.com/reference/android/content/AsyncTaskLoader.html。
PS:順便說下AsyncTaskLoader與AsyncTask的區(qū)別殴俱,看完源碼我們?cè)倩剡^頭來總結(jié)性的說說他們二者區(qū)別政冻,如下:
最主要是加載數(shù)據(jù)枚抵,使用loader我們無須關(guān)注數(shù)據(jù)何時(shí)改變了,也無需關(guān)注activity的生命周期明场,做到數(shù)據(jù)不被重復(fù)多次加載情況汽摹,activty銷毀數(shù)據(jù)自動(dòng)釋放的作用,做到一次加載多次使用的效果苦锨,我們可以依據(jù)需求逼泣,拿著loader變活靈通,這里的博大精深還需要你自己體會(huì)舟舒。
案例:https://github.com/NeglectedByBoss/Loader
更多文章請(qǐng)猛戳
第一時(shí)間獲取大佬技術(shù)文章和行業(yè)動(dòng)態(tài)請(qǐng)關(guān)注微信公眾號(hào)拉庶!