一.通話記錄的加載過程和大部分的應(yīng)用類似,也是異步查詢數(shù)據(jù)庫,得到查詢結(jié)果后再刷新ListView。但是在加載通話記錄時還有一些特別的處理。特別是在綁定Listview時涉及到了一些復(fù)雜的操作。
顯示通話記錄由CallLogFragment.java文件進(jìn)行處理。在加載通話記錄的過程中還用到了CallLogQueryHandler查詢通話記錄數(shù)據(jù)庫,CallLogAdapter填充ListView。
查詢通話記錄的過程大致如下:
其中CallLogQueryHandler繼承自CursorQueryHandler,該類是執(zhí)行異步數(shù)據(jù)庫查詢的常用類。以上過程非常的常見,大部分程序都和該流程類似。
1、在CallLogFragment的onCreateView中設(shè)置RecyclerView的adapter,并調(diào)用fetchCalls查詢數(shù)據(jù)。
2、當(dāng)查詢完成后會回調(diào)到CallLogFragment的onCallsFetched,并調(diào)用changeCursor,registerContentObserver和registerDataSetObserver,數(shù)據(jù)發(fā)生改變的時候,調(diào)用RecyclerView.Adapter的notifyDataSetChanged就會根據(jù)新的cursor rebind and relayout所有可見的view。
二.關(guān)鍵是在CallLogAdapter?的bindView函數(shù)中執(zhí)行了很多難以理解的操作。通過查看通話記錄的顯示內(nèi)容知道,在通話記錄中顯示了撥打電話的號碼,通話記錄的類型,該號碼所對應(yīng)的聯(lián)系人(如果號碼已加入聯(lián)系人)等。當(dāng)掛斷電話時會查詢聯(lián)系人數(shù)據(jù)庫查找所撥打的電話對應(yīng)的聯(lián)系人姓名,并把這些信息寫入通話記錄數(shù)據(jù)庫中。因此在通話記錄的數(shù)據(jù)庫中有一列用于存儲聯(lián)系人姓名。但是通話記錄中的聯(lián)系人名稱和聯(lián)系人數(shù)據(jù)庫中的名稱有時候是不一致的,通話記錄中的聯(lián)系人名稱只是一個緩存??紤]這樣一種情況:當(dāng)和張三打完電話后,立刻把聯(lián)系人中的張三改為張三三,然后查看通話記錄會發(fā)現(xiàn)通話記錄也會變成張三三,而通話記錄數(shù)據(jù)庫中仍然是張三。
為了使通話記錄中的顯示名稱和聯(lián)系人中的一致,在顯示通話記錄時,需要查詢每個電話號碼對應(yīng)的聯(lián)系人信息。為了提高效率,在CallLogAdapter?中用CallLogAdapterExpirableCache數(shù)據(jù)結(jié)構(gòu)緩存查詢到的聯(lián)系人信息,這樣當(dāng)界面刷新時不需要再次執(zhí)行查詢操作。由于數(shù)據(jù)庫查詢操作比較耗時,在bindView中不可能等到查詢結(jié)果返回之后再顯示界面。所以在bindView時采用了這樣的處理策略:首先顯示通話記錄中緩存的聯(lián)系人信息,然后開啟一個線程查詢聯(lián)系人信息,并把這些信息緩存起來。該線程的優(yōu)先級比較低,它會等cup空閑時執(zhí)行查詢聯(lián)系人的操作。
如果聯(lián)系人中的信息和通話記錄數(shù)據(jù)庫中的信息不一致(其實這種情況比較少見),則發(fā)送消息給CallLogAdapter重新執(zhí)行顯示操作。具體的流程如下:
該時序圖只是展現(xiàn)了主要的框架,忽略了很多細(xì)節(jié)。
其中QueryThead負(fù)責(zé)查詢聯(lián)系人信息,得到聯(lián)系人信息緩存到ExpirableCache中,用于Bindview刷新通話記錄。