Android CursorLoader实例详解(附源码)

时间:2021-11-22 22:53:29

Android Loader的基本知识前面几篇博客已经涉及很多,接下来这篇博客将会结合一个实际Demo来描述Loader的使用,该Demo是Contacts 联系人,自定义ContentProvider的内容,提供Uri,在显示联系人列表出使用的是Cursor 的 contentResolver.query 方法,在搜索联系人部分用到了CursorLoader,下面结合这个Demo 来描述Loader的使用。

Demo 截图如下:

Android CursorLoader实例详解(附源码)Android CursorLoader实例详解(附源码)Android CursorLoader实例详解(附源码)


下面对该Demo的各个函数进行详解。搜索界面的代码是一个由Fragment组成的Activity,fragment 代码如下:

package com.uppowerstudio.chapter5.phonebook;

import android.app.ListFragment;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.SimpleCursorAdapter;

import com.uppowerstudio.chapter5.phonebook.database.Constants;

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

SimpleCursorAdapter mAdapter;
String mCurFilter;

@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
//设置包含菜单项
setHasOptionsMenu(true);
mAdapter = new SimpleCursorAdapter(getActivity(), R.layout.list_row,
null, new String[] { "contact_name", "phone_number" },
new int[] { R.id.list_item_contact_name,
R.id.list_item_contact_phone }, 0);
setListAdapter(mAdapter);
//初始化Loader ,Loader的ID 为0
getLoaderManager().initLoader(0, null, this);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Auto-generated method stub
super.onCreateOptionsMenu(menu, 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);
}

@Override
public boolean onQueryTextSubmit(String query) {
// TODO Auto-generated method stub
return true;
}

@Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager().restartLoader(0, null, this);
return true;
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// TODO Auto-generated method stub
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Constants.CONTENT_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Constants.CONTENT_URI;
}
return new CursorLoader(getActivity(), baseUri,
null, null, null,
COLUMN_ID);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// TODO Auto-generated method stub
mAdapter.swapCursor(data);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
// TODO Auto-generated method stub
mAdapter.swapCursor(null);
}
}


activity 代码比较简单,就不贴出来了

下面结合这个 Demo 对 CursorLoader进行详解。

initLoader() 函数

首先,我这里编写的查找联系人的布局是一个activity加fragment,在fragment的onActivityCreated()方法中,我们先初始化Loader,

getLoaderManager().initLoader(0, null, this); 
该函数中有3个参数,第一个是新建的Loader的ID,这里为0,第二个是Bunder类型的数据,这里设置为null,第三个是this,表示回调本身

onQueryTextChange() 函数


在这个Demo 中,我们添加了一个搜索栏用于搜索联系人,onQueryTextChange() 函数用于检测搜索框中的内容是否变化,一旦搜索栏中的内容发生变化,onQueryTextChange() 函数就会被调用。该函数的代码如下:
@Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager().restartLoader(0, null, this);
return true;
}

其中,mCurFilter是搜素栏过滤器,用于获取搜素栏的内容。当onQueryTextChange()被调用时,还调用了getLoaderManager().restartLoader(0, null, this);  函数,这个函数用于重新开启一个新的Loader,虽然说是restart,但是我们注意到这个函数有3个参数,第一个是 ID,即新的Loader 的 ID ,这里为 0 ,第二个参数是一个 Bundle 类型的数据,是一个可选参数,这里设置为null,没什么影响,第三个是 this ,表示回调它本身。

但是restartLoader()  这个函数并非将当前的 Loader 重启,如果之前restartLoader() 方法中的Loader的ID存在,即与之前调用initLoader()方法时的ID一致,则销毁这个Loader,并重新创建一个新的,ID相同的Loader,如果这个ID与之前调用initLoader()方法时的ID不一样,则重新创建一个id为这个ID的新Loader。但此时之前创建的Loader并不会被关闭,依旧可以被使用,至于创建的Loader什么时候被关闭呢?这个下面再解释。

onCreateLoader() 方法


该方法主要用于创建一个新的Loader,这里我们创建了一个CursorLoader用于异步查询联系人,避免ContentProvider应用Cursor查询时可能导致的UI阻塞。代码如下:
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// TODO Auto-generated method stub
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Constants.CONTENT_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Constants.CONTENT_URI;
}
return new CursorLoader(getActivity(), baseUri,
null, null, null,
COLUMN_ID);
}

onCreateLoader()方法有两个参数,第一个就是新创建的Loader的ID,第二个就是之前调用initLoader()restartLoader()方法时的第二个参数Bundle类型的数据。
在这里,我们使用之前在onQueryTextChange()函数中使用到的搜索过滤器mCurFilter, 该过滤器其实就是搜索栏的内容,在这里,我们将问题简单化,将搜索框的内容添加到搜索Uri的后面作为搜索联系人的ID,添加的方法为: Uri.withAppendedPath(Constants.CONTENT_URI,Uri.encode(mCurFilter))  这句代码将搜索栏的内容转码为Uri并添加到CONTENT_URI 后面,其实也就是在CONTENT_URI  content://com.uppowerstudio.chapter5.phonebook.provider/phonebook  后面加上 ID,形成新的查询某个联系人的Uri content://com.uppowerstudio.chapter5.phonebook.provider/phonebook/#   
其中 # 代表任意数字,这里为联系人的ID
onCreateLoader()函数最后返回新建了一个 CursorLoader ,其中getActivity()表示结果返回到这个fragment所绑定的那个activity,baseUri 即是我们要查询的Uri,这里是某个联系人的Uri,三个nul分别是projection,selection,selectionArgs,这里我们均设置为null,这几个参数的定义可以参考 ContentResolove.query() ,这里不再详细描述,最后一个是排序的方式,我们选择了ID,即显示查找到的联系人的时候按照查找出来的联系人的ID来排序。

onLoadFinished() 函数


在查询函数加在完毕之后,onLoaderFinish() 函数就会被调用,该函数的代码如下:
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// TODO Auto-generated method stub
mAdapter.swapCursor(data);
}

该方法中调用了设配器 mAdapter的swapCursor()方法,在加在数据完成后,将加载到的Cursor类型的数据data交换到mAdapter中并显示出来。该函数调用时,用户必须停止对 Cursor 中的数据的引用,因为这些数据即将被删除,但是用户不用也不要调用cursor的close()方法,以为在ContentProvider中用户需要调用cursor的close()方法,但是在CursorLoader中会自动关闭cursor,不用用户关闭。只要系统察觉到应用不再使用Cursor,系统就会自动关闭。

onLoaderReset()函数


该函数在推出搜索界面是被调用,用于将Loader的数据设置为不可用,如果之前新建了多个Loader,该函数就会被多次调用,每次调用都只会Reset 一个拥有独立ID的Loader。该函数的代码如下:
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// TODO Auto-generated method stub
mAdapter.swapCursor(null);
}

在该函数中将null值交换到设配器中。

onQueryTextSubmit()函数


这个函数其实没什么用,在用户的搜索栏输入数据完毕之后,按下回车时,该函数就会被调用,表示提交数据。代码如下:
@Override
public boolean onQueryTextSubmit(String query) {
// TODO Auto-generated method stub
return true;
}

至此,Loader 的讲解已经结束,完整客运行的Demo下载链接如下:

http://download.csdn.net/detail/llp1992/8106475