您的位置:首页 > 移动开发 > Android开发

Android加载器Loader使用

2013-11-27 16:38 447 查看
假设我们现在需要读取数据库中的联系人,如果联系人很多的话,我们必须将查询数据库的操作逻辑放在子线程中,不然会导致UI线程阻塞,然后再及时将查询到的数据告知显示界面让其刷新.
其次,如果联系人数据库中数据变化了,我们还需要通过观察者模式的ContentObserver类中的onChange方法来监听相应的数据库改变,然后再重新查询,排序,刷新等等(这里不对ContentObserver内容观察者做解释,有兴趣的朋友可以自己查资料)

上面思路也能实现我们想要的结果,但是使用加载器Loader和加载管理器LoaderManager能够更加方便实现.

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

1.  装载器对于每个Activity和Fagment都是有效的;
2.  装载器提供异步数据加载的能力;
3.  装载器监视数据资源并且当内容改变时发送新的结果;
4.  在配置改变后重建的时候,装载器自动的重连最后的装载器游标,因此,不需要重新查询数据。

LoaderManager就是加载器的管理器,一个LoaderManager可以管理一个或多个Loader,一个Activity或者Fragment只能有一个LoadManager。LoaderManager管理Loader的初始化,重启和销毁操作。

从官网就可以看出它包含的方法有:





对应的就是这几个操作。

 

initLoader是初始化一个加载器,它的第三个参数是一个LoaderCallbacks<D>接口,LoaderManager的initLoader是不做任何事情的,它只绑定了一个LoaderCallbacks<D>,具体的创建Loader的事情是由这个callback来做的。

LoaderCallbacks<D>接口需要实现的三个方法:





在loader创建loader的时候会调用onCreateLoader,然后当load数据结束的时候(第一次读取数据或者数据有改变的时候load数据)会调用onLoadFinished,而onLoaderReset只有在destory一个loader的时候才有可能调用。

 

所以一般创建数据Cursor(CursorLoader)的工作是在onCreateLoader中做,将CursorLoader返回,这样就创建了对这个数据源的监控,当数据源有数据变化的时候,就会自动调用了onLoadFinished函数了。

通过一个例子来看看如何使用加载器,先上效果图



实现的功能很简单

1,通过加载器来加载数据库中的数据,将返回的游标cursor和适配器绑定

2,通过ContentProvider事务批量删除操作

MainActivity.java

package huahua.loaderdemo;

import huahua.loaderdemo.SortCursor.SortEntry;

import java.util.ArrayList;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.LoaderManager;
import android.app.ProgressDialog;
import android.content.ContentProviderOperation;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContacts.Data;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class MainActivity extends Activity{
//字母列视图View
private AlphabetScrollBar m_asb;
//显示选中的字母
private TextView m_letterNotice;
//联系人的列表
private ListView m_contactslist;
//联系人列表的适配器
private ContactsCursorAdapter m_contactsAdapter;
//所有联系人的数据list
private ArrayList<SortEntry> mSortList = new ArrayList<SortEntry>();
//加载器监听器
private ContactsLoaderListener m_loaderCallback = new ContactsLoaderListener();
//加载对话框
private ProgressDialog m_dialogLoading;
//批量删除按钮
private Button m_DeleteNumBtn;
//选中全部按钮
private Button m_SelectAllBtn;
//新增联系人按钮
private Button m_AddContactBtn;
//选中多少个需要删除的联系人
private int m_choosenum=0;
//id的数组
private ArrayList<String> ChooseContactsID = new ArrayList<String>();
//选择所有联系人的标志
private boolean m_selectAll = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);

//得到字母列的对象,并设置触摸响应监听器
m_asb = (AlphabetScrollBar)findViewById(R.id.alphabetscrollbar);
m_asb.setOnTouchBarListener(new ScrollBarListener());
m_letterNotice = (TextView)findViewById(R.id.pb_letter_notice);
m_asb.setTextView(m_letterNotice);

//初始化装载器,并给列表设置设配器
getLoaderManager().initLoader(0,null,m_loaderCallback);
m_contactslist = (ListView)findViewById(R.id.pb_listvew);
m_contactsAdapter = new ContactsCursorAdapter(this, null);
m_contactslist.setAdapter(m_contactsAdapter);
m_contactslist.setOnItemClickListener(new OnItemClickListener()
{

@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
if(mSortList.get(arg2).mchoose == false)
{
//增加一个要删除的标记
mSortList.get(arg2).mchoose = true;
m_choosenum++;
ChooseContactsID.add(mSortList.get(arg2).mID);
}
else
{
//移除一个要删除的标记
mSortList.get(arg2).mchoose = false;
for(int i=0;i<m_choosenum;i++)
{
if(ChooseContactsID.get(i).equals(mSortList.get(arg2).mID))
{
ChooseContactsID.remove(i);
break;
}
}
m_choosenum--;
}

//刷新界面
m_contactsAdapter.notifyDataSetChanged();
m_DeleteNumBtn.setText("删除("+ m_choosenum +")");
}
});

//初始化按钮组件
m_DeleteNumBtn = (Button)findViewById(R.id.delete_num);
m_SelectAllBtn = (Button)findViewById(R.id.select_all);
m_AddContactBtn = (Button)findViewById(R.id.add_contacts);
m_DeleteNumBtn.setOnClickListener(new BtnClick());
m_SelectAllBtn.setOnClickListener(new BtnClick());
m_AddContactBtn.setOnClickListener(new BtnClick());

}

private class BtnClick implements View.OnClickListener{

@Override
public void onClick(View v) {
if(v.getId() == R.id.add_contacts)
{
Intent intent = new Intent(MainActivity.this, AddContactsActivity.class);
startActivity(intent);

ChooseContactsID.clear();
m_DeleteNumBtn.setText("删除(0)");
m_SelectAllBtn.setText("选择全部");
m_choosenum = 0;

for(int i=0;i<mSortList.size();i++)
{
mSortList.get(i).mchoose = false;
}
m_contactsAdapter.notifyDataSetChanged();
}
else if(v.getId() == R.id.delete_num)
{
if(m_choosenum>0)
{
AlertDialog DeleteDialog = new AlertDialog.Builder(MainActivity.this).
setTitle("删除").
setMessage("删除"+m_choosenum +"个联系人?").
setPositiveButton("确定", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
//删除批量联系人操作,放在线程中处理
new DeleteContactsTask().execute();
}
}).
setNegativeButton("取消", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {

}
}).
create();
DeleteDialog.show();

}
else
{
Toast.makeText(MainActivity.this, "请选择要删除的联系人", Toast.LENGTH_LONG).show();
}
}
else if(v.getId() == R.id.select_all)
{
ChooseContactsID.clear();
if(!m_selectAll)
{
for(int i=0;i<mSortList.size();i++)
{
mSortList.get(i).mchoose = true;
ChooseContactsID.add(mSortList.get(i).mID);
}
m_choosenum = mSortList.size();
m_DeleteNumBtn.setText("删除("+ mSortList.size() +")");
m_SelectAllBtn.setText("取消全部");
m_contactsAdapter.notifyDataSetChanged();
m_selectAll = !m_selectAll;
}
else
{
for(int i=0;i<mSortList.size();i++)
{
mSortList.get(i).mchoose = false;
}
m_choosenum = 0;
m_DeleteNumBtn.setText("删除(0)");
m_SelectAllBtn.setText("选择全部");
m_contactsAdapter.notifyDataSetChanged();
m_selectAll = !m_selectAll;
}

}

}

}

private class  DeleteContactsTask extends AsyncTask<Void, Integer, Void>{

@Override
protected Void doInBackground(Void... params) {
//通过contentprovider事务来批量处理,提升效率
ArrayList<ContentProviderOperation> ops =
new ArrayList<ContentProviderOperation>();

for(int i=0;i<ChooseContactsID.size();i++)
{
ops.add(ContentProviderOperation.newDelete(
Uri.withAppendedPath(RawContacts.CONTENT_URI,ChooseContactsID.get(i))).build());
}

try {
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch (RemoteException e) {
e.printStackTrace();
} catch (OperationApplicationException e) {
e.printStackTrace();
}

//不通过contentprovider事务来处理批量操作数据库,也可以达到目的
//但是效率低很多,可以和上面的方法对比
//			for(int i=0;i<ChooseContactsID.size();i++)
//			{
//				  Uri uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, ChooseContactsID.get(i));
//				  getContentResolver().delete(uri, null, null);
//			}

return null;
}

@Override
protected void onPostExecute(Void result) {
if(m_dialogLoading!= null)
{
m_dialogLoading.dismiss();
ChooseContactsID.clear();
m_DeleteNumBtn.setText("删除(0)");
m_SelectAllBtn.setText("选择全部");
m_choosenum = 0;
}
}

@Override
protected void onPreExecute() {
m_dialogLoading = new ProgressDialog(MainActivity.this);
m_dialogLoading.setProgressStyle(ProgressDialog.STYLE_SPINNER);//设置风格为圆形进度条
m_dialogLoading.setMessage("正在删除");
m_dialogLoading.setCancelable(false);
m_dialogLoading.show();
}

@Override
protected void onProgressUpdate(Integer... values) {
}

}

private class ContactsCursorAdapter extends CursorAdapter{
private ArrayList<SortCursor.SortEntry> list;
private Context context;

public ContactsCursorAdapter(Context context, Cursor c) {
super(context, c);
this.context = context;
// TODO Auto-generated constructor stub
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null)
{
convertView = LayoutInflater.from(context).inflate(R.layout.contacts_list_item, parent, false);
}

if(mSortList.size() <= 0 )
{
Log.i("huahua", "空空空");
return convertView;
}

TextView name = (TextView) convertView.findViewById(R.id.contacts_name);
name.setText(mSortList.get(position).mName);

TextView number = (TextView) convertView.findViewById(R.id.contacts_number);
number.setText(mSortList.get(position).mNum);

if(mSortList.get(position).mchoose == true)
{
ImageView choosecontact = (ImageView)convertView.findViewById(R.id.choose_contact);
choosecontact.setImageResource(R.drawable.cb_checked);
}
else
{
ImageView choosecontact = (ImageView)convertView.findViewById(R.id.choose_contact);
choosecontact.setImageResource(R.drawable.cb_unchecked);
}

return convertView;
}

@Override
public void bindView(View view, Context context, Cursor cursor) {
if(cursor == null)
{
return;
}
/*
* 在bindview中可以直接通过游标cursor来更新界面中的数据
*/
//          TextView name = (TextView) view.findViewById(R.id.contacts_name);
//          name.setText(cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
//
//          TextView number = (TextView) view.findViewById(R.id.contacts_number);
//          number.setText(cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.contacts_list_item, parent, false);
}

}

//加载器的监听器
private class ContactsLoaderListener implements LoaderManager.LoaderCallbacks<Cursor>{

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
//返回一个自定义的加载器
return new SortCursorLoader(MainActivity.this, ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null);
}

@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
//当数据库中的数据改变时,会进入此方法,然后m_contactsAdapter绑定数据库的新游标
m_contactsAdapter.swapCursor(arg1);

//返回已排序好的arraylist
SortCursor data = (SortCursor)m_contactsAdapter.getCursor();
mSortList = data.GetContactsArray();
}

@Override
public void onLoaderReset(Loader<Cursor> arg0) {
m_contactsAdapter.swapCursor(null);

}

}

//字母列触摸的监听器
private class ScrollBarListener implements AlphabetScrollBar.OnTouchBarListener {

@Override
public void onTouch(String letter) {

//触摸字母列时,将联系人列表更新到首字母出现的位置
SortCursor ContactsCursor = (SortCursor)m_contactsAdapter.getCursor();
if(ContactsCursor != null)
{
int idx = ContactsCursor.binarySearch(letter);
if(idx != -1)
{
m_contactslist.setSelection(idx);
}
}
}
}

}


在OnCreate方法中调用了getLoaderManager().initLoader(0,null,m_loaderCallback);

第一个参数0是指loader的id,我们并不关注它,所以设置了一个0。

第二个参数是给Loader初始化的时候传递的参数(也就是onCreateLoader中的第二个参数)。

第三个参数很重要,实现了LoaderManager.LoaderCallbacks<Cursor>接口的三个方法:

onCreateLoader

onLoadFinished

onLoaderReset

 

在onCreateLoader中创建自定义的SortCursorLoader加载器

SortCursorLoader.java

package huahua.loaderdemo;

import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;

public class SortCursorLoader extends CursorLoader{

public SortCursorLoader(Context context, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
super(context, uri, projection, selection, selectionArgs, sortOrder);
// TODO Auto-generated constructor stub
}

@Override
public Cursor loadInBackground() {
Cursor cursor = super.loadInBackground();
Log.i("huahua", "loadInBackground()");
//返回自定义的游标修饰,主要是对数据库中的数据进行排序
return new SortCursor(cursor);
}

}
在这个类中的loadInBackground方法中,会返回自定义的游标修饰,主要是对数据库中的数据按照一定的规则排序

SortCursor.java

package huahua.loaderdemo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import android.database.Cursor;
import android.database.CursorWrapper;
import android.provider.ContactsContract;
import android.provider.ContactsContract.RawContacts.Data;
import android.util.Log;

public class SortCursor extends CursorWrapper{
private ArrayList<SortEntry> mSortList;
private Cursor mCursor;
private int mPos;

public SortCursor(Cursor cursor) {
super(cursor);

mCursor = cursor;
mSortList = new ArrayList<SortEntry>();
for( cursor.moveToFirst(); ! cursor.isAfterLast();  cursor.moveToNext()) {
SortEntry entry = new SortEntry();
entry.mID =  cursor.getString(cursor.getColumnIndex(Data.RAW_CONTACT_ID));
entry.mName =  cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
entry.mOrder = cursor.getPosition();
entry.mPY = PinyinUtils.getPingYin(entry.mName);
entry.mNum =  cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
entry.mFisrtSpell = PinyinUtils.getFirstSpell(entry.mName);
entry.mchoose =false;
mSortList.add(entry);
}

Collections.sort(mSortList, new ComparatorPY());

}

public static class SortEntry {
public String mID;		  //在数据库中的ID号
public String mName;  //姓名
public String mPY;      //姓名拼音
public String mNum;      //电话号码
public String mFisrtSpell;      //中文名首字母 例:张雪冰:zxb
public boolean mchoose;    //是否选中
public int mOrder;      //在原Cursor中的位置
}

private class ComparatorPY implements Comparator<SortEntry>{

@Override
public int compare(SortEntry lhs, SortEntry rhs) {
// TODO Auto-generated method stub
String str1 = lhs.mPY;
String str2 = rhs.mPY;
return str1.compareToIgnoreCase(str2);
}

}

@Override
public boolean moveToPosition(int position) {
mPos = position;
if(position < mSortList.size() && position >=0)
{
return mCursor.moveToPosition(mSortList.get(position).mOrder);
}

if (position < 0) {
mPos = -1;
}
if (position >= mSortList.size()) {
mPos = mSortList.size();
}
return mCursor.moveToPosition(position);
}

@Override
public boolean moveToFirst() {
return moveToPosition(0);
}

@Override
public boolean moveToLast() {
return moveToPosition(getCount() - 1);
}

@Override
public boolean moveToNext() {
// TODO Auto-generated method stub
return moveToPosition(mPos + 1);
}

@Override
public boolean moveToPrevious() {
// TODO Auto-generated method stub
return moveToPosition(mPos - 1);
}

public ArrayList<SortEntry> GetContactsArray() {
return mSortList;
}

public int binarySearch(String letter) {
for (int index = 0;   index < mSortList.size(); index++) {
if (mSortList.get(index).mPY.substring(0, 1).compareToIgnoreCase(letter) == 0) {
return index;
}
}
return -1;
}

}

上面这些就是使用加载器的核心方法,代码中重要的地方都添加了注释,大家最好先自己编写代码加深理解,有些代码和布局文件没贴出来,有兴趣的朋友可以下载源码

源码下载地址

Android装载器的使用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息