5

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.

  • One post suggests using the WifiInfo which will not work for mobile data.
  • Another post suggests getting the network type to estimate the speed.

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?

aminography
  • 21,986
  • 13
  • 70
  • 74
  • What do you use for uploading data (like Retrofit, a simple socket, etc.)? – Gergely Kőrössy Nov 28 '18 at 09:33
  • Have you tried CellInfoGsm ? – Sz-Nika Janos Nov 28 '18 at 09:36
  • Maybe this library helps: https://github.com/facebook/network-connection-class – leonardkraemer Nov 28 '18 at 10:09
  • What do you use for uploading data (like Retrofit, a simple socket, etc.)? – Gergely Kőrössy Nov 28 '18 at 10:33
  • @GergelyKőrössy I am using okhttp for send a multipart from data. – Pemba Tamang Nov 28 '18 at 10:38
  • @Sz-NikaJanos I am looking thanks for the tip, could you give me a link – Pemba Tamang Nov 28 '18 at 10:38
  • @leonardkraemer that is not working for me and its a passive way to get speed after the data is uploaded. I need realtime internet speed. – Pemba Tamang Nov 28 '18 at 10:38
  • I am willing to give 200+ if I get an answer. Through a dummy question or something. Cant seem to increase the bounty right now – Pemba Tamang Nov 28 '18 at 10:43
  • 2
    *I am willing to give 200+ if I get an answer. Through a dummy question or something* - better not tell the mods about that plan – Tim Nov 28 '18 at 10:53
  • that should be doable by default you know haha... –  Nov 28 '18 at 11:24
  • @Sz-NikaJanos where did your answer disappear?? I was trying that out! – Pemba Tamang Nov 28 '18 at 11:25
  • This is a duplicate of https://stackoverflow.com/questions/25962595/tracking-progress-of-multipart-file-upload-using-okhttp Not sure why you deem this as 'not satisfactory'. – Tim Malseed Nov 28 '18 at 12:14
  • forget that said I use okhttp. It's not the answer all the time. – Pemba Tamang Nov 28 '18 at 12:35
  • @TimMalseed to make things clear. I want to get the speed regarless of what I use. Like the custom rom in the link does, or various apps do. Never thought something that seems as trivial as internet speed would take so much work. – Pemba Tamang Nov 28 '18 at 12:53
  • To do that you'll need to keep track of the bytes written to your output stream. It's not a simple task, but there are plenty of existing solutions. There's no general or simple answer to this. It's low level HTTP client functionality. Your solution is going to vary based on the HTTP client you're using. So, 'OkHttp not the answer all the time' doesn't really make sense. To track bytes written to the output stream with OkHttp, see the link above. If you're interested in a solution for a different HTTP client, please let us know which client that would be. – Tim Malseed Nov 28 '18 at 13:44
  • @PembaTamang Do you want the speed only for your network communication (e.g. upload speed while uploading a file) or the whole device's (so other apps might have active network connections that account for some of the bandwidth used)? – Gergely Kőrössy Nov 28 '18 at 15:05
  • @TimMalseed turns out its not that complicated. Check out aminography's answer. – Pemba Tamang Dec 01 '18 at 04:06
  • TrafficStats isn't giving you the transfer rate of an individual download, so it's not really the correct answer to your original question. Sounds like it's the right compromise for ease of implementation that you were looking for though, so I'm glad you found the answer you wanted. – Tim Malseed Dec 02 '18 at 04:31
  • @TimMalseed you are welcome to give a better answer. – Pemba Tamang Dec 02 '18 at 14:32

3 Answers3

20

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

aminography
  • 21,986
  • 13
  • 70
  • 74
  • This gives you the rate of all traffic over the network. This doesn't help you determine the upload rate of a particular HTTP request. – Tim Malseed Dec 02 '18 at 04:37
  • 1
    @TimMalseed: It is possible to measure traffic of a specific app using `TrafficStats.getUidTxBytes (int uid)` and `TrafficStats.getUidRxBytes (int uid)`. You can see that the question mentioned a custom ROM named `Resurrection Remix` which shows all traffic rate in status bar and he wants it according to: **"as well as some custom ROMs"**. However, thank you for your down vote :) – aminography Dec 02 '18 at 05:09
  • Sure, but it's not possible to get the transfer rate of a particular HTTP request. Don't take it personally, it's just unfortunate that OP has not been very clear with the question and this is more of a compromise than a correct solution. I appreciate you detailing the possibilities of `TrafficStats`, and it is an interesting alternative. This answer _is_ useful though, and doesn't deserve to be downvoted, so I apologise for that. – Tim Malseed Dec 02 '18 at 05:14
  • 1
    It's good to be aware answers like this one, I'm just wary of accepting and upvoting them as the 'correct' answer because it misleads less experienced developers. If the intention is to display the transfer rate of a particular upload to the user, and instead they display the transfer rate of all traffic, that's not technically correct, and could be wildly inaccurate depending on what else is going on on the device. What wasn't clear is OP was interested in simplicity over accuracy, which is understandable. – Tim Malseed Dec 02 '18 at 05:18
  • I agree with you, the intention of this question is a bit ambiguous. However if you bypass its title, I think above approach would be acceptable. – aminography Dec 02 '18 at 05:27
  • this is working for me easy and direct but alternative answers are welcomed guys... – Pemba Tamang Dec 02 '18 at 14:34
  • @TimMalseed I am a beginner and I am on the clock so this works for me, this is what I wanted in terms of what resurrection remix does when showing the entire device's network speed. As per okhttp I found a library that does the job. Anyways if you have a better answer feel free to share. Sorry if I confused you and I have another question which I will post as soon as I have 500 reps. – Pemba Tamang Dec 02 '18 at 16:35
  • 1
    ```public void removeListener(ITrafficSpeedListener iTrafficSpeedListener) { mTrafficSpeedListener = iTrafficSpeedListener; }``` Shouldn't it be ```mTrafficSpeedListener = null;``` ? – Md. Ikramul Murad Jan 18 '21 at 08:13
  • @Md.IkramulMurad: You are right. Thank you for reporting. – aminography Jan 18 '21 at 09:48
  • 1
    @aminography, I think parameter shouldn't be passed too. – Md. Ikramul Murad Jan 18 '21 at 10:36
  • @aminography it has one bug. when I use `mTrafficSpeedMeasurer = new TrafficSpeedMeasurer(TrafficSpeedMeasurer.TrafficType.MOBILE);` It gives something negative no when mobile data set off and gives total from the boot when mobile data on again . other times it works fine. so please help me out! – Saurabh Dhage Jun 16 '21 at 10:19
  • This answer was quite useful to me even it's not what the OP exactly asked for. I wanted to have an App on my phone just like this which can just show me this info just to see why the hotspot I provided to windows machine is rapidly being consumed. Atleast for my test, I could isolatedly run those apps which needs my focus. – Thinker-101 May 10 '22 at 20:29
5

What you're trying to determine is the transfer rate of the bytes being uploaded over your HTTP Client. Obviously, this depends on the HTTP client you're using.

There's no out-of-the-box solution which applies to all HTTP clients used on Android. The Android SDK does not provide any methods for you to determine the transfer rate of a particular upload.

Fortunately, you're using OKHttp and there is a relatively straight-forward way to do this. You're going to have to implement a custom RequestBody, and observe the bytes being written to the buffer when the request is in flight.

There's a 'recipe' for doing this on the OkHttp Github: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java

You could also refer to this StackOverflow question dealing with the exact same topic: Tracking progress of multipart file upload using OKHTTP

Another here: OKHTTP 3 Tracking Multipart upload progress

Tim Malseed
  • 6,003
  • 6
  • 48
  • 66
  • Isn't this the same answer as mine? – Murat Karagöz Nov 29 '18 at 09:29
  • I don't think so, but we do both reference the same duplicate question. This question really should be closed, but OP has decided to use their bounty in order to prevent the question from being closed, – Tim Malseed Nov 29 '18 at 09:35
  • 1
    @PembaTamang you were asking for the upload rate of a particular HTTP request, but you've accepted an an answer which gives you the upload rate of ALL requests, even those uploads not started by your app. – Tim Malseed Dec 02 '18 at 04:39
0

I am talking in the context of your app since this makes it easier to capture the real time speed of your uploaded data. You don't need any extra libraries or sdk api's.

You are presumably uploading the data in chunks to the server. So

a) You know the data size of each packet
b) You know the start time before sending the packet / before sending multiple packets
c) You know the end time of xy packets by the server response e.g. status 200

With that you have all parameters to calculate the upload speed

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

EDIT:

Since you are using MultiPart from OkHttp you can monitor the amount of bytes uploaded. Tracking progress of multipart file upload using OKHTTP. You would replace packet.size with the current uploaded amount and the endTime would be an interval of xy seconds.

Murat Karagöz
  • 35,401
  • 16
  • 78
  • 107