接上一篇: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介面到資料載入的主要過程便是這樣。
這裡僅僅只是一個架構性的對各個模組進行分析和介紹;
具體各個模組還需要進一步的詳細分析。
其中可能存在有些地方並不正確,歡迎指正;
或將待後續深入瞭解之後進行更正更新。