Android Loader源码分析(一)

Loader是Android3.0之后推出的一个异步加载类,他可以方便我们在Activity和Fragment中异步加载数据,Loader有一下特点

  • 可用于每个Activity和Fragment。
  • 支持异步加载数据。
  • 监控其数据源并在内容变化时传递新结果。
  • 在某一配置更改后重建加载器时,会自动重新连接上一个加载器的游标。 因此,它们无需重新查询其数据。

Loader的使用

  • initLoader()保证一个加载器被初始化并激活.它具有两种可能的结果:
    • 如果ID所指的加载器已经存在,那么这个加载器将被重用.
    • 如果加载器不存在,initLoader()就触发LoaderManager.LoaderCallbacks的方法onCreateLoader().这是你实例化并返回一个新加载器的地方.
  • 想要丢弃旧的数据,使用restartLoader()

示例代码

//示例代码
//1:id  2:可选参数  3:回调函数
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的实现类是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)来根据id获取LoaderInfo对象。如果Activity或Fragment是第一次调用initLoader(),则获取到的LoaderInfo对象为null。
如果LoaderInfo对象为null,则接着调用createAndInstallLoader()方法(第十行)
下面是createAndInstallLoader()方法的实现

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);  //创建LoaderInfo对象
    Loader<Object> loader = callback.onCreateLoader(id, args);   //这里是第一调用onCreateLoader()来创建一个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循环,会把所有存在的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则会执行两次。

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的话他会被调用两次
异步执行的过程,接下来请看Android Loader源码分析(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,275评论 25 707
  • 参考 Loader源码分析自定义Loader 设计目的 为了在Activity和Fragment中更加方便地异步加...
    AssIstne阅读 1,134评论 0 5
  • 最近刚从旧公司离职,为面试在做准备,因为平时开发CV大法用得比较多,很多基础知识掌握得不是很牢靠以及很多工具框架只...
    黎清海阅读 2,164评论 1 19
  • 1 背景## 在Android中任何耗时的操作都不能放在UI主线程中,所以耗时的操作都需要使用异步实现。同样的,在...
    我是昵称阅读 1,207评论 0 3
  • 姓名:顾君 单位:宁波大发化纤有限公司 学习组:第234期努力一组 【日精进打卡第4天】 【知~学习】 《六项精进...
    JASONGU_2f28阅读 135评论 0 1