Android加载器Loader使用

假设我们现在需要读取数据库中的联系人,如果联系人很多的话,我们必须将查询数据库的操作逻辑放在子线程中,不然会导致UI线程阻塞,然后再及时将查询到的数据告知显示界面让其刷新.

其次,如果联系人数据库中数据变化了,我们还需要通过观察者模式的ContentObserver类中的onChange方法来监听相应的数据库改变,然后再重新查询,排序,刷新等等(这里不对ContentObserver内容观察者做解释,有兴趣的朋友可以自己查资料)


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


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

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

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

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

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


扫描二维码关注公众号,回复: 11120451 查看本文章

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

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

Image(1)

对应的就是这几个操作。

 

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

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

Image(2)

在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装载器的使用





发布了21 篇原创文章 · 获赞 8 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/zhangxuebing2/article/details/16986277