2.2.2 Loaders

Loaders

       在Android3.0中介绍的,载入器(Loader)可以让从activity或者fragment中异步载入数据变得更简单。载入器有下面这些特点:

●       对任意activity和fragment都有效

●       提供异步下载数据功能

●       当内容(content)改变的时候,它们监控数据的来源并且提交新的结果

●       当配置改变导致重新创建(activity/fragment)的时候,它们自动重新连接到最后一个载入器的指针(the last loader’s cursor)。因此,它们不需要重新提交它们的数据

 

Loader API Summary

       有很多类和接口可能包含和使用载入器。它们总结在下面的表格中。

Class/Interface

Description

LoaderManager

这个是与Activity或Fragment管理一个或多个Loader实例有关的抽象类。它帮助app管理在activity或fragment生命周期中的长时间操作。最常见使用该类操作是联合CursorLoader使用的,app还可以自由定义这两个类来下载其他类型的数据。

在每个activity或fragment中,只有一个LoaderManager。但是每个LoaderManager中可以有多个loader.

LoaderManager.

LoaderCallbacks

客户端的回调接口可以与LoaderManager相互作用。例如,可以使用onCreateLoader()回调方法创建一个新的loader

Loader

提供异步下载数据的抽象类。对于载入器来说,这是最进本的类。你可以使用最常见的CursorLoader,你也可以实施自己自定义的子类。当载入器活动的时候,它应该监控数据来源并在content改变的时候发送新结果。

AsyncTaskLoader

抽象载入器,提供一个AsyncTask对象。

CursorLoader

AsyncTaskLoader的父类,查询ContentResolver同时返回一个Cursor。这个类用一种标准方式查询cursor来实现载入器协议,它创建一个AsyncTaskLoader在后台运行线程来查找cursor列表,并不在app的UI上显示出来。使用这个载入器从ContentProvider上异步下载数据是最佳方法。

 

Using Loaders in an Application

       这个部分说明如何在android app中使用载入器。典型的在app中使用载入器包括下面的:

●       一个activityfragment

●       一个LoaderManager实例

●       一个CursorLoader下载由ContentProvider支持的数据。你可以实现自定义的Loader子类或实现AsyncTaskLoader从其他资源处下载数据。

●       一个LoaderManager. LoaderCallbacks的实现。在这个实现中你创建一个新的载入器,同时也在其中管理已有载入器的引用。(manage your references to existing loaders)

●       展示载入器数据的一种方式,例如一个SimpleCursorAdapter

●       A data source, such as a ContentProvider,when using aCursorLoader.

 

Starting a Loader

       LoaderManager管理存在于activity或fragment中的一个或多个Loader实例。每个Activity/Fragment中只有一个LoaderManager

       典型初始化一个Loader是在activity的onCreate()方法中,或者在fragment的onActivityCreated()方法中。你应该如下初始化:

 

//Prepare the loader. Either re-connect with anexisting one, or start a new one

getLoaderManager ().initLoader (0, null, this);

 

initLoader()方法中需要如下参数:

●       一个独立的ID声明这个loader,在例子中ID是0

●       在构造函数中提供可选参数,在例子中是null

●       一个LoaderManager. LoaderCallbacks的实现,LoaderManager会调用这个实现报告loader事件。在例子中,该类实现了LoaderManager. LoaderCallbacks的接口,所以它传递了一个引用给自身,this

 

方法initLoader()确保一个loader会被初始化且是活动的。该方法有两个可能性:

●       如果loader通过已经存在的ID声明,则最后被创建的loader会被重复利用

●       如果loader通过没有存在的ID声明,initLoader()会触发LoaderManager. LoaderCallbacks中的onCreateLoader()方法。这个方法是你创建实例且返回一个新的loader的地方。更多信息可以参见onCreateLoader部分。

 

在两个情况下,LoaderManager. LoaderCallbacks都与loader有关系,并且在loader状态改变的时候都会被调用。如果在调用这个方法的时候,调用者处在其started状态,同时被请求的loader已经存在且已经生成了它的数据,则系统会直接调用方法onLoadFinished()(在initLoader()期间),所以你应该对此做好准备。可以参见文档SeeLoadFinished获取更多该回调方法的信息。

       注意initLoader()方法会返回一个创建好的Loader,你不需要特意去捕获它,LoaderManager会自动控制这个loader的生命周期。LoaderManager会在需要的时候开始或停止下载,同时保持loader的状态和与它相关的content。这就意味着,你极少需要与loader直接交互(通过LoaderThrottle例子可以查看怎样使用loader的方法调整其行为)当特殊情况发生的时候,通常使用LoaderManager. LoaderCallbacks中的方法干涉下载线程。更多信息请查看Using the LoaderManager Callbacks.

 

Restarting a Loader

       当你如下使用initLoader()的时候,它会使用一个带有已经声明ID的已经存在的loader,如果不存在这样一个loader,则这个方法会创建一个。但是有时候你会想抛弃掉旧的数据重新开始。

       使用restartLoader()可以丢弃旧的数据。例如,SearView.OnQueryTextListener的实现在用户查询改变的时候重启了loader。载入器需要重启,这样它才可以用修改过的搜索过滤器执行一个新的查询。

 

public boolean onQueryTextChanged (StringnewText) {

//called whenthe action bar search text has changed. Update the search //filter, and restartthe loader to do a new query with this filter.

mCurFilter =! TextUtils.isEmpty (newText)? newText: null;

getLoaderManager().restartLoader(0,null,this;

return true;

}

 

Using the LoaderManager Callbacks

       LoaderManager .LoaderCallbacks是一个可以让客户端与LoaderManager进行交互的回调接口。

       载入器,特别是CursorLoader,被期望能在停止后还能保存它们的数据。这样使得app可以通过activity或fragment的onStop()和onStart()来保持数据。所以当用户返回一个app时,他们不需要等到数据的重新载入。在知道什么时候创建一个新的loader时,你可以使用LoaderManager. LoaderCallbacks方法告诉app何时停止使用载入器的数据。

       LoaderManager.LoaderCallbacks包括下面这些方法:

●       onCreateLoader()——实例化并返回一个新的loader for the given ID

●       onLoadFinished()——当前一个已经被创建好的loader完成下载时调用

●       onLoaderReset()——当前一个已经被创建好的loader被重置调用,这样使得其数据不可用

这些方法的具体使用会在下面描述

 

OnCreateLoader

       当你尝试通过一个loader(例如通过initLoader()),它会检查loader是否被一个已经存在的ID指定。如果没有,会触发LoaderManager. LoaderCallbacks的方法onCreateLoader()。这是你创建新的loader的地方。典型的loader是CursorLoader,但是你也可以创建自定义的Loader子类。

       在下面例子中,onCreateLoader()回调方法创建了一个CursorLoader。你必须创建一个CursorLoader使用它的构造方法,该构造方法需要完整的设备信息以便从ContentProvider中查询。特别的,它需要:

●       uri——检索内容所需的URI

●       projection——A list of which columns to return. 传递null会返回所有的列,但这样效率很低

●       selection——Afilter declaring which rows to return. 类似SQL的WHERE语句的格式化(排除“WHERE“这个词)。传递null则返回所给URI的所有的行。

●       selectionArgs——你可能在selection中使用多个“?”,可以使用这个参数依次代替selection中出现的“?”。值一定是String。

●       sortOrder——类似SQL中ORDERBY语句的格式化(排除“ORDER BY”这个词)。传递null则使用默认的分类指令,有时候默认分类指令就是不分类。

例如:

//If non-null, this is the current filter the usehas provided.

String mCurFilter;

… …

public Loader<Cursor> onCreateLoader (intid, Bundle args) {

//This iscalled when a new Loader needs to be created. This sample only has //oneLoader, so we don’t care about the ID. First, pick the base URI to use//depending on whether we are currently filtering.

Uri baseUri;

if (mCurFilter!=null){

       baseUri =Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,

Uri.encode(mCurFilter));

} else {

       baseUri=Contacts.CONTENT_URI;

}

//Now createand return a CursorLoader that will take care of creating a //Cursor for thedata being displayed.

Stringselect= “((”+Contacts.DISPLAY_NAME +”NOTNULL) AND (” +

Contacts.HAS_PHONE_NUMBER+”=1)AND (”+Contacts.DISPLAY_NAME+ “!=’’ ))”;

return newCursorLoader(getActivity(), baseUri,

                   CONTACTS_SUMMARY_PROJECTION,select, null,

Contacts.DISPLAY_NAME+”COLLATE LOCALIZED ASC”);

}

 

OnLoadFinished

       当前一个已经创建的loader完成它的下载任务时会调用这个方法。这个方法在释放为loader服务的最后的资源前一定会被调用。此时你应该移除所有旧数据的使用(因为它将很快被释放),但是你不需要自己动手释放数据,因为loader自己会处理好这一切。

       当loader知道app将不再使用某些数据时,它会释放它们。例如,如果数据是来自CursorLoader的一个Cursor,你不需要为它调用close()。如果这个cursor将被存放在CursorAdapter,你应该使用swapCursor()方法使旧的cursor不会关闭。例如:

//This is the Adapter being used to display thelist’s data

SimpleCursorAdapter mAdapter;

… …

public void onLoadFinished (Loader <Cursor>loader, Cursor data) {

//swap thenew cursor in. (The framework will take care of closing the old //cursor oncewe return)

mAdapter.swapCursor(data);

}

 

OnLoaderReset

       当前一个被创建的loader被重置的时候会调用这个方法,此时loader的数据是不可用的。这个回调方法使可以让你知道数据会在什么时候将被释放,然后你可以删除你对它的引用。

// This is the Adapter being used to display thelist’ data.

SimpleCursorAdapter mAdapter;

… …

public void onLoaderReset (Loader<Cursor>loader) {

//This iscalled when the last cursor provided to onLoadFinished () above is //about tobe closed. We need to make sure we are no longer using it.

mAdapter.swapCursor(null);

}

 

Example

       下面是一个fragment显示listview的完整的实现。其中,ListView中包含的结果是从联系人的content provider中查询到的。这里使用CursorLoader管理从provider的查询。

       如下所示,一个app想要获得用户的联系人,它的manifest必须包含READ_CONTACT的许可。

 

public static class CursorLoaderListFragmentextends ListFragment

       ImplementsonQueryTextListener, LoaderManager.LoaderCallbacks<Cursor>{

       //This is the Adapter being used to display the list’s data

       SimpleCursorAdaptermAdapter;

       //Ifnon-null, this is the current filter the user has provided.

       StringmCurFilter;

       @Override

       publicvoid onActivityCreated(Bundle savedInstanceState) {

       super.onActivityCreated(savedInstanceState);

// Give sometext to display if there is no data. In a real application this

// would comefrom a resource.

setEmptyText(“Nophone numbers”);

// We have amenu item to show in action bar

setHasOptionsMenu(true);

//Create anempty adapter we will use to display the loader data

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});

              setListAdapter(mAdapter);

//Prepare theloader. Either re-connect with an existing one, or start a //new one

              getLoaderManager().initLoader(0,null, this);

}

 

@Override

public voidonCreateOptionsMenu(Menu menu, MenuInflater inflater){

       //Place an action bar item for searching

       MenuItem item=menu. add(“Search”);

       item.setIcon(android.R,drawable.ic_menu_search);

       item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);

       SearchView sv=newSearchView(getActivity());

       sv.setOnQueryTextListener(this);

       item.setActionView(sv);

}

 

@Override

publicboolean onQueryTextChange(String newText) {

//Called whenthe action bar search text has changed. Update the //search filter, and restartthe loader to do a new query with this filter

mCurFilter =!TextUtils.isEmpty(newText)?newText:null;

getLoaderManager().restartLoader(0,null, this);

return true;

}

 

@Override

publicboolean onQueryTextSubmit (String query){

       //Don’t care about this

       return true;

}

 

@Override

public voidonListItemClick (ListView l, View v, int position, long id) {

       //Insert desired behavior here

       Log.i(“FragmentComplexList”, “Itemclicked:  ”+id);

}

 

//These arethe Contacts rows that we will retrieve

static finalString[] CONTACT_SUMMARY_PROJECTION= new String[] {

       Contacts._ID,

       Contacts.DISPLAY_NAME,

       Contacts.CONTACT_STATUS,

       Contacts.CONTACT_PRESENCE,

       Contacts.PHOTO_ID,

       Contacts.LOOKUP_KEY,

};

 

publicLoader<Cursor> onCreateLoader(int id, Bundle args){

//This iscalled when a new Loader needs to be created. This sample only //has oneLoader, so we don’t care about the ID. First, pick the base URI //to usedepending on whether we are currently filtering.

Uri baseUri;

if(mCurlFilter != null ){

       baseUri =Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI),

              Uri.encode(mCurFilter));

} else {

       baseUri=Contacts.CONTENT_URI;

}

//Now createand return a CursorLoader that will take care of creating a Cursor for the databeing displayed.

Stringselect= “((”+Contacts.DISPLAY_NAME +”NOTNULL) AND (” +

Contacts.HAS_PHONE_NUMBER+”=1)AND (”+Contacts.DISPLAY_NAME+ “!=’’ ))”;

return newCursorLoader(getActivity(), baseUri,

                   CONTACTS_SUMMARY_PROJECTION,select, null,

Contacts.DISPLAY_NAME+”COLLATE LOCALIZED ASC”);

}

 

public voidonLoadFinished() {

//swap thenew cursor in. (The framework will take care of closing the old //cursor oncewe return)

mAdapter.swapCursor(data);

}

 

public onLoaderReset(Loader<Cursor>loader){

//This iscalled when the last Cursor provided to onLoadFinished() above is //about to beclosed. We need to make sure we are no longer using it.

mAdapter.swapCursor(null);

}

}


广播!!尝试过上述Contact在较新版本的API中不可用!!上述例子不可用!!!

 

More Examples

       下面有一些的不同例子在ApiDemos中显示如何使用下面的例子:

LoaderCursor/LoaderThrottle: http://developer.android.com/samples/index.html

其中LoaderThrottle显示如何在数据改变的时候减少从content provider中的查询。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值