在前面的文章:基于Android的短信应用开发(五)中,我们通过ContentResolver读取了手机短信会话列表,但是,通过ContentResolver直接从数据库读取的方式是存在问题的,之前的做法是实际上是直接在UI线程中进行数据库读取操作,在读取少量数据记录的情况下可能还影响不大,但是在需要操作的数据量较大,如有几百成千个短信会话时,数据的读取是需要花费一定时间的。而直接在UI线程中执行耗时操作将会阻塞线程,阻塞超过一定时间时(一般是5s)时将会造成ANR(程序无响应)的后果。为了避免出现这种情况,我们可以使用AsyncQueryHandler来异步地执行耗时的数据查询操作,下面就来看一下如何通过AsyncQueryHandler来改进之前读取会话信息的操作。
我们先来看一下之前是如何读取会话记录的,具体实现为MessageListAdapter.java的getMessageSessions()方法,下面是其源码:
public void getMessageSessions(){
Cursor sessionCursor = null;
ContentResolver resolver = null;
ContactData contact = null;
try{
Uri uri = Uri.parse(SMS_URI_ALL);
resolver = mContext.getContentResolver();
sessionCursor = resolver.query(uri, new String[]
{ "* from threads--" }, null, null, null);
if (sessionCursor == null) {
return;
}
if (sessionCursor.getCount() == 0){
sessionCursor.close();
sessionCursor = null;
return;
}
sessionCursor.moveToFirst();
while (sessionCursor.isAfterLast() == false)
{
/*
threads表各字段含义如下:
1._id为会话id,他关联到sms表中的thread_id字段。
2.recipient_ids为联系人ID,这个ID不是联系人表中的_id,而是指向表canonical_address里的id,
canonical_address这个表同样位于mmssms.db,它映射了recipient_ids到一个电话号码,也就是说,
最终获取联系人信息,还是得通过电话号码;
3.mesage_count该会话的消息数量
4.snippet为最后收到或发出的信息
*/
int thread_idColumn = sessionCursor.getColumnIndex("_id");
int dateColumn = sessionCursor.getColumnIndex("date");
int message_countColumn = sessionCursor.getColumnIndex("message_count");
int snippetColumn = sessionCursor.getColumnIndex("snippet");
int typeColumn = sessionCursor.getColumnIndex("type");
//格式化短信日期显示
Date date = new Date(Long.parseLong(sessionCursor.getString(dateColumn)));
//获得短信的各项内容
//phoneAndUnread[0]存放电话号码,phoneAndUnread[1]存放该会话中未读信息数
String threadId = sessionCursor.getString(thread_idColumn);
String phoneAndUnread[]=getPhoneNum(threadId);
String phone = phoneAndUnread[0];
String unreadCount = phoneAndUnread[1];
String last_mms=sessionCursor.getString(snippetColumn);
String date_mms=date.toString();
String count_mms=sessionCursor.getString(message_countColumn);
String type = sessionCursor.getString(typeColumn);
SMSInfo smsinfo = new SMSInfo();
/*
phoneAndUnread[0]存放电话号码
*/
contact = getContactFromPhoneNum(mContext, phoneAndUnread[0]);
//获得会话的未读短信与所有短信数
String final_count=unreadCount+"/"+count_mms;
smsinfo.setContactNumber(phone);
smsinfo.setContactName(contact.getContactName());
//如果有该信息会话人头像,则设置已有头像,如果没有则给他设置一个默认的头像
if (contact.getPhotoUri() == null){
smsinfo.setContactPhoto(BitmapFactory.decodeResource(
mContext.getResources(), R.drawable.ic_launcher));
}else{
Uri photoUri = contact.getPhotoUri();
InputStream input = ContactsContract.Contacts.
openContactPhotoInputStream(resolver, photoUri);
smsinfo.setContactPhoto(BitmapFactory.decodeStream(input));
}
smsinfo.setDate(date_mms);
smsinfo.setSmsbody(last_mms);
smsinfo.setType(type);
smsinfo.setMessageCout(final_count);
smsinfo.setThreadId(threadId);
infos.add(smsinfo);
sessionCursor.moveToNext();
}
sessionCursor.close();
}catch(Exception e){
Log.e(ThreadTAG,"E:" + e.toString());
}finally{
if (sessionCursor != null){
sessionCursor.close();
sessionCursor = null;
}
}
}
可以看到,上面的方法是通过ContentResolver的query()执行查询的,而当使用AsyncQueryHandler进行异步查询时,又应该怎样做呢?我们需要做两件事,1.自定义Handler类并
继承AsyncQueryHandler,实现其构造方法(可直接使用父类AsyncQueryHandler的构造方法)以及onQueryComplete()方法;2.直接调用1中定义的Handler的startQuery()方法,下面来看一下具体的做法。
1.定义MessageListQueryHandler类继承自AsyncQueryHandler
public class MessageListQueryHandler extends AsyncQueryHandler{
public MessageListQueryHandler(ContentResolver cr) {
super(cr);
// TODO Auto-generated constructor stub
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// TODO Auto-generated method stub
super.onQueryComplete(token, cookie, cursor);
ContactData contact = null;
ContentResolver resolver = mContext.getContentResolver();
if (cursor == null) {
return;
}
if (cursor.getCount() == 0){
cursor.close();
cursor = null;
return;
}
cursor.moveToFirst();
while (cursor.isAfterLast() == false)
{
/*
threads表各字段含义如下:
1._id为会话id,他关联到sms表中的thread_id字段。
2.recipient_ids为联系人ID,这个ID不是联系人表中的_id,而是指向表canonical_address里的id,
canonical_address这个表同样位于mmssms.db,它映射了recipient_ids到一个电话号码,也就是说,
最终获取联系人信息,还是得通过电话号码;
3.mesage_count该会话的消息数量
4.snippet为最后收到或发出的信息
*/
int thread_idColumn = cursor.getColumnIndex("_id");
int dateColumn = cursor.getColumnIndex("date");
int message_countColumn = cursor.getColumnIndex("message_count");
int snippetColumn = cursor.getColumnIndex("snippet");
int typeColumn = cursor.getColumnIndex("type");
//格式化短信日期显示
Date date = new Date(Long.parseLong(cursor.getString(dateColumn)));
//获得短信的各项内容
//phoneAndUnread[0]存放电话号码,phoneAndUnread[1]存放该会话中未读信息数
String threadId = cursor.getString(thread_idColumn);
String phoneAndUnread[]=getPhoneNum(threadId);
String phone = phoneAndUnread[0];
String unreadCount = phoneAndUnread[1];
String last_mms=cursor.getString(snippetColumn);
String date_mms=date.toString();
String count_mms=cursor.getString(message_countColumn);
String type = cursor.getString(typeColumn);
SMSInfo smsinfo = new SMSInfo();
/*
phoneAndUnread[0]存放电话号码
*/
contact = getContactFromPhoneNum(mContext, phoneAndUnread[0]);
//获得会话的未读短信与所有短信数
String final_count=unreadCount+"/"+count_mms;
smsinfo.setContactNumber(phone);
smsinfo.setContactName(contact.getContactName());
//如果有该信息会话人头像,则设置已有头像,如果没有则给他设置一个默认的头像
if (contact.getPhotoUri() == null){
smsinfo.setContactPhoto(BitmapFactory.decodeResource(
mContext.getResources(), R.drawable.ic_launcher));
}else{
Uri photoUri = contact.getPhotoUri();
InputStream input = ContactsContract.Contacts.
openContactPhotoInputStream(resolver, photoUri);
smsinfo.setContactPhoto(BitmapFactory.decodeStream(input));
}
smsinfo.setDate(date_mms);
smsinfo.setSmsbody(last_mms);
smsinfo.setType(type);
smsinfo.setMessageCout(final_count);
smsinfo.setThreadId(threadId);
infos.add(smsinfo);
cursor.moveToNext();
}
cursor.close();
}
}
在其构造方法中,直接调用父类AsyncQueryHandler的构造方法即可,重头戏是onQueryComplete()方法,此方法将在异步查询完成后,根据数据库查询的结果进行后续的处理操作,在此处基本上是将之前getMessageSessions()中的操作移到了
onQueryComplete()中执行,同时将getMessageSessions()中原有获取查询结果后的操作去掉。
2.在 getMessageSessions()中调用 MessageListQueryHandler的startQuery()方法
在上一步中,我们已经将获取数据库查询结果后的相关操作移到了MessageListQueryHandler的onQueryComplete()中去了,因此,在此时的getMessageSessions()中,我们只需调用MessageListQueryHandler的startQuery()方法就可以了,startQuery()定义如下:
void |
startQuery(int token,
Object cookie,
Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String orderBy)
This method begins an asynchronous query.
|
参数解释:
token,一个令牌,主要用来标识查询,保证唯一即可.需要跟onQueryComplete方法传入的一致。(当然你也可以不一致,同样在数据库的操作结束后会调用对应的onQueryComplete方法 );
cookie,你想传给onXXXComplete方法使用的一个对象。(没有的话传递null即可)
其他的五个参数与ContentResolver的query()方法参数相对应,即:
Uri uri 进行查询的通用资源标志符:
projection 查询的列
selection 限制条件
selectionArgs 查询参数
orderBy 排序条件
OK,方法解释完了,我们还是来看一下 getMessageSessions()新的定义吧。
public void getMessageSessions(){
Uri uri = Uri.parse(SMS_URI_ALL);
String[] projection = new String[] { "* from threads--" };
messageListQueryHandler = new MessageListQueryHandler(mContext.getContentResolver());
messageListQueryHandler.startQuery(0, null, uri, projection, null, null,null);
}
好了,通过上面的两步走,我们就已经成功将数据查询的工作从UI线程转移到AsyncQueryHandler中去了,过程也并不复杂,只需要将数据处理操作放到
AsyncQueryHandler的
onQueryComplete()方法中去,然后调用AsyncQueryHandler的startQuery()就可以搞定啦。
参考文章链接:http://blog.csdn.net/honeybaby201314/article/details/42492251