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
在OnCreate方法中调用了getLoaderManager().initLoader(0,null,m_loaderCallback);
第一个参数0是指loader的id,我们并不关注它,所以设置了一个0。
第二个参数是给Loader初始化的时候传递的参数(也就是onCreateLoader中的第二个参数)。
第三个参数很重要,实现了LoaderManager.LoaderCallbacks<Cursor>接口的三个方法:
onCreateLoader
onLoadFinished
onLoaderReset
在onCreateLoader中创建自定义的SortCursorLoader加载器
SortCursorLoader.java
SortCursor.java
上面这些就是使用加载器的核心方法,代码中重要的地方都添加了注释,大家最好先自己编写代码加深理解,有些代码和布局文件没贴出来,有兴趣的朋友可以下载源码
源码下载地址
Android装载器的使用
其次,如果联系人数据库中数据变化了,我们还需要通过观察者模式的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装载器的使用
相关文章推荐
- 【Android Developers Training】 9. 覆盖于布局之上的Action Bar
- Android高手进阶教程(四)之----Android 中自定义属性(attr.xml,TypedArray)的使用!
- Android一个Demo搞定所有控件
- Android一个Demo搞定所有控件
- android开发小技巧
- android 获取本机号码需要root吗?
- android 获取本机号码需要root吗?
- android 获取本机号码需要root吗?
- android 获取本机号码需要root吗?
- android 获取本机号码需要root吗?
- Android 4.2 ubantu 不能识别adb
- android 获取本机号码需要root吗?
- Android开发环境搭建
- android获取string.xml的值
- 安装了android ADT后,找不到Andriod AVD Manager的android组件的图标解决方法
- Android 扩大触摸的触发区域 TouchDelegate
- 搭建 Android 2.2 开发环境
- Android中通过Intent调用其他应用的方法
- Android开发:ImageView滤镜效果
- Phonegap学习点滴(1) -- for android 环境搭建