Android7.0 数据业务中的短连接
来源:互联网 发布:网络弊大于利总结陈词 编辑:程序博客网 时间:2024/06/06 04:33
数据业务中的短连接,是一种为了满足业务需求,临时建立起来的连接。当业务完成通信需求后,这种数据连接会被框架释放掉。与之相对,长连接一旦拨号成功就会一直存在下去,除非用户主动关闭或者终端受到网络等因素的影响导致连接不可用。
一种比较常见的例子,就是发送彩信时,终端将建立短连接;当彩信发送结束时,短连接将被释放掉。
在这篇博客中,我们就从彩信入手,看看Android中是如何建立和释放短连接的。
1 MmsService
Android中彩信相关的应用为MmsService,我们看看它AndroidManifest.xml中的部分片段:
<application android:label="MmsService" android:process="com.android.phone" android:usesCleartextTraffic="true"> <service android:name=".MmsService" android:enabled="true" android:exported="true"/></application>
容易看出MmsService是运行在Phone进程中的。在这篇博客中,我们不深入研究彩信服务的启动和收发彩信的过程,主要看看彩信如何建立和释放短连接。
在MmsService.java中,每一次发送彩信均会形成一个MmsRequest(抽象类,实现类为SendRequest和DownloadRequest),并将其加入到运行队列中:
private void addToRunningRequestQueueSynchronized(final MmsRequest request) { ........... final int queue = request.getQueueType(); //判读queue值的有效性 ............ mRunningRequestCount++; mCurrentSubId = request.getSubId(); mRunningRequestExecutors[queue].execute(new Runnable() { @Override public void run() { try { //MmsRequest执行 request.execute(MmsService.this, getNetworkManager(request.getSubId())); } finally { synchronized (MmsService.this) { mRunningRequestCount--; if (mRunningRequestCount <= 0) { //将位于pending队列中的请求,加入到running队列中 movePendingSimRequestsToRunningSynchronized(); } } } } }}
在上面对代码中,利用getNetworkManager创建了MmsRequest专属的MmsNetworkManger:
//subId对应于发送彩信的卡private MmsNetworkManager getNetworkManager(int subId) { synchronized (mNetworkManagerCache) { MmsNetworkManager manager = mNetworkManagerCache.get(subId); if (manager == null) { manager = new MmsNetworkManager(this, subId); mNetworkManagerCache.put(subId, manager); } return manager; }}
先看看MmsNetworkManager的构造函数:
public MmsNetworkManager(Context context, int subId) { ............ //构造出申请网络的request //注意Capability指定为MMS,同时NetworkSpecifier指定为对应的sub id mNetworkRequest = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS) .setNetworkSpecifier(Integer.toString(mSubId)) .build(); }
接下来,看看MmsRequest的execute方法:
public void execute(Context context, MmsNetworkManager networkManager) { .......... //检查是否做好准备工作 if (!ensureMmsConfigLoaded()) { ........ } else if (!prepareForHttpRequest()){ ........ } else { long retryDelaySecs = 2; for (int i = 0; i < RETRY_TIMES; i++) { try { //利用MmsNetworkManager建立短连接 networkManager.acquireNetwork(requestId); try { //进行实际的发送或接收 ............ } finally { //利用MmsNetworkManager释放段连接 networkManager.releaseNetwork(requestId); } } .........//进行捕获异常等操作 } //处理发送结果 processResult(context, result, response, httpStatusCode); }}
2 建立短连接
MmsNetworkManager中的acquireNetwork负责申请网络:
public void acquireNetwork(final String requestId) throws MmsNetworkException { synchronized (this) { mMmsRequestCount += 1; //若之前已经申请过网络,则不重新申请 if (mNetwork != null) { return; } if (mNetworkCallback == null) { //申请网络 startNewNetworkRequestLocked(); } //定义申请网络所应耗费的时间 final long shouldEnd = SystemClock.elapsedRealtime() + NETWORK_ACQUIRE_TIMEOUT_MILLIS; long waitTime = NETWORK_ACQUIRE_TIMEOUT_MILLIS; while (waitTime > 0) { try { //等待;申请网络后,ConnectivityService将调用MmsNetworkManager的回调函数,在回调函数进行通知 this.wait(waitTime); } catch (InterruptedException e) { ............ } if (mNetwork != null) { //成功获取到网络 return; } waitTime = shouldEnd - SystemClock.elapsedRealtime(); } //获取网络失败,释放请求 releaseRequestLocked(mNetworkCallback); throw new MmsNetworkException("Acquiring network timed out"); }}
顺着流程,看看MmsNetworkManager中的startNewNetworkRequestLocked:
private void startNewNetworkRequestLocked() { final ConnectivityManager connectivityManager = getConnectivityManager(); //定义回调函数 mNetworkCallback = new NetworkRequestCallback(); //利用ConnectivityManager向ConnectivityService发送请求,并注册回调函数 connectivityManager.requestNetwork( mNetworkRequest, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS);}
在进入connectivityManager前,先看看MmsNetworkManager中的回调函数:
private class NetworkRequestCallback extends ConnectivityManager.NetworkCallback { @Override public void onAvailable(Network network) { super.onAvailable(network); synchronized (MmsNetworkManager.this) { //网络可用时,存储并通知 //于是acquireNetwork函数,可以返回结果 mNetwork = network; MmsNetworkManager.this.notifyAll(); } } //网络断开和不可用时的回调函数 ............}
现在我们可以看看connectivityManager的requestNetwork函数:
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, int timeoutMs) { requestNetwork(request, networkCallback, timeoutMs, //根据网络能力返回type;在之前的版本中会返回TYPE_MOBILE_MMS,android7.0中返回TYPE_NONE inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));}public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, int timeoutMs, int legacyType) { sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, REQUEST, legacyType);}private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback networkCallback, int timeoutSec, int action, int legacyType) { ................... try { .............. synchronized(sNetworkCallback) { if (action == LISTEN) { //这种类型的Request仅用来监听网络的变化,无法申请实际的网络 ............. } else { //调用ConnectivityService的requestNetwork函数 networkCallback.networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType); } } } catch(RemoteException e) { ............ } ..............}
从上面的代码,可以看到申请网络还是需要依靠ConnectivityService:
@Overridepublic NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, Messenger messenger, int timeoutMs, IBinder binder, int legacyType) { //检查参数有效性 ............ //利用参数构造networkRequest及NetworkRequestInfo NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId()); NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, type); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri)); ....................}
ConnectivityService将调用handleRegisterNetworkRequestWithIntent处理EVENT_REGISTER_NETWORK_REQUEST:
private void handleRegisterNetworkRequestWithIntent(Message msg) { //检查msg携带Request的有效性 ............ handleRegisterNetworkRequest(nri);}private void handleRegisterNetworkRequest(NetworkRequestInfo nri) { mNetworkRequests.put(nri.request, nri); if (!nri.isRequest()) { //非申请网络的request ................ } //重新匹配所有的NetworkAgent和NetworkRequest rematchAllNetworksAndRequests(null, 0); //默认建立的长连接对应的NetworkAgent,其capabilities不包含MMS,可以看看DataConnection中的makeNetworkCapabilities函数 //因此无论数据业务开关是否打开,此时mNetworkForRequestId.get(nri.request.requestId)返回值均为null if (nri.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) { //发送request给所有的NetworkFactory,分数为0 sendUpdatedScoreToFactories(nri.request, 0); }}
与之前介绍数据拨号准备工作的博客中描述的一样,请求MMS网络的NetworkRequest将同时发送给TelephonyNetworkFactory和PhoneSwitcher中创建的NetworkFactory。
这里需要注意的是,TelephonyNetworkFactory申明自己的网络能力时,指定匹配对应卡的subId;
PhoneSwitcher中创建的NetworkFactory申明自己的网络能力时,指定匹配所有的subId。
于是,不匹配MMS NetworkRequest对应subId的TelephonyNetworkFactory,将无法处理该NetworkRequest。
此外,当TelephonyNetworkFactory处于激活态时,才能处理NetworkRequest。
综上所述,我们可以得出结论:
当具有数据能力的Phone发送彩信时,对应的TelephonyNetworkFactory可以直接处理;
当不具有数据能力的Phone发送彩信时,没有TelephonyNetworkFactory可以处理MMS Request;此时,必须先通过PhoneSwitcher切换Phone的数据能力,然后再进行处理,这个过程也被成为DDS。
我们来看看DDS的过程。在PhoneSwitcher中,负责处理NetworkRequest的函数为onRequestNetwork:
private void onRequestNetwork(NetworkRequest networkRequest) { //之前分析过,将NetworkRequest封装成DcRequest时,按照xml中的配置,定义了优先级 final DcRequest dcRequest = new DcRequest(networkRequest, mContext); if (mPrioritizedDcRequests.contains(dcRequest) == false) { mPrioritizedDcRequests.add(dcRequest); //MMS request的优先级高于default,于是会排在最前面 Collections.sort(mPrioritizedDcRequests); onEvaluate(REQUESTS_CHANGED, "netRequest"); }}private void onEvaluate(boolean requestsChanged, String reason) { .............. if (diffDetected) { List<Integer> newActivePhones = new ArrayList<Integer>(); int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest); if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; if (newActivePhones.contains(phoneIdForRequest)) continue; //MMS NetworkRequest的phone id加入到newActivePhones newActivePhones.add(phoneIdForRequest); if (newActivePhones.size() >= mMaxActivePhones) break; } ............. for (int phoneId = 0; phoneId < mNumPhones; phoneId++) { if (newActivePhones.contains(phoneId) == false) { //注销不匹配MMS NetworkRequest subId的phone的数据能力 //通过RIL下发命令,完成后通知观察者 deactivate(phoneId); } } for (int phoneId : newActivePhones) { //赋予MMS Request subId对应phone的数据能力 //通过RIL下发命令,完成后通知观察者,TelephonyNetworkFactory activate(phoneId); } }}
不论TelephonyNetworkFactory可以直接处理MMS NetworkRequest,还是DDS后才能进行处理,最终TelephonyNetworkFactory将调用DcTracker的requestNetwork函数。
之后,框架就会激活彩信对应的APN,并用彩信APN进行拨号。这部分流程与之前博客描述的,数据拨号的准备工作及数据长连接拨号基本一致,不再赘述。
3 释放短连接
MmsNetworkManager中的releaseRequestLocked负责释放短连接:
private void releaseRequestLocked(ConnectivityManager.NetworkCallback callback) { if (callback != null) { final ConnectivityManager connectivityManager = getConnectivityManager(); try { connectivityManager.unregisterNetworkCallback(callback); } catch (IllegalArgumentException e) { ............ } } ............}
进入到ConnectivityManager的unregisterNetworkCallback函数:
public void unregisterNetworkCallback(NetworkCallback networkCallback) { ............. try { mService.releaseNetworkRequest(networkCallback.networkRequest); } catch(RemoteException e) { ............ } ............}
随着流程进入到ConnectivityService:
@Overridepublic void releaseNetworkRequest(NetworkRequest networkRequest) { mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));}private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent, int callingUid) { NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent); if (nri != null) { handleReleaseNetworkRequest(nri.request, callingUid); }}private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) { NetworkRequestInfo nri = mNetworkRequests.get(request); if (nri != null) { ............. if (nri.isRequest()) { ............... for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { //释放每个NetworkAgent中对networkRequest的记录 if (nai.networkRequests.get(nri.request.requestId) != null) { nai.networkRequests.remove(nri.request.requestId); ........... //该NetworkAgent不再是任何NetworkRequest的最优匹配对象 if (unneeded(nai)) { //注销掉该NetworkAgent,调用AsyncChannel的disconnect函数,与之前博客所述的长连接去拨号流程一致 //MMS建立的NetworkAgent将在此处被注销 teardownUnneededNetwork(nai); } else { .............. } } } //移除ConnectivityService中NetworkAgentInfo的记录信息 NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); if (nai != null) { mNetworkForRequestId.remove(nri.request.requestId); } ............ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { //移除NetworkFactory中关于该NetworkRequest的记录 //该消息将通知给PhoneSwitcher中创建的NetworkFactory和TelephonyNetworkFactory nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, nri.request); } } else { ......... } ....... }}
当PhoneSwitcher中创建的NetworkFactory收到CMD_CANCEL_REQUEST消息后,最终将调用onReleaseNetwork进行处理:
private void onReleaseNetwork(NetworkRequest networkRequest) { final DcRequest dcRequest = new DcRequest(networkRequest, mContext); //移除MMS对应的DcRequest if (mPrioritizedDcRequests.remove(dcRequest)) { //重新评估所有的DcRequest //此时,由于高优先级的MMS DcRequest被移除了,于是Default data request成为了最优先的request //如果之前为了发送彩信进行过DDS,那么此时将重新进行DDS,将数据能力切换到原来的default subId对应的phone onEvaluate(REQUESTS_CHANGED, "netReleased"); }}
当TelephonyNetworkFactory收到CMD_CANCEL_REQUEST消息后,同样会调用自己的onReleaseNetworkFor函数进行处理:
private void onReleaseNetworkFor(Message msg) { //移除对NetworkRequest的记录 ............. if (mIsActive && isApplicable) { String s = "onReleaseNetworkFor"; localLog.log(s); log(s + " " + networkRequest); //调用DcTracker的releaseNetwork函数,将去激活MMS对应APN,同时移除MMS对应的dataConnection mDcTracker.releaseNetwork(networkRequest, localLog); } else { String s = "not releasing - isApp=" + isApplicable + ", isAct=" + mIsActive; localLog.log(s); log(s + " " + networkRequest); }}
注意到TelephonyNetworkFactory调用DcTracker的releaseNetwork函数有个前提条件,那就是该TelephonyNetworkFactory处于active状态。
考虑到ConnectivityService也发送过CMD_CANCEL_REQUEST给PhoneSwitcher,
因此可能由于时序上的原因,导致TelephonyNetworkFactory在执行onReleaseNetworkFor之前,PhoneSwitcher先进行了DDS。
此时TelephonyNetworkFactory将无法通过onReleaseNetworkFor来完成releaseNetwork的操作。
为了避免这个问题,TelephonyNetworkFactory监听了DDS变化的动作:
private void onActivePhoneSwitch() { final boolean newIsActive = mPhoneSwitcher.isPhoneActive(mPhoneId); if (mIsActive != newIsActive) { mIsActive = newIsActive; if (mIsDefault) { applyRequests(mDefaultRequests, (mIsActive ? REQUEST : RELEASE), logString); } applyRequests(mSpecificRequests, (mIsActive ? REQUEST : RELEASE), logString); }}private void applyRequests(HashMap<NetworkRequest, LocalLog> requestMap, boolean action, String logStr) { for (NetworkRequest networkRequest : requestMap.keySet()) { if (action == REQUEST) { mDcTracker.requestNetwork(networkRequest, localLog); } else { //同样调用了releaseNetwork mDcTracker.releaseNetwork(networkRequest, localLog); } }}
综上所述我们可以看到:
若PhoneSwitcher先进行了DDS,那么TelephonyNetworkFactory将通过applyRequests来调用releaseNetwork;然后在onReleaseNetworkFor中仅进行清除NetworkRequest的记录;
若TelephonyNetworkFactory先调用onReleaseNetworkFor,那么将清除NetworkRequest的记录并执行releaseNetwork;发生DDS后,由于已经清除过NetworkRequest的记录,于是不会重复执行releaseNetwork。
结束语
我们从MMS入手,分析了数据业务中短连接的建立和断开过程。
可以看到短连接主要利用ConnectivityManager的接口,来完成建立和断开的操作,同时在必要的时候利用PhoneSwitcher完成DDS。
之后短连接将依赖于长连接的建立和断开流程,完成实际的操作。
- Android7.0 数据业务中的短连接
- Android7.0 数据业务长连接拨号过程
- Android7.0 数据业务长连接去拨号过程
- Android7.0 数据业务基础类的创建
- android7.0+关闭wifi连接CA验证
- socket中的短连接与长连接
- socket中的短连接与长连接
- socket中的短连接与长连接
- java中的长连接和短连接
- 关于iOS中的短连接方法
- 网络中的长连接和短链接
- [Android6.0] 数据业务重试机制
- android7.0
- Web Service在电信行业数据业务中的应用
- 网络通信中的长连接和短连接
- HTTP协议中的长连接与短连接
- HTTP协议中的长连接与短连接
- socket中的长连接和短连接浅析
- RxJava和retrofit实现多线程下载
- 4.Java基础:集合中的遍历
- react native 学习实践----运行facebook官方提供的例子
- 移动UICollectionViewCell
- iOS--大文件断点下载
- Android7.0 数据业务中的短连接
- java 根据当前日期获取本周或上周日期区间
- 关于BCD编码和解码
- node.js简介
- c++ 设计模式之命令(Command)模式
- Leetcode113. Path Sum II
- 简单理解 分布式跟集群
- new install windows to do
- ACM常用算法分类