【问题标题】:How to detect upload/download transfer rate in Android?如何检测 Android 中的上传/下载传输速率?
【发布时间】:2019-04-22 21:47:06
【问题描述】:

我正在开发一个上传大量数据的应用程序。我想确定上传的传输速率,以显示在通知中。

  • 一位post 建议使用不适用于移动数据的WifiInfo
  • 另一个post 建议获取网络类型来估计速度。

我对这些帖子中的答案不满意,所以我再次询问。

我见过显示上传传输速率的应用程序,以及一些自定义 ROM,例如 Resurrection Remix

如何确定这些上传的传输速率?

【问题讨论】:

  • 你用什么来上传数据(比如Retrofit,一个简单的socket等等)?
  • 你试过 CellInfoGsm 吗?
  • 你用什么来上传数据(比如Retrofit,一个简单的socket等等)?
  • 如果我得到答案,我愿意给 200+。通过一个虚拟问题或其他东西 - 最好不要告诉模组该计划

标签: java android okhttp android-networking android-internet


【解决方案1】:

使用android.net.TrafficStats获取转移的流量是可行的。这是这个想法的一个实现,它测量上游和下游传输速率。您可以通过将TrafficSpeedMeasurer.TrafficType.MOBILE 传递给TrafficSpeedMeasurer 构造函数来测量移动网络的速率,否则使用TrafficSpeedMeasurer.TrafficType.ALL 将导致测量一般流量(WiFi/Mobile)。此外,通过在MainActivity 中设置SHOW_SPEED_IN_BITS = true,您可以将速度测量单位更改为每秒bits。

MainActivity.java

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private static final boolean SHOW_SPEED_IN_BITS = false;

    private TrafficSpeedMeasurer mTrafficSpeedMeasurer;
    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.connection_class);

        mTrafficSpeedMeasurer = new TrafficSpeedMeasurer(TrafficSpeedMeasurer.TrafficType.ALL);
        mTrafficSpeedMeasurer.startMeasuring();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mTrafficSpeedMeasurer.stopMeasuring();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mTrafficSpeedMeasurer.removeListener(mStreamSpeedListener);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mTrafficSpeedMeasurer.registerListener(mStreamSpeedListener);
    }

    private ITrafficSpeedListener mStreamSpeedListener = new ITrafficSpeedListener() {

        @Override
        public void onTrafficSpeedMeasured(final double upStream, final double downStream) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    String upStreamSpeed = Utils.parseSpeed(upStream, SHOW_SPEED_IN_BITS);
                    String downStreamSpeed = Utils.parseSpeed(downStream, SHOW_SPEED_IN_BITS);
                    mTextView.setText("Up Stream Speed: " + upStreamSpeed + "\n" + "Down Stream Speed: " + downStreamSpeed);
                }
            });
        }
    };

}

TrafficSpeedMeasurer.java

import android.net.TrafficStats;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;


public class TrafficSpeedMeasurer {

    private ITrafficSpeedListener mTrafficSpeedListener;
    private SamplingHandler mHandler;

    private TrafficType mTrafficType;
    private long mLastTimeReading;
    private long mPreviousUpStream = -1;
    private long mPreviousDownStream = -1;

    public TrafficSpeedMeasurer(TrafficType trafficType) {
        mTrafficType = trafficType;
        HandlerThread thread = new HandlerThread("ParseThread");
        thread.start();
        mHandler = new SamplingHandler(thread.getLooper());
    }

    public void registerListener(ITrafficSpeedListener iTrafficSpeedListener) {
        mTrafficSpeedListener = iTrafficSpeedListener;
    }

    public void removeListener() {
        mTrafficSpeedListener = null;
    }

    public void startMeasuring() {
        mHandler.startSamplingThread();
        mLastTimeReading = SystemClock.elapsedRealtime();
    }

    public void stopMeasuring() {
        mHandler.stopSamplingThread();
        finalReadTrafficStats();
    }

    private void readTrafficStats() {
        long newBytesUpStream = (mTrafficType == TrafficType.MOBILE ? TrafficStats.getMobileTxBytes() : TrafficStats.getTotalTxBytes()) * 1024;
        long newBytesDownStream = (mTrafficType == TrafficType.MOBILE ? TrafficStats.getMobileRxBytes() : TrafficStats.getTotalRxBytes()) * 1024;

        long byteDiffUpStream = newBytesUpStream - mPreviousUpStream;
        long byteDiffDownStream = newBytesDownStream - mPreviousDownStream;

        synchronized (this) {
            long currentTime = SystemClock.elapsedRealtime();
            double bandwidthUpStream = 0;
            double bandwidthDownStream = 0;

            if (mPreviousUpStream >= 0) {
                bandwidthUpStream = (byteDiffUpStream) * 1.0 / (currentTime - mLastTimeReading);
            }
            if (mPreviousDownStream >= 0) {
                bandwidthDownStream = (byteDiffDownStream) * 1.0 / (currentTime - mLastTimeReading);
            }
            if (mTrafficSpeedListener != null) {
                mTrafficSpeedListener.onTrafficSpeedMeasured(bandwidthUpStream, bandwidthDownStream);
            }

            mLastTimeReading = currentTime;
        }

        mPreviousDownStream = newBytesDownStream;
        mPreviousUpStream = newBytesUpStream;
    }

    private void finalReadTrafficStats() {
        readTrafficStats();
        mPreviousUpStream = -1;
        mPreviousDownStream = -1;
    }

    private class SamplingHandler extends Handler {

        private static final long SAMPLE_TIME = 1000;
        private static final int MSG_START = 1;

        private SamplingHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_START:
                    readTrafficStats();
                    sendEmptyMessageDelayed(MSG_START, SAMPLE_TIME);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown what=" + msg.what);
            }
        }

        void startSamplingThread() {
            sendEmptyMessage(SamplingHandler.MSG_START);
        }

        void stopSamplingThread() {
            removeMessages(SamplingHandler.MSG_START);
        }

    }

    public enum TrafficType {
        MOBILE,
        ALL
    }

}

ITrafficSpeedListener.java

public interface ITrafficSpeedListener {

    void onTrafficSpeedMeasured(double upStream, double downStream);
}

Utils.java

import java.util.Locale;

public class Utils {

    private static final long B = 1;
    private static final long KB = B * 1024;
    private static final long MB = KB * 1024;
    private static final long GB = MB * 1024;

    public static String parseSpeed(double bytes, boolean inBits) {
        double value = inBits ? bytes * 8 : bytes;
        if (value < KB) {
            return String.format(Locale.getDefault(), "%.1f " + (inBits ? "b" : "B") + "/s", value);
        } else if (value < MB) {
            return String.format(Locale.getDefault(), "%.1f K" + (inBits ? "b" : "B") + "/s", value / KB);
        } else if (value < GB) {
            return String.format(Locale.getDefault(), "%.1f M" + (inBits ? "b" : "B") + "/s", value / MB);
        } else {
            return String.format(Locale.getDefault(), "%.2f G" + (inBits ? "b" : "B") + "/s", value / GB);
        }
    }

}

。 视觉效果

【讨论】:

  • 这为您提供了网络上所有流量的速率。这并不能帮助您确定特定 HTTP 请求的上传速率。
  • @TimMalseed:可以使用TrafficStats.getUidTxBytes (int uid)TrafficStats.getUidRxBytes (int uid) 测量特定应用的流量。您可以看到问题提到了一个名为Resurrection Remix 的自定义ROM,它在状态栏中显示了所有流量,他希望根据:“以及一些自定义ROM”。但是,感谢您的反对:)
  • 很高兴知道这样的答案,我只是对接受和支持它们作为“正确”答案持谨慎态度,因为它会误导经验不足的开发人员。如果目的是向用户显示特定上传的传输速率,而不是显示所有流量的传输速率,这在技术上是不正确的,并且可能非常不准确,具体取决于设备上发生的其他情况。不清楚的是 OP 对简单性而不是准确性感兴趣,这是可以理解的。
  • public void removeListener(ITrafficSpeedListener iTrafficSpeedListener) { mTrafficSpeedListener = iTrafficSpeedListener; } 不应该是mTrafficSpeedListener = null; 吗?
  • @aminography,我认为参数也不应该被传递。
【解决方案2】:

您要确定的是通过 HTTP 客户端上传的字节的传输速率。显然,这取决于您使用的 HTTP 客户端。

没有适用于 Android 上使用的所有 HTTP 客户端的开箱即用解决方案。 Android SDK 不提供任何方法让您确定特定上传的传输速率。

幸运的是,您使用的是 OKHttp, 是一种相对直接的方式来执行此操作。您将不得不实现一个自定义的RequestBody,并在请求进行时观察写入缓冲区的字节数。

在 OkHttp Github 上有一个这样做的“秘诀”: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java

您也可以参考这个 StackOverflow 问题来处理完全相同的主题: Tracking progress of multipart file upload using OKHTTP

这里还有一个: OKHTTP 3 Tracking Multipart upload progress

【讨论】:

  • 这不是和我一样的答案吗?
  • 我不这么认为,但我们都引用了相同的重复问题。这个问题确实应该关闭,但 OP 决定使用他们的赏金来防止问题被关闭,
  • @PembaTamang 您询问的是特定 HTTP 请求的上传速率,但您接受了一个答案,该答案为您提供了所有请求的上传速率,即使是那些不是由您的应用程序启动的上传。
【解决方案3】:

我是在您的应用上下文中讨论的,因为这样可以更轻松地捕获您上传数据的实时速度。您不需要任何额外的库或 sdk api。

您可能正在将数据以块的形式上传到服务器。所以

a) 你知道每个数据包的数据大小
b)您知道发送数据包之前/发送多个数据包之前的开始时间
c)您通过服务器响应知道 xy 数据包的结束时间,例如状态 200

这样你就有了计算上传速度的所有参数

double uploadSpeed = packet.size / (endTime - startTime) // time * 1000 to have it in seconds

编辑:

由于您使用的是来自OkHttpMultiPart,因此您可以监控上传的字节数。 Tracking progress of multipart file upload using OKHTTP。您可以将 packet.size 替换为当前上传的数量,endTime 将是 xy 秒的间隔。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-16
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    相关资源
    最近更新 更多