4

I'm trying to perform an AsyncTask class in my Android application that analyzes the network connection speed in for downloading and uploading. I'm working on the download portion now, but I'm not getting results I expect. I'm testing on a Wifi network that gets 15Mbps down/up speeds consistently, however, the results I'm getting from my application are more around barely 1Mbps. When I run the speed test apk on the device I'm testing on that gets around 3.5Mbps. The function works, just seems to be half the speed it should be. Should the following code produce accurate results?

try {
           String DownloadUrl = "http://ipv4.download.thinkbroadband.com:8080/5MB.zip";
           String fileName = "testfile.bin";
       
       
           File dir = new File (context.getFilesDir() + "/temp/");
           if(dir.exists()==false) {
                dir.mkdirs();
           }

           URL url = new URL(DownloadUrl); //you can write here any link
           File file = new File(context.getFilesDir() + "/temp/" + fileName);
           

           long startTime = System.currentTimeMillis();
           Log.d("DownloadManager", "download begining: " + startTime);
           Log.d("DownloadManager", "download url:" + url);
           Log.d("DownloadManager", "downloaded file name:" + fileName);
           
           /* Open a connection to that URL. */
           URLConnection ucon = url.openConnection();

           //Define InputStreams to read from the URLConnection.
           InputStream is = ucon.getInputStream();
           BufferedInputStream bis = new BufferedInputStream(is);

           //Read bytes to the Buffer until there is nothing more to read(-1).  
           ByteArrayBuffer baf = new ByteArrayBuffer(1024);
           int current = 0;
           while ((current = bis.read()) != -1) {
              baf.append((byte) current);
           }
           long endTime = System.currentTimeMillis(); //maybe

           /* Convert the Bytes read to a String. */
           FileOutputStream fos = new FileOutputStream(file);
           fos.write(baf.toByteArray());
           fos.flush();
           fos.close();
           
          File done = new File(context.getFilesDir() + "/temp/" + fileName);
          Log.d("DownloadManager", "Location being searched: "+ context.getFilesDir() + "/temp/" + fileName);
          double size = done.length();
          if(done.exists()) {
              done.delete();
          }

          Log.d("DownloadManager", "download ended: " + ((endTime - startTime) / 1000) + " secs");
          double rate = (((size / 1024) / ((endTime - startTime) / 1000)) * 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"); 
          Log.d("DownloadManager", "download speed: "+ratevalue);       
   } catch (IOException e) {
       Log.d("DownloadManager", "Error: " + e);
   }

Example output

10-08 15:09:52.658: D/DownloadManager(13714): download ended: 70 secs
10-08 15:09:52.662: D/DownloadManager(13714): download speed: 585.14 Kbps

Thanks in advance for the help. If there is a better method, please let me know.

Mike Keskinov
  • 11,614
  • 6
  • 59
  • 87
Joffroi
  • 79
  • 1
  • 2
  • 8
  • you are reading one byte at a time, this can be quite long. why not use a byte[] and read like several Kb at once ? – njzk2 Oct 08 '13 at 22:14
  • Typically, you have ~600K calls to `read` and `append` every second. – njzk2 Oct 08 '13 at 22:16
  • also, your rate value may not be very precise, because `size`, `endTime` and `startTime` are integer, as are 1024, 1000 and 8, and therefore your computation is made in integer before being casted to long (this should give imprecise mesure, but is not responsible for the large difference you observe) – njzk2 Oct 08 '13 at 22:21
  • My time values are set to longs and measured to the millisecond. @njzk2 I'll look into reading more bytes at time. I tested this a few times on other networks and I am appear to get closer results so maybe this is an issue with my home wifi. – Joffroi Oct 09 '13 at 17:46
  • in mean integer in the sense that the conversion to double is made after the computation is made, and therefore there is precision lost. However, I am fairly confident the reading method is the cause of the speed issue. – njzk2 Oct 09 '13 at 18:52
  • @njzk2 I'm really just picking up on java and not sure the correct syntax changes that are needed to correctly implement reading / writing multiple bytes. Is this a good example? http://stackoverflow.com/questions/5690954/java-how-to-read-an-unknown-number-of-bytes-from-an-inputstream-socket-socke – Joffroi Oct 09 '13 at 20:09
  • @Joffroi I'm trying built an application that performs speed test but I'm no way near to achieve results like Speedtest.net(OOKLA). Have you meet what you tried and if possible please guide me. – Mukeshkumar S Oct 06 '17 at 13:59
  • @Joffroi can you tell , how you will going for the upload speed ? – Harsh Gupta Jun 12 '21 at 05:34

1 Answers1

4

Following on my comments, here is an example of how to read several bytes from the stream

//Define InputStreams to read from the URLConnection.
InputStream is = ucon.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);

//I usually use a ByteArrayOutputStream, as it is more common.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int red = 0;
// This size can be changed
byte[] buf = new byte[1024];
while ((red = bis.read(buf)) != -1) {
    baos.write(buf, 0, red);
}

What this does is it reads into a byte[] buffer, and return the amount of read bytes. This is in turn written to the OutputStream, specifying the amount of bytes to write.

ByteArrayOutputStream also have a toByteArray that behaves similarly.

Alternatively, you can also write directly to the file, if you consider that the write to file operation is significantly faster than the read function :

// Simply start by defining the fileoutputstream
FileOutputStream fos = new FileOutputStream(file);
int red = 0;
// This size can be changed
byte[] buf = new byte[1024];
while ((red = bis.read(buf)) != -1) {
    // And directly write to it.
    fos.write(buf, 0, red);
}
long endTime = System.currentTimeMillis(); //maybe
// Flush after, as this may trigger a commit to disk.
fos.flush();
fos.close();

Moreover, if you really only care about the download speed, it is not mandatory to write to the file, or to anywhere, this would be sufficient :

long size = 0;
byte[] buf = new byte[1024];
while ((red = bis.read(buf)) != -1) {
    size += red;
}
njzk2
  • 38,969
  • 7
  • 69
  • 107
  • Sorry for not getting back sooner. I really appreciate the detail response. I tried both methods and ultimately went with the suggestion of not even writing the data size I'm really wanting to focus on just the download speed. Since the change to multiple bytes, my results have been must close to that or other speed test. Again, thanks again for the example, they worked perfectly. – Joffroi Oct 10 '13 at 15:20