异步任务装载器-AsyncTaskLoader 与 SearchView搜索视图控件的使用
使用诀窍:利用装载器管理者创建一个装载器, 要装载器用异步任务做事.当管理者重启装载器就可以再次执行异步任务
异步任务装载器的特点:
它们对每个Activity和Fragment都有效。
他们提供了异步加载数据的能力。
它拥有一个数据改变通知机制,当数据源做出改变时会及时通知。
当Cursor 发生变化时,会自动加载数据,因此并不需要再重新进行数据查询。
asynctaskLoader 异步装载器
1.数据库查询操作是耗时操作,插入多条语句等也是耗时操作--异步装载器可以帮把耗时操作放在子线程
2.方便我们更新数据库数据
3.需要在3.0以上的系统中使用
4.只能用在activity或者fragment中
5.需要使用cursoradapter去填充数据
AsyncTaskLoader中各个方法的执行顺序:
04-01 04:00:25.477: MainActivity: ==onCreate 04-01 04:00:25.701: LoaderCallbacks: ==onCreateLoader 04-01 04:00:25.705: AsyncTaskLoader: ==onStartLoading 04-01 04:00:25.709: AsyncTaskLoader: ==loadInBackground 04-01 04:00:25.721: AsyncTaskLoader: ==deliverResult 04-01 04:00:25.721: LoaderCallbacks: ==onLoadFinished 04-01 04:00:25.749: MainActivity: ==onStart 04-01 04:00:25.749: MainActivity: ==onResume 04-01 04:00:25.973: MainActivity: ==onCreateOptionsMenu
当另一个app修改了同一个数据源(如:共同使用的SDCard上的数据库)后AsyncTaskLoader中各个方法的执行顺序:
04-01 04:01:06.693: MainActivity: ==onPause 04-01 04:01:08.413: MainActivity: ==onStop 04-01 04:01:15.721: AsyncTaskLoader: ==onStartLoading 04-01 04:01:15.721: AsyncTaskLoader: ==loadInBackground 04-01 04:01:15.721: AsyncTaskLoader: ==deliverResult 04-01 04:01:15.721: LoaderCallbacks: ==onLoadFinished 04-01 04:01:15.757: MainActivity: ==onStart 04-01 04:01:15.757: MainActivity: ==onResume
案例:搜索短信, 异步加载,自动刷新适配器
MainActivity.java:
public class MainActivity extends Activity { private SearchView searchView;//搜索框输入的内容 private Spinner spinner;//选择条件 private ListView listView;//显示按条件搜索出来的短信 /** * 记录下拉列表的选项,默认为按号码搜索(与短信数据库的表字段对应) * address为电话号码 boyd为短信内容(数据库中的列名) */ private static String spinnerText="address"; private ArrayAdapter<String> spinnerAdapter; private static ContentResolver contentResolver;//访问短信数据库的内容分解者 //申明查询短信数据库的uri private static Uri smsUri = Uri.parse("content://sms"); //listView适配器 private static SimpleCursorAdapter listView_cursorAdapter; private LoaderManager loaderManager;//装载器管理者 private MyLoaderCallbacks myLoaderCallbacks;//装载器 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); spinnerAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, new String[]{"按电话号码搜索","按短信内容搜索"}); spinner.setAdapter(spinnerAdapter); initEvent(); //这里游标设置为空,也就是还没有数据 listView_cursorAdapter=new SimpleCursorAdapter(this, R.layout.listview_item_sms,null, new String[]{"address","body"}, new int[]{R.id.tv_address,R.id.tv_body}); listView.setAdapter(listView_cursorAdapter); //获取内容分解者 contentResolver = getContentResolver(); //1.先获得装载器管理器,每个activity或fragment只有一个LoaderManager。但是一个LoaderManager可以拥有多个装载器。 loaderManager = getLoaderManager(); myLoaderCallbacks = new MyLoaderCallbacks(); //2.用装载器管理者去初始化装载器,第三个参数为回调接口 loaderManager.initLoader(100,//装载器的id,当已经存在该id对应的装载器,不会调用装载器创建的回调方法 null,// Bundle args 用来传递数据 myLoaderCallbacks);//装载器 } /** * 这就是装载器(机器人) */ class MyLoaderCallbacks implements LoaderCallbacks<Cursor>{ /** * 当你试图去操作一个装载器时(比如,通过initLoader()),会检查是否指定ID的装载器已经存在. * 如果它不存在,将会触发LoaderManager.LoaderCallbacks 的方法onCreateLoader() * 一旦装载器被激活,它们将监视它们的数据源并且在数据改变时发送新的结果。 */ @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { System.out.println("MyLoaderCallbacks--onCreateLoader方法执行"); //装载器工作的异步操作工具,执行异步类里的后台执行方法 return new MyAsyncTaskLoader(MainActivity.this,args); } /** * 这个方法是在前面已创建的装载器已经完成其后台操作过程后被调用. * 这个方法保证会在应用到装载器上的数据被释放之前被调用; */ @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { System.out.println("MyLoaderCallbacks--onLoadFinished方法执行"); } /** * 当一个装载器被重置而其数据无效时被调用。 */ @Override public void onLoaderReset(Loader<Cursor> loader) { System.out.println("MyLoaderCallbacks--onLoaderReset方法执行"); } } /**注意:该类必须为静态,否则报异常 (机器人的任务) * 执行异步加载工作的抽象类.必须重写onStartLoading()、loadInBackground() deliverResult() * 而且要在onStartLoading中调用forceLoad()才能依次调用下一个即将执行的方法。 */ static class MyAsyncTaskLoader extends AsyncTaskLoader<Cursor>{ private Bundle bundle; public MyAsyncTaskLoader(Context context,Bundle bundle) { super(context); this.bundle=bundle;//用来通讯 System.out.println("异步任务构造方法执行"); } @Override//主线程中运行 protected void onStartLoading() { super.onStartLoading(); //onStartLoading方法执行完后不会自动执行loadInBackground,需要强制执行 forceLoad();//强制执行loadInBackground } /**子线程中运行 * 该方法中执行后台处理,初始化装载器或者restartLoader就会执行该方法 * 由于创建该类的时候加的泛型是Cursor,所以这里返回的结果就为Cursor * 该方法就是装载器用异步任务所做的事,做完将结果返回给deliverResult()方法 */ @Override public Cursor loadInBackground() { System.out.println("异步操作执行"); if(bundle!=null){ //获取传过来的数据,搜索框与下拉列表的条件 String str1 = bundle.getString("搜索框文本"); String str2 = bundle.getString("下拉列表选项"); //号码模糊查询 String sql1= "select * from sms where address like %搜索内容%"; //内容模糊查询 String sql2= "select * from sms where body like %搜索内容%"; //搜索框为空,查询所有 if(TextUtils.isEmpty(str1)){ return contentResolver.query(smsUri, null, null, null, null); } //因为定义了全局变量spinnerText,所以str2的值没做用,还剩去了判断 Cursor cursor = contentResolver.query(smsUri, null, spinnerText+" like '%"+str1+"%' ",null, null); return cursor; } return contentResolver.query(smsUri, null, null, null, null); } /** * 在该方法中执行跟适配器交换数据的操作。data为上面函数传过来的值, 在主线程中运行 */ @Override public void deliverResult(Cursor data) { super.deliverResult(data); //将新的cursor替换到cursoradapter中,会自动更新listview listView_cursorAdapter.swapCursor(data); } } /** * 初始化监听事件 */ private void initEvent(){ //searchView.setSubmitButtonEnabled(true);设置显示提交按钮 //搜索框监听事件 searchView.setOnQueryTextListener(new OnQueryTextListener() { @Override//提交的时候调用 public boolean onQueryTextSubmit(String query) { return false; } @Override//搜索框文字改变的时候调用 public boolean onQueryTextChange(String newText) { //搜索框文本变化就需要重新查询数据库,也就是要让装载器去根据条件查询数据库数据显示到listview //传递查询条件 Bundle bundle = new Bundle(); bundle.putString("下拉列表选项", spinnerText); bundle.putString("搜索框文本", newText);//获取输入框当前的文本内容 //重新开启装载器,就会执行后台工作了 loaderManager.restartLoader(100, bundle, myLoaderCallbacks); return false; } }); //下拉列表监听事件 spinner.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { //记录下拉列表的选项内容 String item=spinnerAdapter.getItem(position); if("按电话号码搜索".equals(item)){ spinnerText="address"; }else{ spinnerText="body";//按短信内容查找 } } @Override public void onNothingSelected(AdapterView<?> parent) { } }); } /** * 初始化控件 */ private void initView(){ searchView = (SearchView) findViewById(R.id.searchView); spinner = (Spinner) findViewById(R.id.spinner); listView = (ListView) findViewById(R.id.listView); TextView tv=(TextView) findViewById(R.id.listView_empty); listView.setEmptyView(tv);//为listview设置无数据的提示语 } }
布局文件,activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <!--android:iconifiedByDefault 设置SearchView的默认状态。如果为true,在没有被使用和点击展开时它将被图形化。 android:queryHint 当查询条件为空时显示的一个提示字符串。 --> <SearchView android:id="@+id/searchView" android:layout_width="match_parent" android:layout_height="wrap_content" android:iconifiedByDefault="false" android:queryHint="请输入搜索内容"/> <Spinner android:id="@+id/spinner" android:layout_margin="5dp" android:layout_width="match_parent" android:layout_height="wrap_content"/> <ListView android:id="@+id/listView" android:layout_height="match_parent" android:layout_width="match_parent"/> <!-- ListView没有数据显示的提示语 --> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="20sp" android:textColor="#ff0000" android:gravity="center" android:id="@+id/listView_empty" android:text="ListView里没有数据时显示的文字"/> </LinearLayout>
listview子布局:listview_item_sms.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <!-- listView的item布局 --> <TextView android:id="@+id/tv_address" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#ff0000" android:textSize="25sp" /> <TextView android:id="@+id/tv_body" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="20sp" /> </LinearLayout>
访问短信数据库的权限
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
效果图: