LoaderManager.restartLoader()总是会导致调用onCreateLoader()吗?

15

LoaderManager有一个叫做restartLoader()的方法:

public abstract Loader<D> restartLoader (int id, Bundle args, LoaderCallbacks<D> callback)

启动新的或重新启动现有的Loader,将回调注册到其中,并(如果当前已启动活动/片段)开始加载它。 如果以前已启动具有相同ID的加载器,则当新加载器完成其工作时,它将自动被销毁。在旧的加载器被销毁之前,将传递回调。

根据开发者指南的说明,我得出的结论是,确实会从restartLoader()调用onCreateLoader方法:

重新启动加载器

...

要丢弃旧数据,可以使用restartLoader()。例如,当用户的查询更改时,SearchView.OnQueryTextListener的此实现会重新启动加载器。 需要重新启动加载器,以便它可以使用修订后的搜索过滤器进行新查询:

public boolean onQueryTextChanged(String newText) {
    // Called when the action bar search text has changed.  Update
    // the search filter, and restart the loader to do a new query
    // with this filter.
    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    getLoaderManager().restartLoader(0, null, this);
    return true;
}

public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    // NOTE: The Loader is instantiated with the user's query

    Uri baseUri;       
    if (mCurFilter != null) {
        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(mCurFilter));
    } else {
        baseUri = Contacts.CONTENT_URI;
    }

    // Now create and return a CursorLoader that will take care of
    // creating a Cursor for the data being displayed.
    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
            + Contacts.DISPLAY_NAME + " != '' ))";
    return new CursorLoader(getActivity(), baseUri,
            CONTACTS_SUMMARY_PROJECTION, select, null,
            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
在这个例子中,onCreateLoader 是唯一一个在实例化时将有关用户查询的信息传递给 Loader 的地方。然而,文档中说“启动新的或重新启动现有的 Loader”,这让我感到困惑。
1个回答

30

对于您的问题,简单的回答是肯定的,调用restartLoader()将会再次调用onCreateLoader()。

您可以同时启动多个加载器(比如你有两个SimpleCursorAdapters需要填充数据),例如:

getLoaderManager().initLoader(0, null, this);  //id = 0
getLoaderManager().initLoader(1, null, this);  //id = 1

接下来,Loader Manager将为每个id调用onCreateLoader方法(然后由Loader Manager异步构建返回的loader):

public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
    if (id == 0)
        //return a Loader<Cursor> for id 0
    else if (id == 1)
        //return a Loader<Cursor> for id 1
}

Loader Manager 将结果 loader 传递给 onLoadFinished:

public void onLoadFinished(Loader<Cursor> loader, Cursor cursor)
{
    if (loader.getId() == 0)
        //cursor was returned from onCreateLoader for id 0
        //perhaps do swapCursor(cursor) on an adapter using this loader
    else if (loader.getId() == 1)
        //cursor was returned from onCreateLoader for id 1
        //perhaps do swapCursor(cursor) on an adapter using this loader
}

当您随后调用restart loader时:

getLoaderManager().restartLoader(0, null, this);  //id = 0

...在调用onLoaderReset之前:

public void onLoaderReset(Loader<Cursor> loader)
{
    if (loader.getId() == 0)
        //perhaps do swapCursor(null) on an adapter using this loader
    else if (loader.getId() == 1)
        //perhaps do swapCursor(null) on an adapter using this loader
}

紧接着是一个新的 onCreateLoader 调用。因此,在这方面,onCreateLoader 既用于启动全新的加载器,也用于重置现有的加载器。


我很感兴趣,想知道你为什么这样认为?这就是加载器的全部意义,以异步方式与ContentProvider/数据源进行交互。长时间运行的任务(否则可能会阻塞UI)在onCreateLoader中运行 - LoaderManager在后台运行该部分。 - NigelK
是的,是的,AsyncTaskLoader的loadInBackground部分肯定在后台线程上运行,但我认为onCreateLoader在UI线程上:加载器的客户端通常应该从它们的进程的主线程上执行任何对Loader的调用(也就是Activity回调和其他操作发生的线程)。 -- 和实例化一个AsyncTask相似,这是我的推理。 - Maarten
1
你从客户端/主线程调用Loader的方法是.initLoader()。然后Loader在后台运行onCreateLoader方法。 - NigelK
1
使用以下代码:android.os.Process.getThreadPriority(android.os.Process.myTid());似乎onCreateLoader在UI线程上运行。我也在onCreateLoader中添加了一些UI更新,并且它可以正常工作,没有任何问题。 - ANemati
1
@ANemati 谢谢你;我再次查看后发现你是正确的。onCreateLoader确实在UI线程上运行 - 它返回的loader是由Loader Manager异步构建的。我已经编辑了我的答案以删除不准确之处。 - NigelK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接