使用Java/Android测量下载速度

10
我正在开发一款Android应用程序,在其中需要尽可能准确地测量当前连接的下载速度。目前我找到的最佳方法是:启动计时器,从快速服务器下载Linux发行版,下载约200 kbytes,然后停止计时器并检查经过的时间和总下载字节数。
try{                
    InputStream is = new URL("http://www.domain.com/ubuntu-linux.iso").openStream();
    byte[] buf = new byte[1024];
    int n = 0;              
    startBytes = TrafficStats.getTotalRxBytes(); /*gets total bytes received so far*/
    startTime = System.nanoTime();
    while(n<200){
        is.read(buf);
        n++;                
    }
    endTime = System.nanoTime();
    endBytes = TrafficStats.getTotalRxBytes(); /*gets total bytes received so far*/
    totalTime = endTime - startTime;
    totalBytes = endBytes - startBytes;
}
catch(Exception e){
    e.printStackTrace();
}   
之后我只需要将传输的字节数除以所花费的时间,就可以得到以 bps 为单位的下载速度。

问题: 1. 这个方法准确吗? 2. 你知道更好的方法吗?

非常感谢。


你现在的设置方式不准确。InputStream.read 不能保证完全填充缓冲区。你需要检查返回值以查看实际读取了多少字节。 - Jeffrey
1
@Jeffrey,你没有仔细阅读代码。我不是依靠read()获取字节数。我是通过getTotalRxBytes()函数直接从操作系统获取它。 - Daniel Scocco
啊,我的错。我确实忽略了那一部分。你提供的解决方案会更或多或少地准确。 - Jeffrey
@Jeffrey,谢谢。如果你有任何想法可以让它更好,请告诉我。 - Daniel Scocco
3个回答

6
这里有几个可能存在的问题:
  1. 如果你想在任意设备上实时进行测试(而不是在实验室环境中),你需要遵循Jeffrey的建议,因为其他应用程序可能会消耗getTotalRxBytes()报告的带宽。

  2. 这测试了从此主机下载的速度。如果这是您要与之进行“真实交流”的主机,那很好。或者,如果您只是普通地需要一个下载速度的概念,那也没关系。但是,从A站点测试下载速度并假设它对B站点准确无误将是不可靠的,因为A站点和B站点甚至可能不在同一个大陆上。

  3. 如果您希望经常进行此操作,则您正在测试的主机的所有者可能会对带宽费用、过多的日志条目等感到轻微的烦恼。理想情况下,您只会针对自己拥有的东西进行此操作。

  4. 对于计量数据计划,200KB可能会使设备所有者感到烦恼。

  5. 所有关于互联网访问(例如,服务器可能已关闭)和移动设备(例如,用户可能从WiFi开始,然后移出范围,从而大大改变您的下载能力)的标准警告都适用。

尽管如此,进行下载是衡量下载速度的唯一真实方法。


1
关于第一点:“其他应用程序可能会消耗 getTotalRxBytes() 报告的带宽”,如果想要测量带宽,那么使用 getTotalRxBytes() 是可以的,因为如果其他应用程序消耗了带宽,你需要在计算中考虑到这一点。 - Chiara

3

使用下面的代码。我用它来测量下载速度。 为了测量下载速度,您不需要保存文件。您也可能不需要使用OkHttp。我之所以使用它,是因为它在我们的项目中被使用。

    String downloadURL = "http://test.talia.net/dl/1mb.pak";

    MediaType FILE = MediaType.parse("multipart/form-data;");

    OkHttpClient downloadClient = new OkHttpClient().newBuilder()
            .build();

    Request download = new Request.Builder()
            .url(downloadURL)
            .build();

    downloadClient.newCall(download).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {

        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if(response!=null) {
                long startTime = System.currentTimeMillis();
                InputStream is = response.body().byteStream();
                BufferedInputStream bis = new BufferedInputStream(is);
                long size = 0;
                int red = 0;
                byte[] buf = new byte[1024];
                while ((red = bis.read(buf)) != -1) {
                    size += red;
                }
                long endTime = System.currentTimeMillis();
                double rate = (((size / 1024) / ((endTime - startTime) / 1000.0)) * 8);
                rate = Math.round( rate * 100.0 ) / 100.0;
                String ratevalue;
                if(rate > 1000)
                    ratevalue = String.valueOf(rate / 1024).concat(" Mbps");
                else
                    ratevalue = String.valueOf(rate).concat(" Kbps");
                if(is!=null) {
                    is.close();
                }
                if(bis!=null) {
                    bis.close();
                }
                Log.d("download", "download speed = " + ratevalue);
            }
        }
    });

rate计算中使用1000.0而不是1000更加准确,因为(endTime - startTime) / 1000 = 1.82 = 1,而这0.82在我们的情况下非常重要。 - danyapd

0

有许多取样方法,没有一个“真正”的速度。您可以查看以下标准,以便更好地了解推荐的方式:

https://itu.int/en/ITU-T/C-I/Pages/IM/Internet-speed.aspx

https://itu.int/itu-t/recommendations/rec.aspx?rec=q.3960

https://itu.int/ITU-T/recommendations/rec.aspx?rec=14125

https://tools.ietf.org/pdf/rfc6349.pdf

您提供的代码使用1个线程测试速度。这不是推荐的方式,因为它无法充分利用互联网连接的全部容量。大多数标准建议使用多线程测试。

您还可以使用我们的Android SDK,它应该能够满足您的需求,并符合ITU标准:

https://github.com/speedchecker/speedchecker-sdk-android


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接