javaandroidokhttpandroid-networkingandroid-internet

How to detect upload/download transfer rate in Android?


I am working on an app which uploads a large amount of data. I want to determine the transfer rate of the upload, to show in a notification.

I'm not satisfied with the answers in these posts, so I am asking again.

I've seen apps which display the upload transfer rate, as well as some custom ROMs like Resurrection Remix.

How can I determine the transfer rate of these uploads?


Solution

  • It is feasible to obtain the transferred traffic amount using android.net.TrafficStats. Here is an implementation of this idea which measures the up-stream and down-stream transfer rate. You can measure the rate of mobile network by passing TrafficSpeedMeasurer.TrafficType.MOBILE to the TrafficSpeedMeasurer constructor, otherwise using TrafficSpeedMeasurer.TrafficType.ALL will result in measuring general traffic (WiFi/Mobile). Also by setting SHOW_SPEED_IN_BITS = true in MainActivity you can change the unit of speed measuring to bits per second.

    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);
            }
        }
    
    }
    

    . Visual Result

    enter image description here