You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

开发测速App遇下载速度偏差问题,求优化方案及上传测速实现

Hey there! Let's tackle your speed test app issues step by step—both fixing the download speed discrepancy with Ookla Speedtest and adding upload testing, plus clearing up that TrafficStats confusion.


优化下载测速方案

Your current approach has a few key flaws that are causing the big gap with Ookla's results:

  • Bitmap decoding skews timing: BitmapFactory.decodeStream() doesn’t just download data—it processes it into a bitmap, which adds extra CPU time that shouldn’t count toward download speed.
  • Inaccurate data size: getContentLength() can return compressed file sizes if the server uses gzip, which means you’re calculating speed based on the wrong byte count.
  • Single small file test: Ookla uses multi-threaded downloads of large files over a sustained period (5-10 seconds) to saturate your connection. A single small bitmap download is way too volatile to reflect real bandwidth.
  • Including connection setup time: Your timer starts when you create the URL, but TCP handshake and connection setup delays aren’t part of actual download speed.

Here’s how to fix it:

1. Skip Bitmap Processing & Count Actual Bytes

Download raw bytes instead of decoding them, and track exactly how many bytes you receive. Also, start timing only after the connection is established:

public class OptimizedDownloadTest extends AsyncTask<String, Void, Long> {
    private long startTime;
    private long downloadDuration;

    @Override
    protected Long doInBackground(String... urls) {
        HttpURLConnection connection = null;
        InputStream inputStream = null;
        long totalBytes = 0;

        try {
            URL url = new URL(urls[0]);
            connection = (HttpURLConnection) url.openConnection();
            
            // Disable caching to avoid cached results skewing tests
            connection.setRequestProperty("Cache-Control", "no-cache, no-store, must-revalidate");
            connection.setRequestProperty("Pragma", "no-cache");
            connection.setRequestProperty("Expires", "0");
            
            // Connect first, then start timing when data starts flowing
            connection.connect();
            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                return null;
            }

            inputStream = connection.getInputStream();
            byte[] buffer = new byte[4096]; // 4KB buffer for efficient reading
            int bytesRead;
            
            startTime = System.currentTimeMillis();
            // Run test for 5 seconds (matches Ookla's typical duration)
            while ((bytesRead = inputStream.read(buffer)) != -1 && 
                   System.currentTimeMillis() - startTime < 5000) {
                totalBytes += bytesRead;
            }
            downloadDuration = System.currentTimeMillis() - startTime;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) inputStream.close();
                if (connection != null) connection.disconnect();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return totalBytes;
    }

    @Override
    protected void onPostExecute(Long totalBytes) {
        if (totalBytes != null && downloadDuration > 0) {
            double seconds = downloadDuration / 1000.0;
            double speedMbps = (totalBytes * 8) / (1024 * 1024 * seconds); // Convert bytes to Mbps

            // Update your UI elements
            fileSpeed.setText(String.format("%.2f Mbps", speedMbps));
            duration.setText(String.valueOf(downloadDuration) + " ms");
            filesize.setText(String.valueOf(totalBytes / 1024) + " KB");
        }
    }
}

2. Add Multi-Threaded Downloads

To mimic Ookla’s behavior, run multiple download tasks in parallel to saturate your connection. Use an ExecutorService to manage threads:

ExecutorService executor = Executors.newFixedThreadPool(3); // 3 concurrent downloads
executor.execute(new OptimizedDownloadTest());
executor.execute(new OptimizedDownloadTest());
executor.execute(new OptimizedDownloadTest());
executor.shutdown();

Average the results across all threads for a more accurate, stable reading.


实现上传测速

Upload testing works by sending large amounts of data to a server and timing the process. Here’s a basic implementation:

1. Create an Upload Test Task

public class UploadSpeedTest extends AsyncTask<String, Void, Long> {
    private long startTime;
    private long uploadDuration;

    @Override
    protected Long doInBackground(String... uploadUrls) {
        HttpURLConnection connection = null;
        OutputStream outputStream = null;
        long totalBytesSent = 0;

        try {
            // Generate a 10MB random payload (adjust size based on your needs)
            byte[] uploadData = new byte[10 * 1024 * 1024];
            new Random().nextBytes(uploadData);

            URL url = new URL(uploadUrls[0]);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/octet-stream");
            connection.setRequestProperty("Content-Length", String.valueOf(uploadData.length));

            // Connect first, then start timing when we begin sending data
            connection.connect();
            outputStream = connection.getOutputStream();
            
            startTime = System.currentTimeMillis();
            outputStream.write(uploadData);
            outputStream.flush();
            uploadDuration = System.currentTimeMillis() - startTime;

            // Verify server received the data
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                totalBytesSent = uploadData.length;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null) outputStream.close();
                if (connection != null) connection.disconnect();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return totalBytesSent;
    }

    @Override
    protected void onPostExecute(Long totalBytesSent) {
        if (totalBytesSent != null && uploadDuration > 0) {
            double seconds = uploadDuration / 1000.0;
            double speedMbps = (totalBytesSent * 8) / (1024 * 1024 * seconds);

            // Update UI with upload speed
            uploadSpeed.setText(String.format("%.2f Mbps", speedMbps));
        }
    }
}

Note: You’ll need a server endpoint that accepts large POST requests. For testing, you can set up a simple Node.js/PHP server that reads and discards incoming data, or use free public test endpoints (be mindful of rate limits).


关于TrafficStats的疑问

You’re partially right—TrafficStats doesn’t give real-time speed out of the box, but you can calculate it by polling traffic counts at intervals. Here’s how to track app-specific real-time speed:

private Handler speedUpdateHandler = new Handler();
private long previousRxBytes = 0;
private long previousTxBytes = 0;

private Runnable speedUpdateRunnable = new Runnable() {
    @Override
    public void run() {
        int appUid = getApplicationInfo().uid;
        long currentRxBytes = TrafficStats.getUidRxBytes(appUid);
        long currentTxBytes = TrafficStats.getUidTxBytes(appUid);

        // Calculate real-time download speed (Kbps)
        if (previousRxBytes != 0) {
            long rxDelta = currentRxBytes - previousRxBytes;
            double downloadSpeedKbps = (rxDelta * 8) / 1024.0;
            realtimeDownloadSpeed.setText(String.format("%.2f Kbps", downloadSpeedKbps));
        }

        // Calculate real-time upload speed
        if (previousTxBytes != 0) {
            long txDelta = currentTxBytes - previousTxBytes;
            double uploadSpeedKbps = (txDelta * 8) / 1024.0;
            realtimeUploadSpeed.setText(String.format("%.2f Kbps", uploadSpeedKbps));
        }

        previousRxBytes = currentRxBytes;
        previousTxBytes = currentTxBytes;

        // Update every 1 second
        speedUpdateHandler.postDelayed(this, 1000);
    }
};

// Start tracking when your test begins
speedUpdateHandler.post(speedUpdateRunnable);

// Stop tracking when done
speedUpdateHandler.removeCallbacks(speedUpdateRunnable);

This compares traffic counts every second to calculate real-time speed. Note: Some older devices don’t support per-UID traffic tracking, so handle cases where getUidRxBytes() returns -1.


内容的提问来源于stack exchange,提问作者Kaustubh Bhatter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:08:22