前言:Google原生对Android系统的设计为,当用户接入一个可以正常上网的WIFI网络以后,会自动切断数据流量的链接,但是,若当用户接入了一个需认证的WIFI,如:机场或运营商提供的连接后需要进行登录验证身份的WIFI时,当跳转到认证界面(特定的webview界面)受安全限制而自动使用浏览器打开时,浏览器就会走数据通道而导致不能认证,而用户就会误以为在使用WIFI上网(因为此时的WIFI仍处于连接状态,但实际不能上网),或者用户接入了一个不能正常上网的WIFI,那么也会默认走数据流量通道,从而给用户造成一种误解,误以为在使用WiFi网络而造成流量费。
那么,如何实现当用户接入了一个非正常状态的WIFI时断开数据链接给防止用户不知情的情况下使用数据流量呢(注:连接正常的WIFI后数据链接是会自动断开的),在这里就需要先普及一个Android的网络评分机制了,Android会默认给各种网络状态一个初始化默认的分数,当有更高分数的网络就绪时,就将当前分值低的连接断开。而当当前网络被断开时,就寻找当前就绪的其他网络连接,选取分值高的进行接入。并且,每一个网络接入时,都会进行有效性检测,如果检测不通过,将会被扣掉一定分数,此时该网络的优先级也会相应的下降。
因为默认的分数wifi是高于数据连接的,所以当我们接入正常的wifi时,数据链接会自动断开,而关闭wifi时,网络又会自动切回到数据链接,但是,重点来了,是当我们接入正常wifi时,请注意上面红色部分,接入非正常wifi时由于系统去检测wifi状态时(源码中为ping一个指定的网站,看是否能ping通)返回了fail时wifi有效性检测不通过,故数据链接被切换回了数据流量,但是wifi仍处于链接状态,故,给用户造成了以为是在使用wifi的假象。
解决方案:主要思路是修改源码里面的评分机制的有效性检测,若连接了不可使用的wifi网络后,进行有效性检测时,不允许扣掉分数,那么网络通道就会一直处于wifi状态,而用户若是连接需要认证的wifi去上网时就会去到需要认证的界面,而若用户接入了不可使用的wifi,则用户无法上网,则可以手动关闭wifi功能而自行决定是否使用数据流量上网,请看修改后的源码:(注:类在源码中的地址为:frameworks/base/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java)
未修改前(省略部分源码):
private static final int UNVALIDATED_SCORE_PENALTY = 40;
private int getCurrentScore(boolean pretendValidated) {
......
int score = currentScore;
if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
score -= UNVALIDATED_SCORE_PENALTY;
}
if (score < 0) score = 0;
return score;
}
UNVALIDATED_SCORE_PENALTY:就是检测不通过扣掉的分数
getCurrentScore:该方法返回当前网络的分数
请看修改后的代码:
private static final int UNVALIDATED_SCORE_PENALTY = 40;
private int getCurrentScore(boolean pretendValidated) {
......
int score = currentScore;
if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
if("自定义条件,可用宏控制是否使用"){
score -= UNVALIDATED_SCORE_PENALTY;
}
}
if (score < 0) score = 0;
return score;
}
在这里,我们跳过了减分的语句,即保证,用户连接WIFI后即可关闭数据流量的连接,而断开WiFi后数据通道自动落回到数据流量,当然也可以修改UNVALIDATED_SCORE_PENALTY的值,但是个人不推荐这样改。
好了,到此结束,关于Android评分机制的详细流程大家可以去看源码里的 NetworkFactory、NetworkAgent 、NetworkMonitor和这些类相关的类。