Android 获取实时网速实现详解

正文

最近接到个需求,需要计算WebView加载网页时的网速。查询了一下,Android没有提供直接获取网速的Api,但是提供了获取流量的类TrafficStats。本文介绍如何使用Trafficstats来实现获取网速功能。

TrafficStats简介

TrafficStats提供了一些获取设备从本次开机到目前为止传输/接收的流量的接口,如下:

方法参数说明
getTotalTxBytes-获取设备本次开机到目前为止,WI-FI、流量下传输的字节总数。
getTotalRxBytes-获取设备本次开机到目前为止,WI-FI、流量下接收的字节总数。
getMobileTxBytes-获取设备本次开机到目前为止,流量下传输的字节总数。
getMobileRxBytes-获取设备本次开机到目前为止,流量下接收的字节总数。
getUidTxBytesuid获取应用从本次开机到目前为止,WI-FI、流量下传输的字节总数。
getUidRxBytesuid获取应用从本次开机到目前为止,WI-FI、流量下接收的字节总数。

上述接口可以满足实现计算网速的需求,TrafficStats类其他接口可以查看官方文档

实现获取网速

可以通过一段时间内传输的流量除去时间计算出上行网速,通过一段时间内接收的流量除去时间计算出下行网速。

TrafficStats类的接口获取的网速是从开机时就开始计算的,因此,要计算一段时间内的流量需要在开始时获取一次流量数据,结束时获取一次流量数据,相减得出一段时间的实际流量。

实时网速

本文用getUidTxBytesgetUidRxBytes来演示,其他方法也是类似的,如下:

object NetSpeedUtils {
    var netSpeedCallback: NetSpeedCallback? = null
    private var timer: Timer? = null
    private var timerTask: TimerTask? = null
    private var lastTotalReceiveBytes: Long = 0
    private 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.UNSUPPORTED
        return 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.UNSUPPORTED
        return 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
    @SuppressLint("SetTextI18n")
    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/")
    }
    @SuppressLint("JavascriptInterface", "SetJavaScriptEnabled")
    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()
    }
}

效果如图:

作者:ChenYhong

%s 个评论

要回复文章请先登录注册