Android Loader源碼分析(一)

Loader是Android3.0之后推出的一個異步加載類,他可以方便我們在Activity和Fragment中異步加載數(shù)據(jù)废酷,Loader有一下特點

  • 可用于每個Activity和Fragment澈蟆。
  • 支持異步加載數(shù)據(jù)。
  • 監(jiān)控其數(shù)據(jù)源并在內容變化時傳遞新結果寥闪。
  • 在某一配置更改后重建加載器時疲憋,會自動重新連接上一個加載器的游標敛纲。 因此翰绊,它們無需重新查詢其數(shù)據(jù)监嗜。

Loader的使用

  • initLoader()保證一個加載器被初始化并激活.它具有兩種可能的結果:
    • 如果ID所指的加載器已經存在桐猬,那么這個加載器將被重用.
    • 如果加載器不存在,initLoader()就觸發(fā)LoaderManager.LoaderCallbacks的方法onCreateLoader().這是你實例化并返回一個新加載器的地方.
  • 想要丟棄舊的數(shù)據(jù),使用restartLoader()

示例代碼

//示例代碼
//1:id  2:可選參數(shù)  3:回調函數(shù)
getLoaderManager().initLoader(1, null, mCallback);
getLoaderManager().restartLoader(1, null, mCallback);
getLoaderManager().destroyLoader(1);
  
LoaderManager.LoaderCallbacks<Object> mCallback = new LoaderManager.LoaderCallbacks<Object>() {
    @Override
    public Loader<Object> onCreateLoader(int id, Bundle args) {
        Log.e("MainActivity", "onCreateLoader---" + id);
        Loader<Object> loader = new Loader<>(MainActivity.this);
        return loader;
    }
 
    @Override
    public void onLoadFinished(Loader<Object> loader, Object data) {
        Log.e("MainActivity", "onLoadFinished");
    }
 
    @Override
    public void onLoaderReset(Loader<Object> loader) {
        Log.e("MainActivity", "onLoaderReset");
    }
};

ps:當onCreateLoader的返回值為null的時候厨钻,onCreateLoader會被回調兩次,其中第二次是在Activity的onStart 方法中回調的

getLoaderManager()獲取到的LoaderManager的實現(xiàn)類是LoaderManagerImpl
LoaderManagerImpl的initLoader方法:

public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
    if (mCreatingLoader) {
        throw new IllegalStateException("Called while creating a loader");
    }
 
    LoaderInfo info = mLoaders.get(id);
 
    if (info == null) {
        // Loader doesn't already exist; create.
10      info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
    } else {
        info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
    }
    if (info.mHaveData && mStarted) {
        // If the loader has already generated its data, report it now.
        info.callOnLoadFinished(info.mLoader, info.mData);
    }
 
    return (Loader<D>)info.mLoader;
}

initLoader()會先通過mLoaders.get(id)來根據(jù)id獲取LoaderInfo對象夯膀。如果Activity或Fragment是第一次調用initLoader()苍蔬,則獲取到的LoaderInfo對象為null诱建。
如果LoaderInfo對象為null银室,則接著調用createAndInstallLoader()方法(第十行)
下面是createAndInstallLoader()方法的實現(xiàn)

private LoaderInfo createAndInstallLoader(int id, Bundle args,
        LoaderManager.LoaderCallbacks<Object> callback) {
    try {
        mCreatingLoader = true;
        LoaderInfo info = createLoader(id, args, callback);
        installLoader(info);
        return info;
    } finally {
        mCreatingLoader = false;
    }
}
  
private LoaderInfo createLoader(int id, Bundle args,
        LoaderManager.LoaderCallbacks<Object> callback) {
    LoaderInfo info = new LoaderInfo(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);  //創(chuàng)建LoaderInfo對象
    Loader<Object> loader = callback.onCreateLoader(id, args);   //這里是第一調用onCreateLoader()來創(chuàng)建一個loader對象(可能為null)
    info.mLoader = (Loader<Object>)loader;
    return info;
}
  
void installLoader(LoaderInfo info) {
    mLoaders.put(info.mId, info);
    if (mStarted) {
        // The activity will start all existing loaders in it's onStart(),
        // so only start them here if we're past that point of the activitiy's
        // life cycle  這里如果initloader方法如果在onStart()之后,則在這里start第二次
        info.start();
    }
}

createAndInstallLoader會調用createLoaderinstallLoader

installLoader方法里面的注釋說
The activity will start all existing loaders in it's onStart(),
Activity的onStart()方法:

protected void onStart() {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
    mCalled = true;
    mFragments.doLoaderStart();
    getApplication().dispatchActivityStarted(this);
}
  
//mFragments.doLoaderStart()方法最終會調用到LoaderManager的doStart()方法
void doStart() {
    if (mStarted) {
        RuntimeException e = new RuntimeException("here");
        e.fillInStackTrace();
        Log.w(TAG, "Called doStart when already started: " + this, e);
        return;
    }
     
    mStarted = true;
 
    // Call out to sub classes so they can start their loaders
    // Let the existing loaders know that we want to be notified when a load is complete
    for (int i = mLoaders.size()-1; i >= 0; i--) {
        mLoaders.valueAt(i).start();
    }    //在這里會 start all existing loaders
}

在Activity的onStart()生命周期方法中,最終會調用到LoaderManagerImpl的doStart()方法,在doStart()方法中會有一個for循環(huán)苗桂,會把所有存在的Loader都調用一次start()方法
接著就應該是LoaderInfo的start方法:

void start() {
    if (mRetaining && mRetainingStarted) {
        // Our owner is started, but we were being retained from a
        // previous instance in the started state...  so there is really
        // nothing to do here, since the loaders are still started.
        mStarted = true;
        return;
    }
 
    if (mStarted) {
        // If loader already started, don't restart.
        return;
    }
 
    mStarted = true;
     
    //這里createLoader方法里面第一次調用onCreateLoader()如果返回不為空,則不再調用第二次
    if (mLoader == null && mCallbacks != null) {
       mLoader = mCallbacks.onCreateLoader(mId, mArgs);
    }
    if (mLoader != null) {
        if (mLoader.getClass().isMemberClass()
                && !Modifier.isStatic(mLoader.getClass().getModifiers())) {
            throw new IllegalArgumentException(
                    "Object returned from onCreateLoader must not be a non-static inner member class: "
                    + mLoader);
        }
        if (!mListenerRegistered) {
            mLoader.registerListener(mId, this);
            mLoader.registerOnLoadCanceledListener(this);
            mListenerRegistered = true;
        }
        mLoader.startLoading();
    }
}

So:如果onCreateLoader返回為null告组,則onCreateLoader則會執(zhí)行兩次煤伟。

start方法中有一個非常重要的成員——mLoader(第一次調用onCreateLoader方法返回的Loader對象)
start方法最后會調用mLoader.startLoading();
代碼:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.e("gj", "before");
    getLoaderManager().initLoader(1, null, mCallback);
    Log.e("gj", "after");
}
 
@Override
protected void onStart() {
    Log.e("gj", "before_onStart");
    super.onStart();
    Log.e("gj", "after_onStart");
}
LoaderManager.LoaderCallbacks<Object> mCallback = new LoaderManager.LoaderCallbacks<Object>() {
    @Override
    public Loader<Object> onCreateLoader(int id, Bundle args) {
        Log.e("gj", "onCreateLoader---" + id);
        return null;
    }
 
    @Override
    public void onLoadFinished(Loader<Object> loader, Object data) {
        Log.e("gj", "onLoadFinished");
    }
 
    @Override
    public void onLoaderReset(Loader<Object> loader) {
        Log.e("gj", "onLoaderReset");
    }
};

輸出日志

before_onCreate
onCreateLoader---1
after_onCreate
before_onStart
onCreateLoader---1
after_onStart

當在onStart()之后initLoader的代碼

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}
 
@Override
protected void onStart() {
    Log.e("gj", "before_onStart");
    super.onStart();
    Log.e("gj", "after_onStart");
}
 
@Override
protected void onResume() {
    super.onResume();
    Log.e("gj", "before_onResume");
    getLoaderManager().initLoader(1, null, mCallback);
    Log.e("gj", "after_onResume");
}
 
LoaderManager.LoaderCallbacks<Object> mCallback = new LoaderManager.LoaderCallbacks<Object>() {
    @Override
    public Loader<Object> onCreateLoader(int id, Bundle args) {
        Log.e("gj", "onCreateLoader---" + id);
        return null;
    }
 
    @Override
    public void onLoadFinished(Loader<Object> loader, Object data) {
        Log.e("gj", "onLoadFinished");
    }
 
    @Override
    public void onLoaderReset(Loader<Object> loader) {
        Log.e("gj", "onLoaderReset");
    }
 
};

輸出日志:

before_onStart
after_onStart
before_onResume
onCreateLoader---1
onCreateLoader---1
after_onResume

到這里只分析到mLoader.startLoading(); 知道了為什么onCreateLoader返回為null的話他會被調用兩次
異步執(zhí)行的過程,接下來請看Android Loader源碼分析(二)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市便锨,隨后出現(xiàn)的幾起案子围辙,更是在濱河造成了極大的恐慌,老刑警劉巖放案,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姚建,死亡現(xiàn)場離奇詭異,居然都是意外死亡吱殉,警方通過查閱死者的電腦和手機掸冤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來友雳,“玉大人贩虾,你說我怎么就攤上這事×ぺ澹” “怎么了缎罢?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長考杉。 經常有香客問我策精,道長,這世上最難降的妖魔是什么崇棠? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任咽袜,我火速辦了婚禮,結果婚禮上枕稀,老公的妹妹穿的比我還像新娘询刹。我一直安慰自己,他們只是感情好萎坷,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布凹联。 她就那樣靜靜地躺著,像睡著了一般哆档。 火紅的嫁衣襯著肌膚如雪蔽挠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天瓜浸,我揣著相機與錄音澳淑,去河邊找鬼。 笑死插佛,一個胖子當著我的面吹牛杠巡,可吹牛的內容都是我干的。 我是一名探鬼主播雇寇,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼氢拥,長吁一口氣:“原來是場噩夢啊……” “哼蚌铜!你這毒婦竟也來了?” 一聲冷哼從身側響起兄一,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤厘线,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后出革,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體造壮,經...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年骂束,在試婚紗的時候發(fā)現(xiàn)自己被綠了耳璧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡展箱,死狀恐怖旨枯,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情混驰,我是刑警寧澤攀隔,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站栖榨,受9級特大地震影響昆汹,放射性物質發(fā)生泄漏。R本人自食惡果不足惜婴栽,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一满粗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧愚争,春花似錦映皆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狸膏,卻和暖如春沟饥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背湾戳。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留广料,地道東北人砾脑。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像艾杏,于是被迫代替她去往敵國和親韧衣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,324評論 25 707
  • 參考 Loader源碼分析自定義Loader 設計目的 為了在Activity和Fragment中更加方便地異步加...
    AssIstne閱讀 1,158評論 0 5
  • 最近剛從舊公司離職,為面試在做準備畅铭,因為平時開發(fā)CV大法用得比較多氏淑,很多基礎知識掌握得不是很牢靠以及很多工具框架只...
    黎清海閱讀 2,202評論 1 19
  • 1 背景## 在Android中任何耗時的操作都不能放在UI主線程中,所以耗時的操作都需要使用異步實現(xiàn)硕噩。同樣的假残,在...
    我是昵稱閱讀 1,221評論 0 3
  • 姓名:顧君 單位:寧波大發(fā)化纖有限公司 學習組:第234期努力一組 【日精進打卡第4天】 【知~學習】 《六項精進...
    JASONGU_2f28閱讀 145評論 0 1