在 Android 中使用 Cursor Loader






4.76/5 (10投票s)
本文档有助于理解在 Android 中使用 Cursor Loader。
介绍
Loader 的概念是在 Android 3.0 (API 级别 11) 中引入的。Loader 的特性可以列举如下:
- Loader 主要用于在非 UI 线程上为 Activity 或 Fragment 提供数据的异步加载。虽然应用程序应该从主线程调用 Loader,但 Loader (或 Loader 的子类) 在单独的线程中执行其工作,并将结果传递回主线程。
- 代码实现不应直接派生自 android.content.Loader类,而应专门派生自android.content.CursorLoader类。
- 在 Activity 或 Fragment 中加载数据的不同阶段会调用 Loader 的回调。简而言之,Activity 或 Fragment 需要实现 Listener 才能使用 Loader。
- Loader 内部使用 AsyncTask来执行数据加载。与 AsyncTask 相比,Loader 没有性能优势,前提是 AsyncTask 被设计和开发得当。
- Loader,更具体地说,CursorLoader在后台线程中查询Content Resolver,这样应用程序的用户界面就不会被阻塞,并将加载的 Cursor 返回给 Activity 或 Fragment。
- CursorLoader 实现查询游标的 Loader 协议。
- CursorLoader处理游标的生命周期。在使用 CursorLoader 时,开发者不应调用游标的 close() 方法。
- Loader 会持久化获取的数据,以避免对简单的 Activity 刷新事件(如方向更改、键盘打开等)进行重复的获取操作。
- Loader 会监视其数据源,并在内容更改时传递新结果。它会在配置更改后重新创建时自动重新连接到最后一个 Loader 的游标,从而无需重新查询其数据。换句话说,CursorLoader会自动更新,因此无需重新查询游标。
- Loader,特别是 CursorLoader,预计会在停止后保留其数据。这允许应用程序在 Activity 或 Fragment 的onStop()和onStart()方法之间保留其数据,这样当用户返回到应用程序时,他们不必等待数据重新加载。
- Loader 是兼容库的一部分。因此,开发者可以在 Android HoneyComb 之前的应用程序中使用它。
- 从 Android 3.0 开始,开发者应该使用 CursorLoader而不是Activity.managedQuery或Activity.startManagingCursor。
- 每个 Activity 或 Fragment 只有一个 LoaderManager。LoaderManager会自动管理 Activity 或 Fragment 内的一个或多个 Loader 实例的生命周期。
使用 Loader 的应用程序通常包含以下内容:
- 一个 Activity 或 Fragment。
- 一个 LoaderManager实例。
- 一个 CursorLoader:用于加载由ContentProvider支持的数据。或者,开发者可以自由实现自己的 Loader 或AsyncTaskLoader子类来加载其他类型的数据。
- LoaderManager.LoaderCallbacks的实现。 这是一个回调接口,允许客户端与- LoaderManager进行交互。
- 一种显示 Loader 数据的方式,例如 SimpleCursorAdapter。
- 数据源,例如 ContentProvider(在使用CursorLoader时)。
在 Android < 3.0 中使用 Cursor Loader
- 开发者需要使用兼容库:https://developer.android.com.cn/tools/extras/support-library.html
- 按照以下说明进行设置: https://developer.android.com.cn/sdk/compatibility-library.html#SettingUp
- Activity 必须扩展 FragmentActivity。您可能需要导入以下类:
import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager;
如果您使用 SimpleCursorAdapter,请确保替换此行
import android.widget.SimpleCursorAdapter;  
用这个:
import android.support.v4.widget.SimpleCursorAdapter; 
编写代码的步骤
public class MyActivity implements LoaderManager.LoaderCallbacks<Cursor>
loadermanager.initLoader(1, null, this);
- Activity 需要实现 LoaderManager.LoaderCallbacks
- 初始化 Loader
- 实现 Loader 回调方法。- onCreateLoader:为给定的 ID 实例化并返回一个新的 Loader。这是创建游标的地方。
- onLoadFinished:在之前的 Loader 完成加载后调用。在这里,您可以开始使用游标。
- onLoaderReset:在之前的 Loader 被重置,使其数据不可用时调用。它被重置是为了创建一个新的游标来查询不同的数据。这在上面- onLoadFinished()提供的最后一个游标即将关闭时调用。我们需要确保我们不再使用它。
 
如何使用 Loader 的完整代码如下:
package tpg.main;
 
import tpg.database.DatabaseAccessUtility;
import tpg.database.DatabaseHandler;
import android.app.ListActivity;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.widget.SimpleCursorAdapter;
 
public class MainActivity extends ListActivity implements LoaderCallbacks<Cursor>  {
 
	
    SimpleCursorAdapter mAdapter; 		
    LoaderManager loadermanager;		
    CursorLoader cursorLoader;
    private static String TAG="CursorLoader";
    
    protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	
        loadermanager=getLoaderManager();
	
	String[] uiBindFrom = {  DatabaseHandler.UserTable.name};		
	int[] uiBindTo = {android.R.id.text1};
	
        /*Empty adapter that is used to display the loaded data*/
	mAdapter = new SimpleCursorAdapter(this,android.R.layout.simple_list_item_1, null, uiBindFrom, uiBindTo,0);  
        setListAdapter(mAdapter);
       
/**
* This initializes the loader and makes it active. If the loader 
* specified by the ID already exists, the last created loader is reused.
* If the loader specified by the ID does not exist, initLoader() triggers 
* the LoaderManager.LoaderCallbacks method onCreateLoader(). 
* This is where you implement the code to instantiate and return a new loader. 
* Use restartLoader() instead of this, to discard the old data and restart the Loader.
* Hence, here the given LoaderManager.LoaderCallbacks implementation are associated with the loader. 
*/
       loadermanager.initLoader(1, null, this);
   }
	
/**
* This creates and return a new Loader (CursorLoader or custom Loader) for the given ID. 
* This method returns the Loader that is created, but you don't need to capture a reference to it. 
*/
   public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
		
	String[] projection = { DatabaseHandler.UserTable.id, DatabaseHandler.UserTable.name };
		
/**
* This requires the URI of the Content Provider
* projection is the list of columns of the database to return. Null will return all the columns
* selection is the filter which declares which rows to return. Null will return all the rows for the given URI.
* selectionArgs:  You may include ?s in the selection, which will be replaced
* by the values from selectionArgs, in the order that they appear in the selection. 
* The values will be bound as Strings.
* sortOrder determines the order of rows. Passing null will use the default sort order, which may be unordered.
* To back a ListView with a Cursor, the cursor must contain a column named _ID.
*/
 
	cursorLoader = new CursorLoader(this, DatabaseAccessUtility.CONTENT_URI, projection, null, null, null);
	return cursorLoader;
		
   }
 
/**
* Called when a previously created loader has finished its load. This assigns the new Cursor but does not close the previous one. 
* This allows the system to keep track of the Cursor and manage it for us, optimizing where appropriate. This method is guaranteed
* to be called prior to the release of the last data that was supplied for this loader. At this point you should remove all use of 
* the old data (since it will be released soon), but should not
* do your own release of the data since its loader owns it and will take care of that.
* The framework would take of closing of old cursor once we return.
*/
 
   public void onLoadFinished(Loader<Cursor> loader,Cursor cursor) {
	if(mAdapter!=null && cursor!=null)
		mAdapter.swapCursor(cursor); //swap the new cursor in.
	else
		Log.v(TAG,"OnLoadFinished: mAdapter is null");
   }
 
/**
* This method is triggered when the loader is being reset and the loader  data is no longer available. 
* 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.
*/
 
   public void onLoaderReset(Loader<Cursor> arg0) {
	if(mAdapter!=null)
		mAdapter.swapCursor(null);
	else
		Log.v(TAG,"OnLoadFinished: mAdapter is null");
   }
	
}
上面的代码将在 Listview 中显示 DatabaseHandler.UserTable.name 数据库表中的条目。


