通话记录分析 --- CallLogAdapter交互

1.3 和CallLogAdapter交互

Fragment和Adapter交互的架构如下,


监听器Listener是CallLogQueryHandler的内部接口,仅有2个方法,

public interface Listener {
    /** Called when {@link CallLogQueryHandler#fetchVoicemailStatus()} completes. */
    void onVoicemailStatusFetched(Cursor statusCursor);
    /**
       * Called when {@link CallLogQueryHandler#fetchCalls(int)} complete.
       * Returns true if takes ownership of cursor.
       */
    boolean onCallsFetched(Cursor combinedCursor);
}

mListener是一个WeakReference组,

private final WeakReference<Listener> mListener;

在CallLogQueryHandler的构造方法中初始化,

public CallLogQueryHandler(Context context, ContentResolver contentResolver, Listener listener,
            int limit) {
   super(contentResolver);
   mContext = context.getApplicationContext();
   mListener = new WeakReference<Listener>(listener);
   mLogLimit = limit;
}

CallLogFragment

CallLogFragment的onCallsFetched方法主要逻辑如下,

mAdapter.changeCursor(cursor);

mAdapter 是CallLogAdapter对象,。

CallLogSearchFragment

CallLogSearchFragment是直接调用父类CallLogFragment的onCallsFetched方法。

MSimCallLogFragment

MsimCallLogFragment的onCallsFetched方法和CallLogFragment的onCallsFetched方法几乎完全相同,主要逻辑如下,

mAdapter.changeCursor(cursor);

CallLogAdapter继承于GroupingListAdapter, GroupingListAdapter定义如下,

abstract class GroupingListAdapter extends RecyclerView.Adapter {

其中, GroupingListAdapter实现了RecyclerView.Adapter 的getItemCount方法,

CallLogAdapter实现了RecyclerView.Adapter的onCreateViewHolder/ onBindViewHolder方法。

GroupingListAdapter的changeCursor方法调用流程图如下,


GroupingListAdapter的changeCursor方法主要逻辑如下,

1,取消注册,此时查询完成,需要在更新完界面之后才能受数据库的影响,

mCursor.unregisterContentObserver(mChangeObserver);
mCursor.unregisterDataSetObserver(mDataSetObserver);
mCursor.close();

2,为mCursor变量赋值,

mCursor = cursor;

3,调用findGroups方法对查询到的通话记录分组,

findGroups();

4,更新界面,重新注册数据库,

cursor.registerContentObserver(mChangeObserver);
cursor.registerDataSetObserver(mDataSetObserver);
mRowIdColumnIndex = cursor.getColumnIndexOrThrow("_id");
notifyDataSetChanged();

notifyDataSetChanged原理在此就不论述了,总之会调用getItemCount/onCreateViewHolder/ onBindViewHolder方法更新界面。

看通话记录界面,可以看到:

1,通话记录分为三类:今天,昨天,更早。如何分类的?

2,相邻的同一号码为一组显示。如何做到的?

查询完之后,会做两件事情,分组(分为今天,昨天以及更早),相邻的相同号码的通话记录分为一组。两件事情在两个不同的类中进行,但是同时进行。

CallLogAdapter的addGroups方法如下,

mCallLogGroupBuilder.addGroups(cursor);

mCallLogGroupBuilder是CallLogGroupBuilder对象,在CallLogAdapter的构造方法中初始化,

mCallLogGroupBuilder = new CallLogGroupBuilder(this);

CallLogGroupBuilder的addGroups方法主要逻辑如下,

1,首先清除上次查询的分组,并获取系统的当前时间,

mGroupCreator.clearDayGroups();
long currentTime = System.currentTimeMillis();

2,获取查询的第一条通话记录信息,

cursor.moveToFirst();
String firstNumber = cursor.getString(CallLogQuery.NUMBER);//通话号码
int firstCallType = cursor.getInt(CallLogQuery.CALL_TYPE); //通话类型
•••
final long firstDate = cursor.getLong(CallLogQuery.DATE);//通话时间
final long firstRowId = cursor.getLong(CallLogQuery.ID);

3,调用getDayGroup方法获取分组,然后调用CallLogAdapter的setDayGroup方法设置分组,

int currentGroupDayGroup = getDayGroup(firstDate, currentTime);
mGroupCreator.setDayGroup(firstRowId, currentGroupDayGroup);

getDayGroup 方法如下,

int days = DateUtils.getDayDifference(TIME, date, now);
if (days == 0) {
    return DAY_GROUP_TODAY;
} else if (days == 1) {
    return DAY_GROUP_YESTERDAY;
} else {
    return DAY_GROUP_OTHER;
}

将通话记录时间和当前的系统时间对比,按照时间信息分为3组,

时间小于一天的为一组,返回值为0;

时间超过一天但是未超过2天的为一组,返回值为1;

时间超过2天的为一组,返回值为2;

4,利用cursor信息进行逐个分组,

while (cursor.moveToNext()) {

分组的代码就不贴出来了,例如现在 有8条通话记录,按照情况分别如下,

A, 8条通话记录分别对应8个不同的号码,3条是今天的通话,2条是昨天的通话,3条是以前的通话。

首先利用通话信息调用getDayGroup方法获取时间分组,

currentGroupDayGroup = getDayGroup(date, currentTime);

然后调用CallLogAdapter的setDayGroup方法保存时间分组信息,

mGroupCreator.setDayGroup(currentCallId, currentGroupDayGroup);

CallLogAdapter的setDayGroup方法如下,

if (!mDayGroups.containsKey(rowId)) {
    mDayGroups.put(rowId, dayGroup);
}

将每条通话记录的时间分组信息保存在mDayGroups变量中, mDayGroups定义如下,

private HashMap<Long,Integer> mDayGroups = new HashMap<Long, Integer>();

因此, mDayGroups对应保存的信息为(0,0), (1,0), (2,0), (3,1), (4,1), (5,2), (6,2), (7,2),

B, 8条通话记录分别对应8个完全相同的号码,3条是今天的通话,2条是昨天的通话,3条是以前的通话。

如果号码相同的话,就可以进行合并,

currentGroupSize++; //初始值为1

然后调用CallLogAdapter的setDayGroup方法保存时间分组信息,

mGroupCreator.setDayGroup(currentCallId, currentGroupDayGroup);

最后调用addGroup方法记录合并信息,

addGroup(count - currentGroupSize, currentGroupSize);//参数为(0,8)

CallLogGroupBuilder的addGroup方法调用流程图如下,


CallLogGroupBuilder的addGroup方法如下,

mGroupCreator.addGroup(cursorPosition, size, false);

调用CallLogAdapter的addGroup方法保存分组信息。CallLogAdapter有一个addGroup方法,也有一个addGroups方法,容易混淆。

CallLogAdapter对应的addGroup方法如下,

super.addGroup(cursorPosition, size, expanded);

直接调用父类GroupingListAdapter的addGroup方法,在分析之前,首先看一些变量,

mGroupCount 是int类型的,是通话记录分组的数目,

mGroupMetadata是long型的数组,长度为64位,

private long[] mGroupMetadata;

在findGroups方法中,也就是查询通话记录之后,初始化如下,

mGroupCount = 0;   // mGroupCount 初始值为0
mGroupMetadata = new long[GROUP_METADATA_ARRAY_INITIAL_SIZE];

mGroupMetadata数组的初始大小为16

GroupingListAdapter的addGroup方法逻辑如下,

1,如果分组大小大于16,则将分组大小增加到128,

if (mGroupCount >= mGroupMetadata.length) {
   int newSize = idealLongArraySize(
      mGroupMetadata.length + GROUP_METADATA_ARRAY_INCREMENT);
   long[] array = new long[newSize];
   System.arraycopy(mGroupMetadata, 0, array, 0, mGroupCount);
   mGroupMetadata = array;
}

2,保存分组信息,高32位保存一个分组的起始id,低32位保存了一个分组的通话记录数目,

long metadata = ((long)size << 32) | cursorPosition;
if (expanded) {
  metadata |= EXPANDED_GROUP_MASK;
}
mGroupMetadata[mGroupCount++] = metadata;

如果8条通话记录完全相同,则高32位是0,低32位为8,记录为(0,8)

C, 8条通话记录,前3条号码相同,第5条和第6条相同,第7条和第8条相同。

执行逻辑如下,

由于前3条号码相同,因此在while循环中调用第4条通话记录信息时,会处理前面3条信息,

addGroup(cursor.getPosition() - currentGroupSize, currentGroupSize);

调用此方法之后, mGroupMetadata记录的值为(0,3)

当全部调用完成之后, mGroupMetadata记录的值为(0,3),(3,1),(4,2),(6,2)。

小结:

GroupingListAdapter的findGroups方法最后调用CallLogGroupBuilder的addGroups方法进行通话的时间,号码进行分组,并且分别保存在CallLogAdapter的mDayGroups变量和GroupingListAdapter的mGroupMetadata中。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值