AsyncTaskLoader1

来源:互联网 发布:htpc直播软件 编辑:程序博客网 时间:2024/06/06 16:50

在Android3.0中引入了装载器的功能,这使它很容易在Activity或Fragment中使用异步的方式加载数据。装载器的特点如下:

1.  装载器对于每个Activity和Fagment都是有效的;

2.  装载器提供异步数据加载的能力;

3.  装载器监视数据资源并且当内容改变时发送新的结果;

4.  在配置改变后重建的时候,装载器自动的重连最后的装载器游标,因此,不需要重新查询数据。

有多种类和接口可以用于应用程序的装载器。在下表中进行概要介绍:

类/接口

描述

LoaderManager

跟Activity和Fragment关联的用于管理一个或多个Loader实例的一个抽象类。这有益于管理跟Activity或Fragment生命周期相结合长时操作;最常用的是CursorLoader装载器,但是,为了加载其他类型的数据,你可以为应用程序编写它们自己的应用程序。

每个Activity或Fragment只有一个LoaderManager,但是一个LoaderManager能够有多个装载器。

LoaderManager.LoaderCallbacks

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

Loader

一个执行异步数据加载的抽象类。这是装载器的基类,一般情况下使用CursorLoader,但是也可以实现自己的子类。装载器在激活状态时应该监视数据源,并且在内容发生改变时发送新的结果。

AsyncTaskLoader

提供一个AsyncTask对象来完成工作的抽象装载器。

CursorLoader

AsyncTaskLoader的子类,它查询ContentResolver对象,并且返回一个Cursor对象。这个类用标准的游标查询方法实现了Loader协议,基于AsyncTaskLoader对象用后台线程来执行游标查询,以便应用程序的界面不会被死锁。使用这个装载器是从ContentProvider对象中异步加载数据的最好方法,而不是通过Fragment或Activity的APIs来执行被管理的查询。

上表中的类和接口是用于实现应用程序装载器的必要组件。对于你创建的每一个装载器并不需要以上全部内容,但是为了初始化一个装载器,你总是需要一个LoaderManager对象的引用,并且实现一个像CursorLoader类那样实现Loader类。


在Android应用程序中如何使用装载器,使用装载器的应用程序通常包括以下内容:

1.  一个Activity或Fragment;

2.  一个LoaderManager实例;

3.  一个用于加载通过ContentProvider保存的数据的CursorLoader对象,也可以是你自己实现的用于加载其他类型数据的Loader或AsyncTaskLoader类的子类。

4.  一个LoaderManager.LoaderCallbacks相关的实现,这是你创建新的装载器和管理既存装载器引用的地方;

5.  一个显示装载器数据的方法,如一个SimpleCursorAdapter;

6.  一个数据源,如CursorLoader使用的ContentProvider对象。


LoaderManager管理Activity或Fragment内部的一个或多个装载器实例,每个Activity或Fragment只有一个LoaderManager对象。

通常在Activity的onCreate()方法或Fragment的onActivityCreated()内初始化一个装载器,如:

// Prepare the loader.  Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);

 

initLoader方法带有以下参数:

1.  标识这个装载器的唯一ID,这个例子中ID是“0”;

2.  可选参数,它是给装载器提供的在构造时所需的参数,这个例子中是null;

3.  一个LoaderManager.LoaderCallbacks的实现,LoaderManager会调用这个实现来报告装载器事件。在这个例子中,这个类实现了LoaderManager.LoaderCallbacks接口,因此它把它自己(this)的引用传给了initLoader()方法。

initLoader()调用确保了装载器被初始化和激活。它有两种可能的结果:

1.  如果通过ID指定的装载器已经存在,那么最后被创建的装载器被重用。

2.  如果通过ID指定的装载器不存在,initLoader()方法会触发LoaderManager.LoaderCallbacks的onCreateLoader()方法。这是你实现初始化代码的地方,并且返回一个新的装载器。关于更多的讨论,请看onCreateLoader()相关章节。

在这两种情况下,都给LoaderManager.LoaderCallbacks实现了与装载器的关联,并且在装载器状态改变的时候,相关的回调方法会被调用。如果在调用的时点调用者是在被启动的状态,并且被请求的装载器已经存在且已经生成了相关的数据,那么系统会立即调用onLoadFinished()(在initLoader()方执行期间),因此你必须为这种情况做好准备。对于这个回调的更多讨论,请看onloadFinished()回调。

 

注意:initLoader()方法返回一个被创建的Loader对象,但是你不需要获取关于这个对象的引用,因为LoaderManager对象自动的管理着装载器的生存。LoaderManager对象在需要的时候启动和终止装载器,并且保持着装载器的状态和相关匹配的内容。这就意味着,你很少直接跟装载器交互(虽然可以使用装载器方法微调装载器的行为,请参阅例子LoaderThrottle),大多数情况都是使用LoaderManagerLoaderCallbacks的回调方法在加载过程中发生特定事件时来进行干预。关于更多的讨论,可参阅使用LoaderManager回调方法(Using the LoaderManager Callbacks)


在使用initLoader()方法时,如果指定ID的装载器存在,就使用这个既存的装载器,否则会创建一个新的。但是有些时候你会想要废弃旧的数据并重启装载器。

你可以使用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;
}


LoaderManager.LoaderCallbacks是一个让客户与LoadManager进行交互的回调接口。
装载器,尤其是CursorLoader装载器,被期望用来保存被终止后的数据,这样就允许应用程序在Activity之间或Fragment的onStop()和onStart()方法之间进行切换时保存数据,以便在用户返回应用程序时,它们不需要因数据重载而等待。使用LoaderManager.LoaderCallbacks()的回调方法就知道在什么时候要创建一个新的装载器,并且告诉应用程序什么时候是终止使用装载器数据的时间。

LoaderManager.LoaderCallbacks接口包含以下方法:

1. onCreateLoader()---用给定的ID实例化并返回一个新的Loader对象;

2. onLoaderFinished()---当之前创建的装载器已经完成它的装载时,调用这个方法;

3. onLoaderReset()---当之前创建的装载器被重置时,调用这个方法,这样会使这个装载器的数据变的无效。

下面分别对这些方法进行详细说明。

onCreateLoader

当你想要访问一个装载器是时(例如,通过initLoader()方法),它会检查指定ID的装载器是否存在,如果不存在,它会触发LoaderManager.LoaderCallbacks的onCreateLoader()回调方法。这是创建一个新的装载器的地方,典型的是创建一个CursorLoader装载器,但是你能够实现你自己的Loader类的子类。

例如:

 // If non-null, this is the current filter the user has provided.
String mCurFilter;
...
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    // This is called when a new Loader needs to be created.  This
    // sample only has one Loader, 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 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()回调方法创建了一个CursorLoader装载器,你必须使用构造器方法来创建这个CursorLoader对象,它需要一整套执行对ContentProvider的查询所需要信息,具体需要如下:

1.  uri---要获取的内容的位置(URI);

2.  projection---一个要返回的列的列表,如果传递null,将返回所有的列,这样是低效的。

3.  selection---一个要返回的行的过滤器声明,它使用SQL WHERE子句的格式(不包括WHERE关键字本身)。传递null时将返回给定URI的所有行数据。

4.  selectionArgs---你可以在selection中包含“?”字符,这些字符将会被selectionArgs中的值顺序替换。这些值将作为字符串被绑定。

5.  sortOrder---使用SQL ORDER BY子句的格式(不包括ORDER BY自己)指定行记录是如何排序的,如果传递null将会使用默认的排序,也可以是无序的。

onLoadFinished

当之前创建的装载器已经完成了它的加载时这个方法会被调用。在提供给这个装载器的最后的数据释放之前会保证调用这个方法。在这个时点你应该删除所有的对旧数据使用(因为数据即将被释放),但是你不应该自己做数据的释放,因为装载器本身会做这件事情。

装载器一旦知道应用程序不在使用它,就会释放数据。例如,如果数据是来自CursorLoader对象的一个游标,你不应该自己来调用close()方法,如果游标被放到了CursorAdapter中,你应该使用swapCursor()方法,以便旧的Cursor对象不被关闭。如:

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
...

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    mAdapter.swapCursor(data);
}

 

onLoaderReset

当之前被创建的装载器被重置的时候,这个方法会被调用。这样就是它的数据变的无效。这个回调能够让你找到数据被释放的时机,以便你能够在数据被释放之前删除对它的引用。

用带有null参数的swapCursor()方法实现这样的调用,如:

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
...

public void onLoaderReset(Loader<Cursor> loader) {
    // This is called when the last Cursor provided to onLoadFinished()
    // above is about to be closed.  We need to make sure we are no
    // longer using it.
    mAdapter.swapCursor(null);
}



实例代码:

查看文本打印?
  1. 这个例子是在Fragment中显示一个ListView的完整实现,ListView中包含针对通讯录内容提供器的查询结果。它使用一个CursorLoader来管理基于内容提供器的查询。  
  2.   
  3. 因为这个应用程序访问了用户的通讯录,因此它的清单文件中必须包含READ_CONTACTS许可。  
  4.   
  5. public static class CursorLoaderListFragment extends ListFragment  
  6.         implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {  
  7.   
  8.     // This is the Adapter being used to display the list's data.  
  9.     SimpleCursorAdapter mAdapter;  
  10.   
  11.     // If non-null, this is the current filter the user has provided.  
  12.     String mCurFilter;  
  13.   
  14.     @Override public void onActivityCreated(Bundle savedInstanceState) {  
  15.         super.onActivityCreated(savedInstanceState);  
  16.   
  17.         // Give some text to display if there is no data.  In a real  
  18.         // application this would come from a resource.  
  19.         setEmptyText("No phone numbers");  
  20.   
  21.         // We have a menu item to show in action bar.  
  22.         setHasOptionsMenu(true);  
  23.   
  24.         // Create an empty adapter we will use to display the loaded data.  
  25.         mAdapter = new SimpleCursorAdapter(getActivity(),  
  26.                 android.R.layout.simple_list_item_2, null,  
  27.                 new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },  
  28.                 new int[] { android.R.id.text1, android.R.id.text2 }, 0);  
  29.         setListAdapter(mAdapter);  
  30.   
  31.         // Prepare the loader.  Either re-connect with an existing one,  
  32.         // or start a new one.  
  33.         getLoaderManager().initLoader(0, null, this);  
  34.     }  
  35.   
  36.     @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {  
  37.         // Place an action bar item for searching.  
  38.         MenuItem item = menu.add("Search");  
  39.         item.setIcon(android.R.drawable.ic_menu_search);  
  40.         item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);  
  41.         SearchView sv = new SearchView(getActivity());  
  42.         sv.setOnQueryTextListener(this);  
  43.         item.setActionView(sv);  
  44.     }  
  45.   
  46.     public boolean onQueryTextChange(String newText) {  
  47.         // Called when the action bar search text has changed.  Update  
  48.         // the search filter, and restart the loader to do a new query  
  49.         // with this filter.  
  50.         mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;  
  51.         getLoaderManager().restartLoader(0, null, this);  
  52.         return true;  
  53.     }  
  54.   
  55.     @Override public boolean onQueryTextSubmit(String query) {  
  56.         // Don't care about this.  
  57.         return true;  
  58.     }  
  59.   
  60.     @Override public void onListItemClick(ListView l, View v, int position, long id) {  
  61.         // Insert desired behavior here.  
  62.         Log.i("FragmentComplexList", "Item clicked: " + id);  
  63.     }  
  64.   
  65.     // These are the Contacts rows that we will retrieve.  
  66.     static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {  
  67.         Contacts._ID,  
  68.         Contacts.DISPLAY_NAME,  
  69.         Contacts.CONTACT_STATUS,  
  70.         Contacts.CONTACT_PRESENCE,  
  71.         Contacts.PHOTO_ID,  
  72.         Contacts.LOOKUP_KEY,  
  73.     };  
  74.     public Loader<Cursor> onCreateLoader(int id, Bundle args) {  
  75.         // This is called when a new Loader needs to be created.  This  
  76.         // sample only has one Loader, so we don't care about the ID.  
  77.         // First, pick the base URI to use depending on whether we are  
  78.         // currently filtering.  
  79.         Uri baseUri;  
  80.         if (mCurFilter != null) {  
  81.             baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,  
  82.                     Uri.encode(mCurFilter));  
  83.         } else {  
  84.             baseUri = Contacts.CONTENT_URI;  
  85.         }  
  86.   
  87.         // Now create and return a CursorLoader that will take care of  
  88.         // creating a Cursor for the data being displayed.  
  89.         String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("  
  90.                 + Contacts.HAS_PHONE_NUMBER + "=1) AND ("  
  91.                 + Contacts.DISPLAY_NAME + " != '' ))";  
  92.         return new CursorLoader(getActivity(), baseUri,  
  93.                 CONTACTS_SUMMARY_PROJECTION, select, null,  
  94.                 Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");  
  95.     }  
  96.   
  97.     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {  
  98.         // Swap the new cursor in.  (The framework will take care of closing the  
  99.         // old cursor once we return.)  
  100.         mAdapter.swapCursor(data);  
  101.     }  
  102.   
  103.     public void onLoaderReset(Loader<Cursor> loader) {  
  104.         // This is called when the last Cursor provided to onLoadFinished()  
  105.         // above is about to be closed.  We need to make sure we are no  
  106.         // longer using it.  
  107.         mAdapter.swapCursor(null);  
  108.     }  

0 0