目录
- 正文
- TrafficStats简介
- 实现获取网速
- 实时网速
正文
最近接到个需求,需要计算WebView
加载网页时的网速。查询了一下,Android没有提供直接获取网速的Api,但是提供了获取流量的类TrafficStats
。本文介绍如何使用Trafficstats
来实现获取网速功能。
TrafficStats简介
TrafficStats
提供了一些获取设备从本次开机到目前为止传输/接收的流量的接口,如下:
方法 | 参数 | 说明 |
getTotalTxBytes | - | 获取设备本次开机到目前为止,WI-FI、流量下传输的字节总数。 |
getTotalRxBytes | - | 获取设备本次开机到目前为止,WI-FI、流量下接收的字节总数。 |
getMobileTxBytes | - | 获取设备本次开机到目前为止,流量下传输的字节总数。 |
getMobileRxBytes | - | 获取设备本次开机到目前为止,流量下接收的字节总数。 |
getUidTxBytes | uid | 获取应用从本次开机到目前为止,WI-FI、流量下传输的字节总数。 |
getUidRxBytes | uid | 获取应用从本次开机到目前为止,WI-FI、流量下接收的字节总数。 |
上述接口可以满足实现计算网速的需求,TrafficStats
类其他接口可以查看官方文档。
实现获取网速
可以通过一段时间内传输的流量除去时间计算出上行网速,通过一段时间内接收的流量除去时间计算出下行网速。
TrafficStats
类的接口获取的网速是从开机时就开始计算的,因此,要计算一段时间内的流量需要在开始时获取一次流量数据,结束时获取一次流量数据,相减得出一段时间的实际流量。
实时网速
本文用getUidTxBytes
和getUidRxBytes
来演示,其他方法也是类似的,如下:
object NetSpeedUtils { | |
var netSpeedCallback: NetSpeedCallback? = nullprivate var timer: Timer? = nullprivate var timerTask: TimerTask? = nullprivate var lastTotalReceiveBytes: Long = 0private var lastTotalTransferBytes: Long = 0/** | |
* 根据应用uid获取设备启动以来,该应用接收到的总字节数 | |
* | |
* @param uid 应用的uid | |
*/fun getTotalReceiveBytes(): Long { | |
var receiveBytes: Long = TrafficStats.UNSUPPORTED.toLong() | |
ExampleApplication.exampleContext?.run { | |
receiveBytes = TrafficStats.getUidRxBytes(applicationInfo.uid) | |
} | |
// 当获取不到时,会返回TrafficStats.UNSUPPORTEDreturn if (receiveBytes == TrafficStats.UNSUPPORTED.toLong()) 0 else receiveBytes / 1024 | |
} | |
/** | |
* 根据应用uid获取设备启动以来,该应用传输的总字节数 | |
* | |
* @param uid 应用的uid | |
*/fun getTotalTransferBytes(): Long { | |
var transferBytes: Long = TrafficStats.UNSUPPORTED.toLong() | |
ExampleApplication.exampleContext?.run { | |
transferBytes = TrafficStats.getUidTxBytes(applicationInfo.uid) | |
} | |
// 当获取不到时,会返回TrafficStats.UNSUPPORTEDreturn if (transferBytes == TrafficStats.UNSUPPORTED.toLong()) 0 else transferBytes / 1024 | |
} | |
// 通过Timer每隔1秒计算网速private fun calculateNetSpeed() { | |
ExampleApplication.exampleContext?.run { | |
val nowTotalReceiveBytes = getTotalReceiveBytes() | |
val nowTotalTransferBytes = getTotalTransferBytes() | |
val downloadSpeed = nowTotalReceiveBytes - lastTotalReceiveBytes | |
val uploadSpeed = nowTotalTransferBytes - lastTotalTransferBytes | |
lastTotalReceiveBytes = nowTotalReceiveBytes | |
lastTotalTransferBytes = nowTotalTransferBytes | |
netSpeedCallback?.onNetSpeedChange("$downloadSpeed kb/s", "$uploadSpeed kb/s") | |
} | |
} | |
fun startMeasuringNetSpeed() { | |
if (timer == null && timerTask == null) { | |
timer = Timer() | |
timerTask = object : TimerTask() { | |
override fun run() { | |
calculateNetSpeed() | |
} | |
} | |
timer?.run { timerTask?.let { schedule(it, 0L, 1000L) } } | |
} | |
} | |
fun stopMeasuringNetSpeed() { | |
timerTask?.cancel() | |
timerTask = null | |
timer?.cancel() | |
timer = null | |
} | |
interface NetSpeedCallback { | |
fun onNetSpeedChange(downloadSpeed: String, uploadSpeed: String) | |
} | |
} | |
// 示例类 | |
class TrafficStatsActivity : BaseGestureDetectorActivity() { | |
private lateinit var binding: LayoutTrafficStatsActivityBinding | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
binding = DataBindingUtil.setContentView(this, R.layout.layout_traffic_stats_activity) | |
binding.includeTitle.tvTitle.text = "TrafficStatsExample" | |
NetSpeedUtils.netSpeedCallback = object : NetSpeedUtils.NetSpeedCallback { | |
override fun onNetSpeedChange(downloadSpeed: String, uploadSpeed: String) { | |
binding.tvNetSpeed.run { post { text = "downloadSpeed:$downloadSpeed , uploadSpeed:$uploadSpeed" } } | |
} | |
} | |
binding.btnStartMeasureNetSpeed.setOnClickListener { | |
NetSpeedUtils.startMeasuringNetSpeed() | |
} | |
binding.btnStopMeasureNetSpeed.setOnClickListener { | |
NetSpeedUtils.stopMeasuringNetSpeed() | |
} | |
initWebViewSetting(binding.webView) | |
binding.webView.loadUrl("https://go.minigame.vip/") | |
} | |
private fun initWebViewSetting(webView: WebView?) { | |
webView?.run { | |
settings.cacheMode = WebSettings.LOAD_DEFAULT | |
settings.domStorageEnabled = true | |
settings.allowContentAccess = true | |
settings.allowFileAccess = true | |
settings.useWideViewPort = true | |
settings.loadWithOverviewMode = true | |
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW | |
settings.javaScriptEnabled = true | |
settings.javaScriptCanOpenWindowsAutomatically = true | |
settings.setSupportMultipleWindows(true) | |
} | |
} | |
override fun onDestroy() { | |
super.onDestroy() | |
binding.webView.clearHistory() | |
binding.webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null) | |
binding.root.run { | |
if (this is ViewGroup) { | |
this.removeView(binding.webView) | |
} | |
} | |
binding.webView.destroy() | |
} | |
} |
效果如图: