活动——装载程序

1. 装载器API概要


2. 在应用中使用装载器

    2.1 启动装载器

    2.2 重启装载器

    2.3 使用LoaderManager回调


3. 实例

    3.1 更多实例



在android 3.0中引入了装载器,它使得在活动或碎片内异步加载数据变得容易。装载程序有如下特性:

  • 它们对每个活动和碎片都是可用的。
  • 它们提供异步的数据加载。
  • 它们监控着活动和碎片的数据源,并在数据改变时递送新结果。
  • 配置改变后,当活动和碎片被重建时,它们自动重新连接到最后装载器的游标。因此,活动和碎片不必再次查询它们的数据。

1. 装载器API概要



在应用里使用装载器的过程中可能有多个类和接口被调用。它们被总结在下面的这个表中:

 类/接口

 描述

 



LoaderManager

ActivityFragment有关以管理一个或多个Loader实例的抽象基类。它帮助应用管理连同ActivityFragment生命周期长时间运行的操作;和CursorLoader一起是最常见的使用,然而应用可以自由地写它们自己的装载器来加载别的数据类型。

每个活动或碎片仅有一个LoaderManager。但是一个LoaderManager可以拥有多个装载器。

LoaderManager.LoaderCallbacks

LoaderManager交互的客户端回调接口。例如,可以使用onCreateLoader()回调方法创建一个新的装载器。

 Loader

执行异步数据加载的抽象类。它是装载器的基类。通常,你将使用CursorLoader,但是你也可以实现你自己的子类。当装载器活动时,它们应该监控它们的数据源并且当内容改变时递送新的结果

 AsyncTaskLoader

提供一个AsyncTask来执行工作的抽象装载器。

 CursorLoader

AsyncTaskLoader的子类,它查询ContentResolver并返回一个Cursor。此类实现了Loader协议,并以标准的方式查询游标,创建AsyncTaskLoader在后台线程中执行游标查询,这样做是为了不阻塞应用的UI。使用此装载器是从ContentProvider异步地加载数据的最好方式,而不是通过碎片或活动的API执行托管查询。


上表中的类和接口是你在应用中用来实现装载器的基本组件。你不需要所有这些为你创建的每个装载机,但是你通常需要一个LoaderManager引用以便初始化装载器以及Loader类的实现,譬如CursorLoader。下面的章节展示在应用中如何使用这些类和接口。


2. 在应用中实例装载器



本章节描述在Android应用中如何使用装载器。使用装载器的应用基本上包括以下部分:



2.1 启动装载器


ActivityFragment里,LoaderManager管理一个或多个Loader实例。 每个活动或碎片仅有一个LoaderManager


通常,在活动的onCreate()方法内初始化Loader或者是在碎片的onActivityCreated()方法内。像下面这样做:


// 准备装载器。重连一个现有的装载器,或者启动一个新的。
getLoaderManager().initLoader(0, null, this);

initLoader()方法携带以下参数:

initLoader()调用确保了装载器被初始化且是激活的。 此调用可能有两种结果:call ensures that a loader is initialized and active. It has two possible out comes:

在任何一种情况下,给定的LoaderManager.LoaderCallbacks实现与装载器是相关联的,并且当装载器状态改变时被调用。如果在此调用点装载器处于它的启动状态,且请求的装载器已经存在,那么系统则立即调用onLoadFinished()(在initLoader()期间),所以你必须为这种情的出现做好准备。更多次回调的讨论,请查阅onLoadFinished


注意, initLoader()方法返回被创建的Loader,但是你不需要获得它的引用。LoaderManager自动管理着装载器的生命。LoaderManager启动并在必要时停止加载,而且维持着装载器的状态和它有关联的内容。这意味着,很少与装载器直接交互(不过对于使用装载器方法来微调装载器行为的例子,请查阅LoaderThrottle实例)。当特定事件发生时,通常你必须使用LoaderManager.LoaderCallbacks方法来介入加载过程。对于更多该主题的讨论,请查阅使用LoaderManager回调章节。


2.2 重启装载器


如上所示,当使用initLoader()时,它使用一个具有指定ID的现有装载器,如果存在一个的话。如果没有,则创建一个。不过有时你打算丢弃旧的数据然后重新开始。


为了丢弃旧数据,你需要使用restartLoader()。例如,下面这个SearchView.OnQueryTextListener实现在用户的查询数据发生变化时重启了装载器:

public boolean onQueryTextChanged(String newText) {
    // 在当动作栏搜索文本发生变化时被调用。
    // 更新搜索过滤,然后重启装载器用这个过滤器来执行一条新的查询。
    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    getLoaderManager().restartLoader(0, null, this);
    return true;
}



2.3 使用LoaderManager回调


LoaderManager.LoaderCallbacks是一个允许客户端与LoaderManager交互的回调接口。

在特殊的CursorLoader内,装载器被希望用来在客户端被停止后获取他们的数据。这允许应用跨越活动或碎片的onStop()onStart()方法来保持它们的数据,所以当用户返还到应用时,他们不必等待数据重新加载。当知道何时创建一个新的装载器时,使用LoaderManager.LoaderCallbacks接口方法,然后告诉应用何时停止使用装载器的数据。

LoaderManager.LoaderCallbacks接口包括这些方法:

  • onLoaderReset()  — 当先前的装载器正在重置时调用,因此使得它的数据不可用。

这些方法在接下来的章节里有更为详细地描述。


onCreateLoader

当试图访问装载器时(例如,通过initLoader()),它来检查通过制定ID的装载器是否存在。如果不存在,它触发LoaderManager.LoaderCallbacksonCreateLoader()方法。这就是创建新的装载器的地方。通常,这可能是CursorLoader,但是你可以实现你自己的Loader子类。


在此例中Loader回调方法创建一个CursorLoader。创建它必须使用CursorLoader的构造函数,它要求一套完整的需要来执行ContentProvider查询的信息。它尤其需要:

  • uri — 检索内容的URI。
  • projection — 返回哪些列的清单。传递null将返回所以列,但效率不高。
  • selection — 一个声明返回哪些行的过滤器,其格式如同SQL WHERE子句(不包括WHERE本身)。传递null将返回给定URI的全部行。
  • selectionArgs — 在selection中可以包含 ?符号,这将被来自selectionArgs的值所替代,它们按顺序出现在selection中。这些值限制为String。
  • sortOrder — 如何读行排序,格式如同SQL ORDER BY字句(不包括ORDER BY本身)。传递null将使用默认的存储顺序,它可能是无序的。

例如:

 // 如果非空,它是用户提供的当前过滤器。
String mCurFilter;
...
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    // 在需要创建一个新的装载器时调用它。
    // 该例子中只有一个装载器,所以不用在乎ID。
    // 首先,依当前我们是否正在过滤来选择基准URI来用。
    Uri baseUri;
    if (mCurFilter != null) {
        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(mCurFilter));
    } else {
        baseUri = Contacts.CONTENT_URI;
    }

    // 现在创建并返回一个CursorLoader,它将负责创建正被显示的数据游标。
    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");
}



onLoaderReset


当先前创建的装载器正在被重置时调用这个方法,因此致使它的数据不可用。通过此函数可以知道数据何时将要被释放,这样你就可以移除你对它的引用。


下面的实现以一个null值调用swapCursor()

// 这是用来显示列表数据的适配器。
SimpleCursorAdapter mAdapter;
...

public void onLoaderReset(Loader<Cursor> loader) {
    //当最后提供给onLoadFinished()的Cursor将要被关闭时被调用。我们需要确保不再使用它。
    mAdapter.swapCursor(null);
}


3. 实例



作为实例,下面是一个Fragment的完整实现,它显示了一个ListView,其包含有对联系人内容提供者进行查询的结果。实现使用CursorLoader来管理在提供者上的查询。


对于一个访问用户联系人的应用,如此例所示,它的清单文件中必须包括READ_CONTACTS权限。


public static class CursorLoaderListFragment extends ListFragment
        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {

    // 用来显示列表数据的适配器。
    SimpleCursorAdapter mAdapter;

    // 如果非空,这是用户提供的当前过滤器。
    String mCurFilter;

    @Override public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // 如果没有数据,提供一些文本来显示。
        // 在真正的应用中这可能出自于资源。
        setEmptyText("No phone numbers");

        //在动作栏显示一个菜单项
        setHasOptionsMenu(true);

        //创建一个空的适配器,我们将使用它来显示被加载的数据。
        mAdapter = new SimpleCursorAdapter(getActivity(),
                android.R.layout.simple_list_item_2, null,
                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
        setListAdapter(mAdapter);

        //准备装载器。要么与一个现有的装载器重新连接,要么启动一个新的装载器。
        getLoaderManager().initLoader(0, null, this);
    }

    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        //放置一个动作栏项用来搜索。
        MenuItem item = menu.add("Search");
        item.setIcon(android.R.drawable.ic_menu_search);
        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        SearchView sv = new SearchView(getActivity());
        sv.setOnQueryTextListener(this);
        item.setActionView(sv);
    }

    public boolean onQueryTextChange(String newText) {
        //当动作栏搜索文本改变时调用。 
        // 更新搜索过滤器,并重启装载器用这个过滤器来执行新的查询。
        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
        getLoaderManager().restartLoader(0, null, this);
        return true;
    }

    @Override public boolean onQueryTextSubmit(String query) {
        //不要在乎它。
        return true;
    }

    @Override public void onListItemClick(ListView l, View v, int position, long id) {
        // 这里插入期望的行为。
        Log.i("FragmentComplexList", "Item clicked: " + id);
    }

    //这些是将要取回的联系人数据行。
    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
        Contacts._ID,
        Contacts.DISPLAY_NAME,
        Contacts.CONTACT_STATUS,
        Contacts.CONTACT_PRESENCE,
        Contacts.PHOTO_ID,
        Contacts.LOOKUP_KEY,
    };
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // 当需要创建一个新的装载器时调用它。本例中仅有一个装载器,所以不必在乎ID。
        // 首先,依当前是否正在过滤,选择基准URI来用。
        Uri baseUri;
        if (mCurFilter != null) {
            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                    Uri.encode(mCurFilter));
        } else {
            baseUri = Contacts.CONTENT_URI;
        }

        // 现在创建并返回一个CursorLoader,它将负责创建正被显示的数据的游标.
        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");
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // 交换新的游标。 (一旦返回,框架将负责关闭老的游标)。
        mAdapter.swapCursor(data);
    }

    public void onLoaderReset(Loader<Cursor> loader) {
        // 当提供给onLoadFinished()的最后的游标将要关闭时调用。
        // 需要确保我们不在使用它。
        mAdapter.swapCursor(null);
    }
}


3.1 更多实例


在ApiDemos中有很多不同的例子,它们说明了如何使用装载器:

  • LoaderCursor — 如上所示代码段的完整版。
  • LoaderThrottle — 一个如何使用节流来减少内容提供者执行查询的数目,当它的数据发生变化时。

更多下载好安装SDK实例的信息,请参阅 Getting the Samples




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值