Android 网络评分机制
在前两节简单介绍了连接管理的大致框架,数据链接的准备工作,包括APN的初始化与默认APN使能,DcTracker的构造,包括各种事件的注册等工作。但是数据链接的打开不止是只有用户主动去打开,Android可以提供数据业务的对象主要有,移动数据网络、WIFI、蓝牙、网线等,这些连接本身都可以独立使用,但是对于用户来说,每一时刻又最多只能使用一种方式接入网络,那么当这些功能同时打开时,比如即使用户打开了移动数据连接,但是又打开了wifi,那么只要wifi畅通,移动数据链接是不会用于上网的,那究竟如何选择最佳的接入环境呢?这就需要提供一个能够动态管理他们的打开与断开的功能,Android专门设计了一套管理方法来实现上面的这种机制,包括ConnectivityManager、ConnectivityService、NetworkAgent等对象之间的关系以及消息流走向,本节在这些知识的基础上介绍连接管理的核心机制,即连接管理中的评分机制,其中ConnectivityService是管理员身份,没种网络都会去向它注册,网络的使用权全靠它来分配。
连接管理通过一个评分机制来实现不同接入方式的选择。具体来说就是,每一种上网方式在初始化时,都向ConnectivityService标明自己网络的分值(比如数据连接50,WIFI60,蓝牙69,网线70),当有更高分数的网络就绪时,就将当前分值低的连接断开。而当当前网络被断开时,就寻找当前就绪的其他网络连接,选取分值高的进行接入。并且,每一个网络接入时,都会进行有效性检测,如果检测不通过,将会被扣掉一定分数,此时该网络的优先级也会相应的下降。下面我们利用三个部分来分析评分机制的原理:
1、NetworkFactory
2、NetworkAgent
3、NetworkMonitor
其中NetworkFactory是每一种网络持有一个,比如WIFI和Telephony会分别注册一个,但是NetworkAgent和NetworkMonitor是一种数据类型就会有一个,比如数据连接总的APN TYPE有8种,其中任意一种链接上之后都会各注册一个。
1、NetworkFactory
NetworkFactory直译就是网络工厂,开机之后每种网络都必须注册自己的NetworkFactory,NetworkFactory的作用是用来创建NetworkAgent,同时作为ConnectivityService与网络之间的通讯枢纽
private DctController(PhoneProxy[] phones) {for (int i = 0; i < mPhoneNum; ++i) {// Register for radio state changePhoneBase phoneBase = (PhoneBase)mPhones[i].getActivePhone();updatePhoneBaseForIndex(i, phoneBase);}
} private void updatePhoneBaseForIndex(int index, PhoneBase phoneBase) {phoneBase.getServiceStateTracker().registerForDataConnectionAttached(mRspHandler,EVENT_DATA_ATTACHED + index, null);phoneBase.getServiceStateTracker().registerForDataConnectionDetached(mRspHandler,EVENT_DATA_DETACHED + index, null);mNetworkFilter[index] = new NetworkCapabilities();mNetworkFilter[index].addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_IA);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);mNetworkFilter[index].addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);mNetworkFactory[index] = new TelephonyNetworkFactory(this.getLooper(),mPhones[index].getContext(), "TelephonyNetworkFactory", phoneBase,mNetworkFilter[index]);mNetworkFactory[index].setScoreFilter(50);mNetworkFactoryMessenger[index] = new Messenger(mNetworkFactory[index]);cm.registerNetworkFactory(mNetworkFactoryMessenger[index], "Telephony");
}
可以看出来一个NetworkFactory 支持多种网络类型(NetworkCapabilities),网络类型与APN的TYPE相对应。
以移动数据网络为例,TelephonyNetworkFactory 将会继承NetworkFactory ,并重写其中两个重要的方法,needNetworkFor和releaseNetworkFor,这两个方法就是ConnectivityService与移动网络之间桥梁,分别负责请求当前网络和断开当前网络。
private class TelephonyNetworkFactory extends NetworkFactory {protected void needNetworkFor(NetworkRequest networkRequest, int score) {// figure out the apn type and enable itif (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId())) {mPendingReq.put(networkRequest.requestId, networkRequest);return;}if (getRequestPhoneId(networkRequest) == mPhone.getPhoneId()) { DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker;String apn = apnForNetworkRequest(networkRequest);if (dcTracker.isApnSupported(apn)) {requestNetwork(networkRequest, dcTracker.getApnPriority(apn));}} else {mPendingReq.put(networkRequest.requestId, networkRequest);}} protected void releaseNetworkFor(NetworkRequest networkRequest) {if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId())) {mPendingReq.remove(networkRequest.requestId);return;}if (getRequestPhoneId(networkRequest) == mPhone.getPhoneId()) { DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker;String apn = apnForNetworkRequest(networkRequest);if (dcTracker.isApnSupported(apn)) {releaseNetwork(networkRequest);}}}
再看NetworkFactory 的注册cm.registerNetworkFactory(mNetworkFactoryMessenger[index], "Telephony");其中mNetworkFactoryMessenger是一个包装了mNetworkFactory的Messenger对象,这个主要是建立AsyncChannel通道时用。
@ConnectivityService.java
public void registerNetworkFactory(Messenger messenger, String name) {enforceConnectivityInternalPermission();NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel());mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
}
handleRegisterNetworkFactory处理EVENT_REGISTER_NETWORK_FACTORY消息
private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {mNetworkFactoryInfos.put(nfi.messenger, nfi);nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
}
在这里,ConnectivityService做了两个事情:
1、将新注册的NetworkFactoryInfo 保存到mNetworkFactoryInfos中;
2、利用刚才创建的AsyncChannel向NetworkAgent发起单向连接请求;
nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);即利用传入的Messenger对象建立起ConnectivityService与NetworkFactory的通讯通道,ConnectivityService后续的消息都将通过这个asyncChannel传入到数据网络中的NetworkFactory。
当asyncChannel通道建立成功后ConnectivityService会收到CMD_CHANNEL_HALF_CONNECTED消息。
@Override
public void handleMessage(Message msg) {NetworkInfo info;switch (msg.what) {case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {handleAsyncChannelHalfConnect(msg);break;}
}
private void handleAsyncChannelHalfConnect(Message msg) {AsyncChannel ac = (AsyncChannel) msg.obj;if (mNetworkFactoryInfos.containsKey(msg.replyTo)) { //此时是链接的是NetworkFactory,走这个pathif (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {// A network factory has connected. Send it all current NetworkRequests.for (NetworkRequestInfo nri : mNetworkRequests.values()) {if (nri.isRequest == false) continue;NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);}} else {mNetworkFactoryInfos.remove(msg.obj);}} else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {}
}
此时是链接的是NetworkFactory,走这个path,mNetworkFactoryInfos是在handleRegisterNetworkFactory时保存的。
在这里,ConnectivityService通过AsyncChannel通道向当前的NetworkFactory发起CMD_REQUEST_NETWORK的请求,需要注意的是,该请求所附带的第二个参数选择,由于当前处于初始化阶段,因此当前的mNetworkForRequestId中为空,也就是说此时传递的第二个参数必然为0。
我们接下来看NetworkFactory收到该请求时的处理:
@NetworkFactory.java
public void handleMessage(Message msg) {switch (msg.what) {case CMD_REQUEST_NETWORK: {handleAddRequest((NetworkRequest)msg.obj, msg.arg1);break;}}
}
private void handleAddRequest(NetworkRequest request, int score) {NetworkRequestInfo n = mNetworkRequests.get(request.requestId);if (n == null) {if (DBG) log("got request " + request + " with score " + score);n = new NetworkRequestInfo(request, score);mNetworkRequests.put(n.request.requestId, n);} else {n.score = score;}evalRequest(n);
}
接下来评估网络评分,是需要链接网络还是断开网路
private void evalRequest(NetworkRequestInfo n) {if (n.requested == false && n.score < mScore &&n.request.networkCapabilities.satisfiedByNetworkCapabilities(mCapabilityFilter) && acceptRequest(n.request, n.score)) {needNetworkFor(n.request, n.score);n.requested = true;} else if (n.requested == true &&(n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {releaseNetworkFor(n.request);n.requested = false;}
}
该逻辑就是整个网络评价系统最关键的地方,如果NetworkRequestInfo没有被requested过,并且其分值(n.score)小于当前NetworkFactory自己的分值(mScore),那么就说明,当前NetworkFactory所处的网络优先级高于其他网络的优先级,就会触发当前NetworkFactory所在网络的needNetworkFor()流程,也就是连接建立流程,并将标记NetworkRequestInfo.requested=true。
当NetworkRequestInfo被requested过(也就是当前网络被needNetworkFor过),此时如果再次收到请求,并且携带的新score大于当前NetworkFactory所处网络的mScore,那么就说明当前NetworkFactory所在网络优先级已经不是最高,需要将其releaseNetworkFor掉,并标记NetworkRequestInfo.requested=false。
evalRequest中调用TelephonyNetworkFactory 重写的needNetworkFor或者releaseNetworkFor,分别是链接网络和断开网络,后续的流程如下图(请求网络的情况)
这里写图片描述
在此数据链接的NetworkFactory算是创建完毕,并将自己注册到ConnectivityService中。
2、NetworkAgent
前面提到NetworkFactory是在系统初始化时就被创建,而NetworkAgent是在真正接入网络时才会创建,NetworkAgent的创建在DataConnection状态机里的DcActiveState状态时。
private class DcActiveState extends State {@Override public void enter() {// If we were retrying there maybe more than one, otherwise they'll only be one.notifyAllOfConnected(Phone.REASON_CONNECTED);mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED,mNetworkInfo.getReason(), null);mNetworkInfo.setExtraInfo(mApnSetting.apn);updateTcpBufferSizes(mRilRat);final NetworkMisc misc = new NetworkMisc();misc.subscriberId = mPhone.getSubscriberId();mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),"DcNetworkAgent", mNetworkInfo, makeNetworkCapabilities(), mLinkProperties,50, misc);}
}
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {super(looper);mContext = context;ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);
}
当网络链接完成之后,就会新建一个DcNetworkAgent,接着分析NetworkAgent的构造,和NetworkFactory类似,也是将自己注册到ConnectivityService中去,继续看registerNetworkAgent
public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,LinkProperties linkProperties, NetworkCapabilities networkCapabilities,int currentScore, NetworkMisc networkMisc) {NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),new NetworkInfo(networkInfo), new LinkProperties(linkProperties),new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,new NetworkMisc(networkMisc), mDefaultRequest);synchronized (this) {nai.networkMonitor.systemReady = mSystemReady;}mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
}private void handleRegisterNetworkAgent(NetworkAgentInfo na) {mNetworkAgentInfos.put(na.messenger, na);assignNextNetId(na);na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);NetworkInfo networkInfo = na.networkInfo;na.networkInfo = null;updateNetworkInfo(na, networkInfo);
}
在这里,ConnectivityService做了三个事情:
1、将新注册的NetworkAgentInfo保存到mNetworkAgentInfos中;
2、利用刚才创建的AsyncChannel向NetworkAgent发起单向连接请求;
3、更新最新的NetworkAgentInfo状态;
@Override
public void handleMessage(Message msg) {NetworkInfo info;switch (msg.what) {case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {handleAsyncChannelHalfConnect(msg);break;}
}
以上流程和NetworkFactory注册时几乎一模一样的模式
private void handleAsyncChannelHalfConnect(Message msg) {AsyncChannel ac = (AsyncChannel) msg.obj;if (mNetworkFactoryInfos.containsKey(msg.replyTo)) { } else if (mNetworkAgentInfos.containsKey(msg.replyTo)) { //此时是链接的是NetworkAgent,走这个pathif (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {// A network agent has requested a connection. Establish the connection.mNetworkAgentInfos.get(msg.replyTo).asyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);} }
}
唯一的区别是在handleAsyncChannelHalfConnect中这里,当ConnectivityService与NetworkAgent之间单向通道建立完成后,又发起了双向通道的请求,此时在NetworkAgent端,将会收到CMD_CHANNEL_FULL_CONNECTION的消息,建立双向通道的目的是,有时候网络也需要通过AsyncChannel向ConnectivityService发送消息。至此,NetworkAgent的初始化完毕。
现在的问题是NetworkAgent如何影响网络链接的?
NetworkAgent提供了两种方法更新评分管理:
1、sendNetworkScore
public void sendNetworkScore(int score) {queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score));
}
2、sendNetworkInfo
public void sendNetworkInfo(NetworkInfo networkInfo) {queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
}
先来分析第二种情况,比如移动数据网络的断开时就会调用此方法:
@DataConnection.java
private class DcActiveState extends State {public void exit() {mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,mNetworkInfo.getReason(), mNetworkInfo.getExtraInfo());mNetworkAgent.sendNetworkInfo(mNetworkInfo);mNetworkAgent = null;}
}
接着就会进入ConnectivityService
@Override
public void handleMessage(Message msg) {NetworkInfo info;case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);info = (NetworkInfo) msg.obj;updateNetworkInfo(nai, info);break;}
}
private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {if (state == NetworkInfo.State.CONNECTED && !networkAgent.created) {} else if (state == NetworkInfo.State.DISCONNECTED || state == NetworkInfo.State.SUSPENDED) {networkAgent.asyncChannel.disconnect(); }
由于是断开数据网络,因此这里是断开AsyncChannel,从而进入
AsyncChannel.CMD_CHANNEL_DISCONNECTED@Overridepublic void handleMessage(Message msg) {NetworkInfo info;switch (msg.what) {case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {handleAsyncChannelDisconnected(msg);break;}}
private void handleAsyncChannelDisconnected(Message msg) { NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); if (nai != null) { //删掉当前NetworkAgent对象 mNetworkAgentInfos.remove(msg.replyTo); final ArrayList<NetworkAgentInfo> toActivate = new ArrayList<NetworkAgentInfo>(); for (int i = 0; i < nai.networkRequests.size(); i++) { NetworkRequest request = nai.networkRequests.valueAt(i); NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId); if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) { mNetworkForRequestId.remove(request.requestId); //将0分更新到各个NetworkFactory中 sendUpdatedScoreToFactories(request, 0); } } }
}
private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0,networkRequest);}
}
在这里,由于当前连接是断开状态,因此其分值必然为0,这样就把他的0分值通知到各个NetworkFactory中,由NetworkFactory判断是否需要开启自己的网络,通知方法同样是CMD_REQUEST_NETWORK,也就是说,无论是直接更新NetworkAgent中的分数,还是更新NetworkAgent的状态,最终都会触发NetworkFactory中的评分机制。
3、NetworkMonitor
NetworkMonitor的构造是在注册NetworkAgent,构造NetworkAgentInfo是创建的,其实质ping网络是在updateNetworkInfo中,细节不分析,但是NetworkMonitor对网络可用性的评分是有影响的,即当网络链接上之后,会去ping当前网络是否可用,如果不可用则会影响getCurrentScore获取的分数值,getCurrentScore是每次网络评分获取的分数的必经之路:
private int getCurrentScore(boolean pretendValidated) {int score = currentScore;if (!everValidated && !pretendValidated) score -= UNVALIDATED_SCORE_PENALTY;if (score < 0) score = 0;if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;return score;
}
当一个网络连接建立时,系统将用该连接Ping一个Google的网站来判断该连接是否真的可以上网,如果不可以,那么就会扣掉该网络40分,从而可能导致该网络的评分低于其他网络评分
如果是用户指定了网络那么分数直接等于EXPLICITLY_SELECTED_NETWORK_SCORE(100分)
至此网络评分就分析完毕
链接:https://www.jianshu.com/p/541a21bf82d6
Android 网络评分机制相关推荐
- 【Android】Android网络评分机制简单总结
文章参考于: 三.Android 网络评分机制 - 简书在前两节简单介绍了连接管理的大致框架,数据链接的准备工作,包括APN的初始化与默认APN使能,DcTracker的构造,包括各种事件的注册等工作 ...
- android网络重试机制,okhttp源码解析(四):重试机制
前言 这一篇我们分析okhttp的重试机制,一般如果网络请求失败,我们会考虑连续请求多次,增大网络请求成功的概率,那么okhttp是怎么实现这个功能的呢? 正文 首先还是回到之前的Intercepto ...
- [RK3288][Android6.0] WiFi之NetworkFactory形成的评分机制
Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 NetworkFactory作为网络评分机制中一个重要角色而存在,每个模块实现需要继承Networ ...
- android网络的评分机制、连接国内ap wifi不回连问题
前言: 本文介绍了android下网络的评分机制,同时分析wifi连接国内ap时,重新打开wifi后,wifi不回连ap的问题,并提供解决方法. android下可以有多种网络存在,如:wifi.mo ...
- Android连接管理的评分机制(WIFI,Ethernet,BT,移动数据
Android可以支持:移动数据网络.WIFI.蓝牙.网线等,这些连接本身都可以独立使用,连接管理通过一个评分机制来实现不同接入方式的选择.具体来说就是,每一种上网方式在初始化时,都向Connecti ...
- (一百九十六)Android Q 学习WiFi的评分机制(三)
前言:之前在(一百九十六)Android Q 学习WiFi的评分机制(二)梳理了CS对WiFi score变化的处理,主要是rematchAllNetworksAndRequests方法中的处理,其中 ...
- Android P WiFi自动连接评分机制
1.WifiConnectivityManager的初始化 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiState ...
- Android 8.0/9.0 wifi 自动连接评分机制
今天了解了一下Wifi自动连接时的评分机制,总结如下: WifiConnectivityManager的初始化: /frameworks/opt/net/wifi/service/java/com/a ...
- Android Framework层播放器评分机制
本文涉及源码版本为:Oreo 8.0.0_r4 /frameworks/av/media/libmedia/mediaplayer.cpp /frameworks/av/include/media/m ...
- Android应用后台网络管控机制
应用后台网络管控机制 概述 在维护手管应用时,经常遇到与应用后台网络控制相关的问题,在解决这些问题的过程中,学习了下应用后台网络控制的流程以及一些日志的分析方法,现在把它总结一下,方便自己以及他人的学 ...
最新文章
- Spring Boot 多版本更新,紧急修复 RFD 安全漏洞
- 三菱d700变频器接线图_昆明市三菱恒压供水变频器接线图
- List Tuple Dictionary 区别
- Android --- Serializable 接口与 Parcelable 接口的使用方法和区别,怎么选择?
- python基础(part1)--注释/变量/del语句
- 有关Spring缓存性能的更多信息
- apache禁止多目录运行php文件下载,Nginx Apache下如何禁止指定目录运行PHP脚本
- C#在控制台工程中嵌入winform窗体
- 操作系统面试相关总结
- sql的内连接、左连接、右连接
- RK3288 Android7.1软件开发指南
- 离散小波变换wavedec matlab,MATLAB小波变换指令及其功能介绍(超级有用)
- C#程序设计--控制台程序输出上下三角形和菱形
- Sofa memcached client
- 详解vue中数据传递(父传子、子传父、兄弟之间以及vuex)代码附上
- 电脑xls图标未正常显示
- 实现外网Ping通WSL(网卡桥接方式实现)
- 标准成本还是实际成本 成本核算标准选择
- 跟我一起写 Makefile
- win10python安装配置selenium
热门文章
- SharePoint Designer中无法显示任何列表
- c语言的编译器还真是不好理解...
- IIS连接oralce数据提示“System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本”...
- android -------- 打开本地浏览器或指定浏览器加载,打电话,打开第三方app
- SharePoint 2013 创建web应用程序报错This page can’t be displayed
- 执行对象cocos2d-x 2.x action动作整理集合
- Object类的wait和notify详解
- redis 哨兵的原理
- gluoncv 目标检测,训练自己的数据集
- worldcloud库的使用