Andrew G
Andrew G

Reputation: 1557

really showing java outputstream progress and timeouts

I am having what feels like should be a solved problem. An Android application I'm writing sends a message much like SMS where a user can attach a file. I'm using an HttpUrlConnection to send this data to my server which basically boils down to a java.io.OutputStream (I'm wrapping it in a DataOutputStream).

Being on a mobile device, sometimes network connectivity can be downright terrible and a send may take way too long. I have the following two fundamental problems:

  1. The user has no way of knowing the progress of the upload
  2. If the network is terrible and progress abysmal - I'd rather just abort or have some reasonable timeout rather than sit there and try for 5-10 minutes.

Problem 1:

I have tried to show upload progress based on my outputstream write() calls which I'm doing with 4K buffers:

        buffer = new byte[4096];
        long totalBytes = 0;
        while ((bytesRead = fis.read(buffer)) > -1) {
            totalBytes += bytesRead;
            dos.write(buffer, 0, bytesRead);
            if(showProgress){
               updateProgressBar(totalBytes);
            }
        }

While this shows me progress, it seems it just shows me how fast the app can transfer the file buffer to the OS network stack buffer. The progress bar finishes very quickly even on slow network and then sits there for another large amount of time before I finally get the JSON back from my server telling me the status of the send. Surely there is some way to get some progress from the time I pass it to the OS to the time my server tells me it received it?

Problem 2:

Sometimes network connectivity is bad but not bad enough that the hardware radio triggers the callback for no connection found (in this case I go into an offline mode). So when it's bad but not off my app will just sit there at a sending dialog until the cows come home. This is connected to problem 1 in that I need to somehow be aware of the actual throughput since OutputStream doesn't provide a timeout mechanism natively. If it fell below some threshhold I could cancel the connection and inform the user that they need to get somewhere with decent reception.

Side Note: Asynchronous send / output queue is not an option for me because I cannot persist a message to disk and therefore cannot guarantee the drafted message is indefinitely in case it fails to send at some later point. I need/want to block on send, I just need to be smarter about giving up and/or informing the user about what is going on.

Upvotes: 2

Views: 2071

Answers (1)

user207421
user207421

Reputation: 310936

it seems it just shows me how fast the app can transfer the file buffer to the OS network stack buffer.

It's worse than that. It shows you how fast the app can transfer your data into the HttpURLConnection's internal ByteArrayOutputStream, which it is writing to so it can see the content length and set the header before writing any content.

Fortunately it's also better that than. If you know in advance how long the data is, set fixed-length transfer mode. If you don't, set chunked transfer mode with a lowish chunk size like 1024.

You will then be seeing how quickly your application can move data into the socket send buffer; in the case of chunked transfer mode, in units of the chunk size. However once the socket send buffer fills up your writes will then block and you will be seeing actual network transfers, at least until you have done the last write. Writing and closing are both asynchronous from that point on, so your display will pop down earlier, but everybody has that problem.

Re problem 2, once the transfer has settled down to network speed as above you can then compute your own throughput and react accordingly if it is poor.

Upvotes: 1

Related Questions