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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Loader 知识梳理(2) initLoader和restartLoader的区别

發(fā)布時(shí)間:2025/3/17 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Loader 知识梳理(2) initLoader和restartLoader的区别 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、概述

在前面的一篇文章中,我們分析了LoaderManager的實(shí)現(xiàn),里面涉及到了很多的細(xì)節(jié)問(wèn)題,我們并不需要刻意地記住每個(gè)流程,之所以需要分析,以后在使用的過(guò)程中,如果遇到問(wèn)題了,再去查看相關(guān)的源代碼就好了。 對(duì)于Loader框架的理解,我認(rèn)為掌握以下四個(gè)方面的東西就可以了:

  • 對(duì)LoaderManager的實(shí)現(xiàn)原理有一個(gè)大概的印象。
  • LoaderManager的三個(gè)關(guān)鍵方法initLoader/restartLoader/destroyLoader的使用場(chǎng)景。
  • 學(xué)會(huì)自定義Loader來(lái)實(shí)現(xiàn)數(shù)據(jù)的異步加載。
  • 總結(jié)一些App開(kāi)發(fā)中常用的場(chǎng)景。

第一點(diǎn)可以參考前面的這篇文章:

Loader框架 - LoaderManager初探

今天這篇,我們將專(zhuān)注于分析第二點(diǎn):initLoader/restartLoader的區(qū)別

二、基本概念的區(qū)別

首先,我們回顧一下,對(duì)于init/restart的定義:

  • initLoader

  • 通過(guò)調(diào)用這個(gè)方法來(lái)初始化一個(gè)特定ID的Loader,如果當(dāng)前已經(jīng)有一個(gè)和這個(gè)ID關(guān)聯(lián)的Loader,那么并不會(huì)去回調(diào)onCreateLoader來(lái)通知使用者傳入一個(gè)新的 Loader實(shí)例替代那個(gè)舊的實(shí)例,僅僅是替代Callback,也就是說(shuō),上面的Bundle參數(shù)被丟棄了;而假如不存在一個(gè)關(guān)聯(lián)的實(shí)例,那么會(huì)進(jìn)行初始化,并啟動(dòng)它。

  • 這個(gè)方法通常是在Activity/Fragment的初始化階段調(diào)用,因?yàn)樗鼤?huì)判斷之前是否存在相同的Loader,這樣我們就可以復(fù)用之前已經(jīng)加載過(guò)的數(shù)據(jù),當(dāng)屏幕裝轉(zhuǎn)導(dǎo)致Activity重建的時(shí)候,我們就不需要再去重新創(chuàng)建一個(gè)新的Loader,也免去了重新讀取數(shù)據(jù)的過(guò)程。

  • restartLoader

  • 調(diào)用這個(gè)方法,將會(huì)重新創(chuàng)建一個(gè)指定ID的Loader,如果當(dāng)前已經(jīng)有一個(gè)和這個(gè)ID關(guān)聯(lián)的Loader,那么會(huì)對(duì)它進(jìn)行canceled/stopped/destroyed等操作,之后,使用新傳入的Bundle參數(shù)來(lái)創(chuàng)建一個(gè)新的Loader,并在數(shù)據(jù)加載完畢后遞交給調(diào)用者。

  • 并且,在調(diào)用完這個(gè)函數(shù)之后,所有之前和這個(gè)ID關(guān)聯(lián)的Loader都會(huì)失效,我們將不會(huì)收到它們傳遞過(guò)來(lái)的任何數(shù)據(jù)。

總結(jié)下來(lái)就是:

  • 當(dāng)調(diào)用上面這兩個(gè)方法時(shí),如果不存在一個(gè)和ID關(guān)聯(lián)的Loader,那么這兩個(gè)方法是完全相同的。
  • 如果已經(jīng)存在相關(guān)聯(lián)的Loader,那么init方法除了替代Callback,不會(huì)做任何其它的事情,包括取消/停止等。而restart方法將會(huì)創(chuàng)建一個(gè)新的Loader,并且重新開(kāi)始查詢(xún)。

三、代碼的區(qū)別

為了方便大家更直觀地理解,我們截取一部分的源碼來(lái)看一下:

  • initLoader
LoaderInfo info = mLoaders.get(id); if (info == null) {//如果不存在關(guān)聯(lián)的Loader,那么創(chuàng)建一個(gè)新的Loaderinfo = createAndInstallLoader(id, args, LoaderManager.LoaderCallbacks<Object>)callback); } else {//如果已經(jīng)存在,那么僅僅替代Callback,傳入的Bundle參數(shù)會(huì)被丟棄。info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback; } 復(fù)制代碼
  • restartLoader
LoaderInfo info = mLoaders.get(id); //如果已經(jīng)存在一個(gè)相關(guān)聯(lián)的Loader,那么執(zhí)行操作。 if (info != null) {//mInactiveLoaders列表就是用來(lái)跟蹤那些已經(jīng)被拋棄的LoaderLoaderInfo inactive = mInactiveLoaders.get(id);if (inactive != null) {//對(duì)跟蹤列表進(jìn)行一系列的操作。} else {//取消被拋棄的Loader,并加入到跟蹤列表當(dāng)中,以便在新的Loader完成任務(wù)之后銷(xiāo)毀它。info.mLoader.abandon();mInactiveLoaders.put(id, info);} } //通知調(diào)用者,創(chuàng)建一個(gè)新的Loader,這個(gè)Loader將會(huì)和新的Bundle和Callback相關(guān)聯(lián)。 info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback); 復(fù)制代碼

通過(guò)上面的代碼,就印證了前面第二節(jié)我們的說(shuō)話(huà),我們根據(jù)這些區(qū)別可以知道它們各自的職責(zé):

  • initLoader是用來(lái)確保Loader能夠被初始化,如果已經(jīng)存在相同ID的Loader,那么它會(huì)復(fù)用之前的。
  • restartLoader的應(yīng)用場(chǎng)景則是我們的查詢(xún)條件發(fā)生了改變。因?yàn)長(zhǎng)oaderManager是用ID關(guān)聯(lián)的,當(dāng)這個(gè)Loader已經(jīng)獲取到了數(shù)據(jù),那么就不需要再啟動(dòng)它了。因此當(dāng)我們的需求發(fā)生了改變,就需要重新創(chuàng)建一個(gè)Loader。

也就是說(shuō):

  • 查詢(xún)條件一直不變時(shí),使用initLoader
  • 查詢(xún)條件有可能發(fā)生改變時(shí),采用restartLoader。

五、對(duì)于屏幕旋轉(zhuǎn)的情況

5.1 重建

當(dāng)我們?cè)贛anifest.xml沒(méi)有給Activity配置configChanged的時(shí)候,旋轉(zhuǎn)屏幕會(huì)導(dǎo)致的Activity/Fragment重建,這時(shí)候有兩點(diǎn)需要注意的:

  • 由于此時(shí)我們的查詢(xún)條件并不會(huì)發(fā)生改變,并且LoaderManager會(huì)幫我們恢復(fù)Loader的狀態(tài)。因此,我們沒(méi)有必要再去調(diào)用restartLoader來(lái)重新創(chuàng)建Loader來(lái)執(zhí)行一次耗時(shí)的查詢(xún)操作。

  • LoaderManager雖然會(huì)恢復(fù)Loader,但是它不會(huì)保存Callback實(shí)例,因此,如果我們希望在旋轉(zhuǎn)完之后獲得數(shù)據(jù),那么至少要調(diào)用一次initLoader來(lái)傳入一個(gè)新的Callback進(jìn)行監(jiān)聽(tīng)。

在這種情況下,假如我們?cè)谛D(zhuǎn)之前Loader已經(jīng)加載數(shù)據(jù)完畢了,那么onLoadFinished會(huì)立即被會(huì)調(diào)

5.2 沒(méi)有重建

當(dāng)沒(méi)有重建時(shí),不會(huì)走onCreate方法,因此需要在別的地方來(lái)初始化Loader。

5.3 LoaderId

針對(duì)上面的這兩種情況,我們都需要自己去保存LoaderId,在組件恢復(fù)之后,通過(guò)這個(gè)保存的id去調(diào)用init/restart方法,一般情況下,是通過(guò)savedInstanceState來(lái)保存的。

六、示例

現(xiàn)在,我們通過(guò)一個(gè)很簡(jiǎn)單的例子,來(lái)看一下,initLoader/restartLoader的區(qū)別,我們的Demo中有一個(gè)EditText和一個(gè)TextView,當(dāng)EditText發(fā)生改變時(shí),我們將當(dāng)前EditText的內(nèi)容作為查詢(xún)的Key,查詢(xún)?nèi)蝿?wù)就是調(diào)用Loader,延時(shí)2s,并將這個(gè)key作為查詢(xún)的結(jié)果展示在TextView上。

6.1 采用initLoader來(lái)查詢(xún):

public class MainActivity extends Activity {private static final String LOADER_TAG = "loader_tag";private static final String QUERY = "query";private MyLoaderCallback mMyLoaderCallback;private TextView mResultView;private EditText mEditText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();}private void init() {mEditText = (EditText) findViewById(R.id.loader_input);mResultView = (TextView) findViewById(R.id.loader_result);mEditText.addTextChangedListener(new MyEditTextWatcher());mMyLoaderCallback = new MyLoaderCallback();}private void startQuery(String query) {if (query != null) {Bundle bundle = new Bundle();bundle.putString(QUERY, query);getLoaderManager().initLoader(0, bundle, mMyLoaderCallback);}}private void showResult(String result) {if (mResultView != null) {mResultView.setText(result);}}private static class MyLoader extends BaseDataLoader<String> {public MyLoader(Context context, Bundle bundle) {super(context, bundle);}@Overrideprotected String loadData(Bundle bundle) {Log.d(LOADER_TAG, "loadData");try {Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}return bundle != null ? bundle.getString(QUERY) : "empty";}}private class MyLoaderCallback implements LoaderManager.LoaderCallbacks {@Overridepublic Loader onCreateLoader(int id, Bundle args) {Log.d(LOADER_TAG, "onCreateLoader");return new MyLoader(getApplicationContext(), args);}@Overridepublic void onLoadFinished(Loader loader, Object data) {Log.d(LOADER_TAG, "onLoadFinished");showResult((String) data);}@Overridepublic void onLoaderReset(Loader loader) {Log.d(LOADER_TAG, "onLoaderReset");showResult("");}}private class MyEditTextWatcher implements TextWatcher {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {Log.d(LOADER_TAG, "onTextChanged=" + s);startQuery(s != null ? s.toString() : "");}@Overridepublic void afterTextChanged(Editable s) {}}} 復(fù)制代碼

當(dāng)我們輸入a時(shí),成功地獲取到了數(shù)據(jù):

之后,我們繼續(xù)輸入b,發(fā)現(xiàn)onLoadFinished立即被回調(diào)了,但是結(jié)果還是之前地a 我們通過(guò)AS的斷電發(fā)現(xiàn),整個(gè)調(diào)用過(guò)程如下: 也就是在initLoader之后,立即執(zhí)行了:

public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {//按照前面的分析,此時(shí)的info不為null。if (info.mHaveData && mStarted) {info.callOnLoadFinished(info.mLoader, info.mData);}return (Loader<D>)info.mLoader;} 復(fù)制代碼

而之后,callOnLoadFinished就會(huì)把之前的數(shù)據(jù)傳回給調(diào)用者,因此沒(méi)有執(zhí)行onCreateLoader,也沒(méi)有進(jìn)行查詢(xún)操作:

void callOnLoadFinished(Loader<Object> loader, Object data) {//傳遞數(shù)據(jù)給調(diào)用者.if (mCallbacks != null) {mCallbacks.onLoadFinished(loader, data);}} 復(fù)制代碼

假如,我們?cè)赼觸發(fā)的任務(wù)還沒(méi)有執(zhí)行時(shí)就輸入b,我們看看會(huì)發(fā)生什么,可以看到,它并不會(huì)考慮后來(lái)的任務(wù):

6.2 采用restartLoader查詢(xún)

還是按照前面的方式,我們先輸入a:

這時(shí)候,和采用initLoader的結(jié)果完全相同,接下來(lái)再輸入b: 可以清楚地看到,在執(zhí)行完restartLoader之后,LoaderManager回調(diào)了onCreateLoader方法讓我們傳入新的Loader,并且之后重新進(jìn)行了查詢(xún),并成功地返回了結(jié)果。 接下來(lái),我們看一下連續(xù)輸入的情況: 可以看到,雖然a觸發(fā)的任務(wù)已經(jīng)開(kāi)始了,但是當(dāng)我們輸入b的時(shí)候,最終得到的時(shí)ab這個(gè)結(jié)果,并且a所觸發(fā)的任務(wù)的結(jié)果并沒(méi)有返回給調(diào)用者,這也是我們所希望的,因?yàn)槲覀兊慕Y(jié)果一定是要以用戶(hù)最后輸入的為準(zhǔn)。

6.3 加上保證正確初始化的代碼

最后,我們?cè)偌由媳WCLoader能夠正確初始化的代碼,一個(gè)簡(jiǎn)單的聯(lián)想輸入 - 加載框架就搭建好了。

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();restore(savedInstanceState);}private void save(Bundle outState) {if (mEditText != null) {outState.putString(QUERY, mEditText.getText().toString());}}private void restore(Bundle savedInstanceState) {if (savedInstanceState != null) {Bundle bundle = new Bundle();String query = bundle.getString(QUERY);bundle.putString(QUERY, query);getLoaderManager().initLoader(0, bundle, mMyLoaderCallback);}}@Overrideprotected void onSaveInstanceState(Bundle outState) {save(outState);super.onSaveInstanceState(outState);} 復(fù)制代碼

總結(jié)

以上是生活随笔為你收集整理的Loader 知识梳理(2) initLoader和restartLoader的区别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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