Loader 知識(shí)梳理(2) - initLoader和restartLoader的區(qū)別

一芋浮、概述

在前面的一篇文章中絮蒿,我們分析了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開發(fā)中常用的場(chǎng)景溯祸。

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

Loader框架 - LoaderManager初探

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

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

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

  • initLoader

  • 通過(guò)調(diào)用這個(gè)方法來(lái)初始化一個(gè)特定IDLoader筷登,如果當(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è)指定IDLoader,如果當(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仲吏,并且重新開始查詢不铆。

三、代碼的區(qū)別

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

  • initLoader
LoaderInfo info = mLoaders.get(id);
if (info == null) {
    //如果不存在關(guān)聯(lián)的Loader誓斥,那么創(chuàng)建一個(gè)新的Loader
    info = createAndInstallLoader(id, args, LoaderManager.LoaderCallbacks<Object>)callback);
} else {
   //如果已經(jīng)存在,那么僅僅替代Callback品腹,傳入的Bundle參數(shù)會(huì)被丟棄岖食。
   info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
  • restartLoader
LoaderInfo info = mLoaders.get(id);
//如果已經(jīng)存在一個(gè)相關(guān)聯(lián)的Loader,那么執(zhí)行操作舞吭。
if (info != null) {
    //mInactiveLoaders列表就是用來(lái)跟蹤那些已經(jīng)被拋棄的Loader
    LoaderInfo inactive = mInactiveLoaders.get(id);
    if (inactive != null) {
        //對(duì)跟蹤列表進(jìn)行一系列的操作泡垃。
    } else {
        //取消被拋棄的Loader,并加入到跟蹤列表當(dāng)中羡鸥,以便在新的Loader完成任務(wù)之后銷毀它蔑穴。
        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);

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

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

也就是說(shuō):

  • 查詢條件一直不變時(shí)蝗羊,使用initLoader
  • 查詢條件有可能發(fā)生改變時(shí)藏澳,采用restartLoader

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

5.1 重建

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

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

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

在這種情況下乱投,假如我們?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)容作為查詢的Key辩昆,查詢?nèi)蝿?wù)就是調(diào)用Loader,延時(shí)2s物喷,并將這個(gè)key作為查詢的結(jié)果展示在TextView上卤材。

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

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;

    @Override
    protected 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);
        }

        @Override
        protected 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 {

        @Override
        public Loader onCreateLoader(int id, Bundle args) {
            Log.d(LOADER_TAG, "onCreateLoader");
            return new MyLoader(getApplicationContext(), args);
        }

        @Override
        public void onLoadFinished(Loader loader, Object data) {
            Log.d(LOADER_TAG, "onLoadFinished");
            showResult((String) data);
        }

        @Override
        public void onLoaderReset(Loader loader) {
            Log.d(LOADER_TAG, "onLoaderReset");
            showResult("");
        }
    }

    private class MyEditTextWatcher implements TextWatcher {

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            Log.d(LOADER_TAG, "onTextChanged=" + s);
            startQuery(s != null ? s.toString() : "");
        }

        @Override
        public void afterTextChanged(Editable s) {}
    }

}

當(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;
    }

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

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

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

6.2 采用restartLoader查詢

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


這時(shí)候吐句,和采用initLoader的結(jié)果完全相同,接下來(lái)再輸入b

可以清楚地看到伐债,在執(zhí)行完restartLoader之后映胁,LoaderManager回調(diào)了onCreateLoader方法讓我們傳入新的Loader木羹,并且之后重新進(jìn)行了查詢,并成功地返回了結(jié)果解孙。
接下來(lái)坑填,我們看一下連續(xù)輸入的情況:

可以看到,雖然a觸發(fā)的任務(wù)已經(jīng)開始了妆距,但是當(dāng)我們輸入b的時(shí)候穷遂,最終得到的時(shí)ab這個(gè)結(jié)果,并且a所觸發(fā)的任務(wù)的結(jié)果并沒(méi)有返回給調(diào)用者娱据,這也是我們所希望的蚪黑,因?yàn)槲覀兊慕Y(jié)果一定是要以用戶最后輸入的為準(zhǔn)。

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

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

    @Override
    protected 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);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        save(outState);
        super.onSaveInstanceState(outState);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末结啼,一起剝皮案震驚了整個(gè)濱河市掠剑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌郊愧,老刑警劉巖朴译,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件井佑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡眠寿,警方通過(guò)查閱死者的電腦和手機(jī)躬翁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)盯拱,“玉大人盒发,你說(shuō)我怎么就攤上這事〗品辏” “怎么了宁舰?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)奢浑。 經(jīng)常有香客問(wèn)我蛮艰,道長(zhǎng),這世上最難降的妖魔是什么雀彼? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任印荔,我火速辦了婚禮,結(jié)果婚禮上详羡,老公的妹妹穿的比我還像新娘仍律。我一直安慰自己,他們只是感情好实柠,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布水泉。 她就那樣靜靜地躺著,像睡著了一般窒盐。 火紅的嫁衣襯著肌膚如雪草则。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天蟹漓,我揣著相機(jī)與錄音炕横,去河邊找鬼。 笑死葡粒,一個(gè)胖子當(dāng)著我的面吹牛份殿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嗽交,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼卿嘲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了夫壁?” 一聲冷哼從身側(cè)響起拾枣,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后梅肤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體司蔬,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年姨蝴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了葱她。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡似扔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搓谆,到底是詐尸還是另有隱情炒辉,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布泉手,位于F島的核電站黔寇,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏斩萌。R本人自食惡果不足惜缝裤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颊郎。 院中可真熱鬧憋飞,春花似錦、人聲如沸姆吭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)内狸。三九已至检眯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昆淡,已是汗流浹背锰瘸。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昂灵,地道東北人避凝。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像眨补,于是被迫代替她去往敵國(guó)和親恕曲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理渤涌,服務(wù)發(fā)現(xiàn)佩谣,斷路器,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • 1 背景## 在Android中任何耗時(shí)的操作都不能放在UI主線程中实蓬,所以耗時(shí)的操作都需要使用異步實(shí)現(xiàn)茸俭。同樣的吊履,在...
    我是昵稱閱讀 1,209評(píng)論 0 3
  • 一、概述 剛開始學(xué)習(xí)Loader的時(shí)候调鬓,只是使用CursorLoader把它當(dāng)作加載封裝在ContentProvi...
    澤毛閱讀 10,123評(píng)論 4 8
  • Android開發(fā)者都經(jīng)歷過(guò)APP UI開發(fā)不當(dāng) 會(huì)造成overDraw艇炎,導(dǎo)致APP UI渲染過(guò)慢,但是很多人卻沒(méi)...
    Tamic閱讀 15,907評(píng)論 30 104
  • 我不會(huì)老去 在未見到你之前 云淡風(fēng)輕 天色也不晚 我可以先泡上一杯青茶 在茶色氤氳中 翻開一座城 城中人相見腾窝,歡笑...
    仲童閱讀 245評(píng)論 0 5