Loader 是在 Android 3.0 之后引入的api,主要完成单线程好事数据加载,并且能够在数据有更新的时候,通知UI自动刷新。
优点:
简单,快捷
过程可控
使用的缺点:
对只需要几秒内的线程操作方便
缺点:
在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.
优点:
提供异步加载数据功能;
对数据源变化进行监听,实时更新数据;
在Activity配置发生变化(如横竖屏切换)时不避免数据重复加载;
适用于任何Activity和Fragment;
我们经常会遇到这种需求,在activity 或者 fragment 里面,我们需要查找某个数据源,并且显示出来。
然后当数据源改变的时候,Ui显示也要自动更新。
一般查找数据的时间,可能比较长,为了避免anr,我们都会通过 Thread + handler 组合来更新,但是这样就比较复杂。好在google推出 Loader 机制来帮我们实现这一功能。
我们 以访问 通讯录数据库同时实时更新UI 为例子。
接口 | 说明 |
---|---|
LoaderManager | 和Activity 或 Fragment 相关联的抽象类用来管理一个或者多个Loader实例 |
Loader | 执行异步加载数据的抽象类。这是加载器的基类。我们通常使用CursorLoader,当加载器处于活动状态时,应监控其数据源并在内容变化时,传递新的内容 |
LoaderManager.LoaderCallback | 回调接口,用于客户端与LoaderManager进行交互,比如 可以使用onCreateLoader()回调方法创建新的加载器 |
AsyncTaskLoader | 提供AsyncTask来执行工作的抽象加载器 |
CursorLoader | AsyncTaskLoader的子类,它将查询ContentResolve并返回一个Cursor. |
布局就一个 listView 不在此显示
package myapplication.lum.com.myloadercontext;
import android.app.Activity;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ContentResolver;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.text.TextUtils;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.SimpleCursorAdapter;
public class MainActivity extends Activity implements LoaderCallbacks,
OnQueryTextListener {
private ListView lv;
private SimpleCursorAdapter adapter;
private String curFilter;// 用户当前输入的搜索内容,只有在用户输入的时候才会有,应用程序刚打开的时候没有
private Uri phoneUri;
private Cursor cursor;// 记录当前ListView中展示的数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CheckPermission checkPermission = new CheckPermission(this);
checkPermission.checkPermissionExe();
lv = (ListView) findViewById(R.id.lv);
phoneUri = Phone.CONTENT_URI;// 电话号码Ur
//第一个参数 当使用的activity名字
//第二个参数 要绑定数据的布局
//第三个参数 游标没有可以设置为空
//第四个参数 指定 column 中的哪些列的数据将绑定(显示)到 UI 中
//第五个参数就是要绑定到UI界面的那个组件
adapter = new SimpleCursorAdapter(MainActivity.this,
android.R.layout.simple_list_item_2, null, new String[] {
Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
new int[] { android.R.id.text1, android.R.id.text2 });
lv.setAdapter(adapter);
// 点击item,打电话:权限
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view,
int position, long id) {
// 点击到某个item的时候,从cursor取出该item的数据
cursor.moveToPosition(position);
//拿出来id,因为要通过id查电话号码
String idd = cursor.getString(cursor
.getColumnIndex(Contacts._ID));
ContentResolver resolver = getContentResolver();
//电话号码相关信息都在Phone中
Cursor cursor2 = resolver.query(phoneUri, new String[] { Phone.DATA1 },
Phone.CONTACT_ID + "=?", new String[] { idd }, null);
if(cursor2.moveToNext()){
//取出电话号码
String num = cursor2.getString(cursor2.getColumnIndex(Phone.DATA1));
System.out.println("==num==="+num);
//打电话:权限
Intent intent=new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel://"+num));
startActivity(intent);
}
}
});
getLoaderManager().initLoader(1, null, this);
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
Uri uri;
if (curFilter != null) {
uri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(curFilter));
} else {
uri = Contacts.CONTENT_URI;
}
// 记录的是要查询的列
String[] projection = new String[] { Contacts._ID,
Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY, };
// 查询条件:
String selection = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
CursorLoader loader = new CursorLoader(MainActivity.this, uri,
projection, selection, null, Contacts.DISPLAY_NAME
+ " COLLATE LOCALIZED ASC");
return loader;
}
@Override
public void onLoadFinished(Loader loader, Cursor data) {
adapter.swapCursor(data);
cursor = data;
}
@Override
public void onLoaderReset(Loader loader) {
adapter.swapCursor(null);
}
@Override
public boolean onQueryTextSubmit(String query) {
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
curFilter = TextUtils.isEmpty(newText) ? null : newText;
// 输入内容改变过程中,动态刷新ListVIew
getLoaderManager().restartLoader(1, null, this);
return true;
}
}
运行效果:
我们先是 创建一个 loader 并且给它设置了查询的 数据库的地址和筛选条件,
他就会在后台对数据库进行检测观察,当数据有变化时,它就会回调自己的
onLoadFinished() 函数,我们在这个函数里,通过 adapter.swapCursor(data); 刷新数据。
当然 loader 也可以用来加载自己的数据库,视频,图片等。
文章参考:
Android中CursorLoader的使用、原理及注意事项
Android中CursorAdapter的使用详解
Android Cursor 的使用细节