4.2 通话信息查询
通常情况下,如果是一个陌生号码,则会显示为该陌生号码。如果是已知联系人,
则会显示该联系人的名称,当然,还会显示归属地等其他信息。如下图,
无论来电,去电还是状态更新,最后都会调用InCallPresenter的onCallListChange方法,并且在onCallListChange方法中
调用startOrFinishUi 方法显示界面之后,
newState = startOrFinishUi(newState);
会调用所有监听器的onStateChange 方法,让各个Presenter更新显示界面,
for (InCallStateListener listener : mListeners) {
Log.d(this, "Notify " + listener + " of state " + mInCallState.toString());
listener.onStateChange(oldState, mInCallState, callList);
}
和上图通话信息有关的是CallCardPresenter,在CallCardPresenter的onStateChange方法中,会调用maybeStartSearch方法查询信息。
4.2.1 查询过程
CallCardPresenter的onStateChange方法调用流程图如下,
CallCardPresenter的maybeStartSearch方法如下,
private void maybeStartSearch(Call call, boolean isPrimary) {
// no need to start search for conference calls which show generic info.
if (call != null && !call.isConferenceCall()) {
startContactInfoSearch(call, isPrimary, isGeocoderLocationNeeded(call));
}
}
输入的是Call对象和boolean值,如果call对象不为空并且不是会议电话,才调用startContactInfoSearch方法查询信息。
startContactInfoSearch方法主要逻辑如下,
1,首先调用ContactInfoCache的getInstance获取对象,
final ContactInfoCache cache = ContactInfoCache.getInstance(mContext);
看来ContactInfoCache 也是一个单例模式,在整个dialer进程中只有一个。
2,调用ContactInfoCache的findInfo方法,
cache.findInfo(call, isIncoming, new ContactLookupCallback(this, isPrimary));
在此过程中,构造了一个ContactLookupCallback对象, ContactLookupCallback是CallCardPresenter的static内部类,
实现了ContactInfoCacheCallback接口,一共有2个回调方法,
onContactInfoComplete | 通话信息查询完成之后回调 |
onImageLoadComplete | 图像查询完成之后回调 |
查询的策略是:首先进行异步查询,查询完成之后将结果进行封装回调。这也是 android常用的开发方法。
ContactInfoCache的findInfo方法逻辑如下,
1,根据Call对象在mInfoMap中获取对应的查询信息结果,在mCallBacks中获取对应的回调对象,
final String callId = call.getId();
final ContactCacheEntry cacheEntry = mInfoMap.get(callId);//通话信息结果
Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId); //回调对象
mInfoMap和mCallBacks定义如下,
private final HashMap<String, ContactCacheEntry> mInfoMap = Maps.newHashMap();
private final HashMap<String, Set<ContactInfoCacheCallback>> mCallBacks = Maps.newHashMap();
mInfoMap 中保存着每一路电话的相关信息,例如归属地;
mCallBacks 中保存着每一路电话的回调的集合。
2,如果cacheEntry不为空,说明已经保存上次查询的消息,就直接回调onContactInfoComplete方法,
callback.onContactInfoComplete(callId, cacheEntry);
3,如果回调callBacks集合不为空,直接将callback添加到callBacks中,直接返回,
if (callBacks != null) {
callBacks.add(callback);
return;
}
4,为每一路Call创建一个集合callBacks,然后将回调对象callback加入集合中,最后加入到mCallBacks中,
callBacks = Sets.newHashSet();
callBacks.add(callback);
mCallBacks.put(callId, callBacks);
5,最后调用CallerInfoUtils的getCallerInfoForCall方法进行异步查询,
final CallerInfo callerInfo = CallerInfoUtils.getCallerInfoForCall(
mContext, call, new FindInfoCallback(isIncoming));
同样地,也会创建一个FindInfoCallback对象进行回调处理, FindInfoCallback 是ContactInfoCache的内部类,
实现了CallerInfoAsyncQuery.OnQueryCompleteListener接口的onQueryComplete方法,该方法如下,
public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
findInfoQueryComplete((Call) cookie, callerInfo, mIsIncoming, true);
}
也就是说,一旦完成异步查询,就会回调onQueryComplete方法,然后调用findInfoQueryComplete方法,
findInfoQueryComplete方法后面再论述。
CallerInfoUtils的getCallerInfoForCall方法如下,
1,首先调用buildCallerInfo 方法封装call对象的信息,
CallerInfo info = buildCallerInfo(context, call);
2,利用CallerInfo对象,调用CallerInfoAsyncQuery的startQuery方法开始查询,
CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context, info, listener, call);
在论述CallerInfoAsyncQuery 之前,首先看下CallerInfoAsyncQuery的结构,
AsyncQueryHandler和 WorkerHandler都继承自Handler类,并且WorkerHandler是AsyncQueryHandler的内部类;
CallerInfoAsyncQueryHandler继承AsyncQueryHandler, CallerInfoWorkerHandler继承WorkerHandler,并且CallerInfoWorkerHandler
是CallerInfoAsyncQueryHandler的内部类, CallerInfoAsyncQueryHandler是CallerInfoAsyncQuery的内部类。
当然, CallerInfoAsyncQuery内部还有一个OnQueryCompleteListener接口监听器,用于回调。
CallerInfoAsyncQuery的startQuery方法主要逻辑如下,
1,构造数据库查询的URI,
final Uri contactRef = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
.appendPath(info.phoneNumber)
.appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
String.valueOf(PhoneNumberHelper.isUriNumber(info.phoneNumber))) .build();
2,构造CallerInfoAsyncQuery 对象,并调用allocate方法,
CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
c.allocate(context, contactRef);
allocate方法如下,
mHandler = new CallerInfoAsyncQueryHandler(context);
mHandler.mQueryContext = context;
mHandler.mQueryUri = contactRef;
构造内部类CallerInfoAsyncQueryHandler对象,并为其变量mQueryContext和mQueryUri赋值,实际上, CallerInfoAsyncQuery
只是一个大空壳,实际的查询任务由CallerInfoAsyncQueryHandler 和 CallerInfoWorkerHandler完成。
3,构造CookieWrapper变量并赋值,
CookieWrapper cw = new CookieWrapper();
cw.listener = listener; //保存回调对象
cw.cookie = cookie; //保存 Call对象
cw.number = info.phoneNumber; // 保存号码,例如 10086
// event 用于保存号码的类型,总共有3中情况
if (PhoneNumberUtils.isLocalEmergencyNumber(context, info.phoneNumber)) {
cw.event = EVENT_EMERGENCY_NUMBER;
} else if (info.isVoiceMailNumber()) {
cw.event = EVENT_VOICEMAIL_NUMBER;
} else {
cw.event = EVENT_NEW_QUERY;
}
CookieWrapper只是CallerInfoAsyncQuery的一个简单的内部类,主要用于保存信息。
4,调用CallerInfoAsyncQueryHandler 对象的startQuery方法进行查询,
c.mHandler.startQuery(token, cw, // cookie
contactRef, // uri
null, // projection
null, // selection
null, // selectionArgs
null);
CallerInfoAsyncQueryHandler的startQuery方法只是简单的调用父类AsyncQueryHandler的startQuery方法。
有关AsyncQueryHandler 查询过程的原理,在此再啰嗦一次。
AsyncQueryHandler首先利用HandlerThread开启了一个线程,并且利用该线程的Looper构造WorkerHandler对象,
这样当AsyncQueryHandler (UI线程) 给mWorkerThreadHandler发送消息时,就可以切换到子线程中执行,执行完成之后
mWorkerThreadHandler就会给AsyncQueryHandler发送消息,切换到主线程中执行。
在此的逻辑就是, AsyncQueryHandler 首先利用HandlerThread开启了一个线程,并且利用该线程的Looper构造
CallerInfoWorkerHandler对象,实际查询时,是在CallerInfoWorkerHandler的子线程handleMessage方法中执行,
执行完成之后;会给AsyncQueryHandler 发送消息,回调CallerInfoAsyncQueryHandler的onQueryComplete方法。
首先看看CallerInfoWorkerHandler的handleMessage方法,该方法主要逻辑如下,
1,如果CookieWrapper 对象为空,直接调用父类的handleMessage方法进行处理,相当于什么也没做。在CallerInfoAsyncQuery
的startQuery方法中已经构造CookieWrapper 对象了,因此这样情况一般不会发生,
WorkerArgs args = (WorkerArgs) msg.obj;
CookieWrapper cw = (CookieWrapper) args.cookie;
if (cw == null) {
super.handleMessage(msg);
2,根据CookieWrapper对象的event变量分别处理,
如果是EVENT_NEW_QUERY类型,则进行数据库的查询,一般都是这种类型,
case EVENT_NEW_QUERY:
//start the sql command.
super.handleMessage(msg);
break;
如果是其他类型,不查询直接发送消息返回,
Message reply = args.handler.obtainMessage(msg.what);
reply.obj = args;
reply.arg1 = msg.arg1;
reply.sendToTarget();
下个小节主要论述查询信息的返回过程。