Mms模块ConversationList流程分析(2)
接上一篇:Mms模块ConversationList流程分析(1)
三 联系人数据的查询
前面使用AsyncQueryHandler所获取到的cursor仅仅是查询了,所有对话信息数据;但是其中的联系人仅仅只是保存了其ID:recipientIds;
还需要根据此recipientIds获取其联系人的信息;这个就是在包装信息数据给ListItem使用的时候获取的;
Conversation conv = Conversation.from(context, cursor);获取联系人相关信息;
下面看下这个过程:
先从Conversation中的Cache中查找是否当前cursor所对应的Thread信息已存在于缓存中,
若存在则将其更新并返回;否则新创建,并将其加入到缓存Cache中;
public static Conversation from(Context context, Cursor cursor) {
long threadId = cursor.getLong(ID);
if (threadId > 0) {
//从Conversation缓存中查找cursor所对应的Thread信息
Conversation conv = Cache.get(threadId);
if (conv != null) {
//已存在缓存中 update the existing conv in-place
fillFromCursor(context, conv, cursor, false);
return conv;
}
}
//不存在于缓存中,新创建
Conversation conv = new Conversation(context, cursor, false);
Cache.put(conv);
return conv;
}
实际上不管是更新还是创建 都会走函数fillFromCursor();
那么下面看看这个函数都干了些什么事情;
private static void fillFromCursor(Context context, Conversation conv,
Cursor c, boolean allowQuery) {
synchronized (conv) {
//填写conv实例基本的对话信息 如ThreadId,date,count,attach,type等;
}
//获取cursor中联系人Ids;
String recipientIds = c.getString(RECIPIENT_IDS);
//通过recipientIds 获取对应的联系人数据:address,name……
ContactList recipients = ContactList.getByIds(recipientIds, allowQuery);
synchronized (conv) {
//关联联系人数据
conv.mRecipients = recipients;
//计算未读信息条数
}
}
(注意这里的synchronized的用法,这个是线程相关,这里不详细分析)
——》ContactList.getByIds
这里就转到ContactList类里面操作去了()这里还是mms的data包下里面的类;
1 ContactList
到此查询过程如下:
下面几个部分就围绕这个流程图进行详细介绍;
ContactList
此类从ArrayList继承下来:public class ContactList extends ArrayList<Contact>{}
public static ContactList getByIds(String spaceSepIds, boolean canBlock) {
ContactList list = new ContactList();
//foreach语句
for (RecipientIdCache.Entry entry : RecipientIdCache
.getAddresses(spaceSepIds)) { //根据Id获取号码 访问数据库
if (entry != null && !TextUtils.isEmpty(entry.number)) {
//根据号码获取联系人数据
Contact contact = Contact.get(entry.number, canBlock);
contact.setRecipientId(entry.id);
list.add(contact);
}
}
//返回联系人列表 给Conversation;
return list;
}
通过这里Contact contact = Contact.get(entry.number, canBlock); 传入号码
——》转到Contact里面执行;
下面看看这个类的get方法
2 Contact 异步或者阻塞方式获取联系人数据
number:联系人的号码
canBlock:将决定是以阻塞的方式还是异步的方式获取联系人数据
public static Contact get(String number, boolean canBlock) {
//调用的是ContactsCache类实例的get方法
return sContactCache.get(number, canBlock);
}
下面看一下ContactsCache里面的get方法
ContactsCache是Contact类的内部类;
public Contact get(String number, boolean canBlock) {
//返回一个contact不管数据库中是否存在 先从内部缓存中查找匹配号码的
//若不存在则直接将其返回,若不存在则返回新创建一个
Contact contact = get(number); //内部查找
Runnable r = null;
synchronized (contact) {
while (canBlock && contact.mQueryPending) { //是否阻塞方式
contact.wait(); }
final Contact c = contact; //匿名内部类实现线程
r = new Runnable() {
public void run() {
//仍然要在线程中更新,填充联系人数据 不管是否从缓存中取得
updateContact(c); }
}; }
if (canBlock) {
r.run(); //阻塞方式
} else {
pushTask(r); //异步方式 }
return contact;
}
通过此方法异步或者阻塞方式获取到的联系人数据 通过此get方法得到的联系人数据可能仅仅只是包含号码,而没有其他数据信息;
下面看一下updateContact方法
private void updateContact(final Contact c) {
Contact entry = getContactInfo(c.mNumber);
//从数据库中获取Contact数据
Contact entry = getContactInfo(c.mNumber);
//设置Contact实例c的数据信息:name id……
//notify to update who?
//who? Here It is ConversationListItem;
UpdateListener l;//从Contact的UpdateListener队列中获取一个Listener对象
//更新当前监听者所使用的Contact数据
l.onUpdate(c)
}
3 Contact中updateListener的添加
这里存在一个updateListener对象就是 ConversationListItem实例:什么时候传进去的呢?
看到 bind方法被调用时 也就是 上面所讲bindView时;
public final void bind(){
……
Contact.addListener(this); //添加UpdateListener
}
4 联系人处理方式
Contact处理联系人数据有两种方式:异步和阻塞;具体这里不作详细分析;
那么这里有个点让我不明白!
1) 加载一个Thread ListItem对应的联系人数据 可能有多个 是在一个ContactList getByIds方法中for循环执行;
2) 异步方式单独处理每一个号码对应的联系人数据
3) 将pushTask(r);加入到TaskStack中之后,放弃对CPU的控制权;
4) TaskStack中线程mWorkerThread;异步执行存在很多的不确定性,怎么控制;
5) 一次异步方式执行updateContact会去通知ItemList更新数据,为什么不是选择一个Thread信息所有的号码处理完毕 之后再去更新ItemList;
是这么个道理: 加入TaskStack中时;执行:
public void push(Runnable r) {
synchronized (mThingsToLoad) {
mThingsToLoad.add(r);
mThingsToLoad.notify(); //放弃资源控制
}
} 这里就等待getByIds将所有的号码处理加入到TaskStack中来处理;
但是仍然是每一次线程run方法都会去更新;也就是更新一个联系人就要更新UI一次。这样岂不浪费时间和资源;
这个mWorkerThread线程是在启动MmsApp时候就启动了
MmsApp.java中
@Override
public void onCreate(){
……
//创建ContactsCache对象,ContactsCache创建TaskStack对象
//TaskStack是ContactsCache内部类 其构造函数启动线程
Contact.init(this);
}
这个Mms这个联系人管理是比较的复杂!这里所认识的可能并一定正确;待后续完善。
五 数据更新到界面更新
回到ConversationListAdapter的函数bindView函数中来
@Override
public void bindView(View view, Context context, Cursor cursor) {
ConversationListItem headerView = (ConversationListItem) view;
Conversation conv = Conversation.from(context, cursor);
ConversationListItemData ch = new ConversationListItemData(context, conv);
headerView.bind(context, ch);
}
需要看一下headerView.bind所执行的bind函数:
//更新ListViewItem中控件的相关内容
void bind(Context context, final ConversationListItemData ch) {
……
// Date
mDateView.setText(ch.getDate());
// From.
mFromView.setText(formatMessage(ch));
// Register for updates in changes of any of the contacts in this conversation.
ContactList contacts = ch.getContacts();
Contact.addListener(this); // 有onUpdate函数
// Subject
mSubjectView.setText(ch.getSubject());
//avatar
updateAvatarView();
}
整个ConversationList界面到数据加载的主要过程便是这样。
这里仅仅只是一个框架性的对各个模块进行分析和介绍;
具体各个模块还需要进一步的详细分析。
其中可能存在有些地方并不正确,欢迎指正;
或将待后续深入了解之后进行更正更新。