65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Android 中使用 Cursor Loader

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (10投票s)

2013年1月16日

CPOL

3分钟阅读

viewsIcon

113712

downloadIcon

1917

本文档有助于理解在 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.managedQueryActivity.startManagingCursor。  
  • 每个 Activity 或 Fragment 只有一个 LoaderManagerLoaderManager 会自动管理 Activity 或 Fragment 内的一个或多个 Loader 实例的生命周期。 

使用 Loader 的应用程序通常包含以下内容:

  • 一个 Activity 或 Fragment。
  • 一个 LoaderManager 实例。 
  • 一个 CursorLoader:用于加载由 ContentProvider 支持的数据。或者,开发者可以自由实现自己的 Loader 或 AsyncTaskLoader 子类来加载其他类型的数据。 
  • LoaderManager.LoaderCallbacks 的实现。 这是一个回调接口,允许客户端与 LoaderManager 进行交互。  
  • 一种显示 Loader 数据的方式,例如 SimpleCursorAdapter。 
  • 数据源,例如 ContentProvider (在使用 CursorLoader 时)。  

在 Android < 3.0 中使用 Cursor Loader

  1. 开发者需要使用兼容库:https://developer.android.com.cn/tools/extras/support-library.html  
  2. 按照以下说明进行设置:  https://developer.android.com.cn/sdk/compatibility-library.html#SettingUp   
  3. 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);
  1. Activity 需要实现 LoaderManager.LoaderCallbacks
  2. 初始化 Loader
  3. 实现 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 数据库表中的条目。

帮助文档  

© . All rights reserved.