一.通话记录的加载过程和大部分的应用类似,也是异步查询数据库,得到查询结果后再刷新ListView。但是在加载通话记录时还有一些特别的处理。特别是在绑定Listview时涉及到了一些复杂的操作。
显示通话记录由CallLogFragment.java文件进行处理。在加载通话记录的过程中还用到了CallLogQueryHandler查询通话记录数据库,CallLogAdapter填充ListView。
查询通话记录的过程大致如下:
其中CallLogQueryHandler继承自CursorQueryHandler,该类是执行异步数据库查询的常用类。以上过程非常的常见,大部分程序都和该流程类似。
1、在CallLogFragment的onCreateView中设置RecyclerView的adapter,并调用fetchCalls查询数据。
2、当查询完成后会回调到CallLogFragment的onCallsFetched,并调用changeCursor,registerContentObserver和registerDataSetObserver,数据发生改变的时候,调用RecyclerView.Adapter的notifyDataSetChanged就会根据新的cursor rebind and relayout所有可见的view。
二.关键是在CallLogAdapter 的bindView函数中执行了很多难以理解的操作。通过查看通话记录的显示内容知道,在通话记录中显示了拨打电话的号码,通话记录的类型,该号码所对应的联系人(如果号码已加入联系人)等。当挂断电话时会查询联系人数据库查找所拨打的电话对应的联系人姓名,并把这些信息写入通话记录数据库中。因此在通话记录的数据库中有一列用于存储联系人姓名。但是通话记录中的联系人名称和联系人数据库中的名称有时候是不一致的,通话记录中的联系人名称只是一个缓存。考虑这样一种情况:当和张三打完电话后,立刻把联系人中的张三改为张三三,然后查看通话记录会发现通话记录也会变成张三三,而通话记录数据库中仍然是张三。
为了使通话记录中的显示名称和联系人中的一致,在显示通话记录时,需要查询每个电话号码对应的联系人信息。为了提高效率,在CallLogAdapter 中用CallLogAdapterExpirableCache数据结构缓存查询到的联系人信息,这样当界面刷新时不需要再次执行查询操作。由于数据库查询操作比较耗时,在bindView中不可能等到查询结果返回之后再显示界面。所以在bindView时采用了这样的处理策略:首先显示通话记录中缓存的联系人信息,然后开启一个线程查询联系人信息,并把这些信息缓存起来。该线程的优先级比较低,它会等cup空闲时执行查询联系人的操作。
如果联系人中的信息和通话记录数据库中的信息不一致(其实这种情况比较少见),则发送消息给CallLogAdapter重新执行显示操作。具体的流程如下:
该时序图只是展现了主要的框架,忽略了很多细节。
其中QueryThead负责查询联系人信息,得到联系人信息缓存到ExpirableCache中,用于Bindview刷新通话记录。