Downloading file with progress. Is this the right way of doing it? - android

Is there a more efficent way of doing this in terms of memory usage
and performance. The following method downloads a bitmap and calls
a function with the progress.
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
URLConnection connection = url.openConnection();
connection.connect();
int fileLength = connection.getContentLength();
InputStream input = new BufferedInputStream(url.openStream());
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
if(imageInterface != null) {
imageInterface.duringDownload(
imageView, ((int)total * 100 / fileLength));
}
outputStream.write(data, 0, count);
}
byte[] byteArray = outputStream.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
input.close();
outputStream.flush();
outputStream.close();
return bitmap;

Your time consuming tasks should not run on UI thread. Use an AsyncTask and update the UI from on onProgressUpdate method.
Increase your bucket size. At the moment you read 1024 byte chunks at a time and update UI after each read. For example for a 1MB image you refresh your UI 1024 times. This is inefficient, so if you increase buffer size you need to do less UI refreshes:
byte data[] = new byte[100 * 1024];

Also, I believe your method is error prone to OutOfMemoryException if it will try to load a large image. To fix this you'll need to scale down the bitmap before assigning it in memory.
Read this article if you are really concerned about efficiency: http://developer.android.com/training/displaying-bitmaps/index.html

Do something similar to the example given in the AsyncTask documentation:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
// Do not update UI here, only do downloading in background.
protected Long doInBackground(URL... urls) {
while (...) {
// do input.read() and outputStream.write() just like in your original code
// Use AsyncTask method to publish progress
publishProgress((int)total * 100 / fileLength);
}
}
// Here is where you use the progress value to update UI.
protected void onProgressUpdate(Integer... progress) {
imageInterface.duringDownload(
imageView, progress[0]);
}
}

Related

Java - Read from InputStream to ByteBuffer by chunk size

I am using Cronet API with our current API stack, specifically UploadDataProvider, there is a ByteBuffer with preset limit, seems like the limit size is fixed and we need to pass the data chunk by chunk. Our current API uses InputStream, and write chunk to OutputStream. We're using following code to work with infinite size of file:
byte[] buf = new byte[16 * 1024];
int bytesRead;
while ((bytesRead =inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
I'd like to achieve the same for this Cronet API, UploadDataProvider. My plan was in its read(UploadDataSink, ByteBuffer) method, whenever this read() method was called, read ByteBuffer's limit from inputStream, but my following code not working as expected.
public class MyUploadDataProvider extends UploadDataProvider {
private ReadableByteChannel byteChannel;
MyUploadDataProvider(InputStream inputStream) {
byteChannel = Channels.newChannel(inputStream);
}
#Override
public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) throws IOException {
boolean finalChunk = false;
int read = this.byteChannel.read(byteBuffer);
if (read == -1) {
finalChunk = true;
}
uploadDataSink.onReadSucceeded(finalChunk);
}
}
Not sure why it read failed, can anyone please help me fix this? Thanks!

read of Inputstream non stop when internet connection lost

I'a using a asynctask to download file. It works normally until i turn off wifi connection (there are no other internet connection) of my android, download dialog still and no changes. When i check by log, i discover that function read() of inputstream is non stop. So how to check this case? here is my code:
URL url = new URL(this.url);
URLConnection connection = url.openConnection();
connection.setReadTimeout(1000);
connection.connect();
// this will be useful so that you can show a typical 0-100% progress bar
int fileLength = connection.getContentLength();
fileName = "temp.zip";
// download the file
InputStream input = new BufferedInputStream(connection.getInputStream());
OutputStream output = new FileOutputStream(path+fileName);
byte buffer[] = new byte[1024000];
long total = 0;
int count;
Log.v("test download:","download in background");
while (((count = input.read(buffer)) != -1)) {
Log.v("test download:","read:"+count);
total += count;
publishProgress((int) (total * 100 / fileLength - 1));
output.write(buffer, 0, count);
}
Since you already set a timeout by calling setReadTimeout(), you should get a SocketTimeoutException shortly after the connection dropped.
Do your code happen to maybe capture this exception silently?
Your file download buffer size is too much byte buffer[] = new byte[1024]; is enough
when i try this Download a file with Android, and showing the progress in a ProgressDialog (top answer) with asynctask ,work fine, didn't get that problem

URLConnection.getContentLength() returns -1

I have a URL which, when I enter in browser, opens the image perfectly. But when I try the following code, I get getContentLength() as -1:
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// determine the image size and allocate a buffer
int fileSize = connection.getContentLength();
Please guide me what can be the reason behind this?
If the server is sending down the response using Chunked Transfer Encoding, you will not be able to pre-calculate the size. The response is streamed, and you'll just have to allocate a buffer to store the image until the stream is complete. Note that you should only do this if you can guarantee that the image is small enough to fit into memory. Streaming the response to flash storage is a pretty reasonable option if the image may be large.
In-memory solution:
private static final int READ_SIZE = 16384;
byte[] imageBuf;
if (-1 == contentLength) {
byte[] buf = new byte[READ_SIZE];
int bufferLeft = buf.length;
int offset = 0;
int result = 0;
outer: do {
while (bufferLeft > 0) {
result = is.read(buf, offset, bufferLeft);
if (result < 0) {
// we're done
break outer;
}
offset += result;
bufferLeft -= result;
}
// resize
bufferLeft = READ_SIZE;
int newSize = buf.length + READ_SIZE;
byte[] newBuf = new byte[newSize];
System.arraycopy(buf, 0, newBuf, 0, buf.length);
buf = newBuf;
} while (true);
imageBuf = new byte[offset];
System.arraycopy(buf, 0, imageBuf, 0, offset);
} else { // download using the simple method
In theory, if the Http client presents itself as HTTP 1.0, most servers will switch back to non-streaming mode, but I don't believe this is a possibility for URLConnection.
I am late here but this might help someone. I was facing same issue i was always getting -1 value, when ever i was trying get the content length.
previously i was using below method to get content length.
long totalByte=connection.getContentLength();
Below fixed my problem:-
long totalByte=connection.getHeaderFieldLong("Content-Length",-1);

Dowloading a file on android is larger then the size of the file

I'm downloading a video using the below code and maintaining a progress bar to show how much of the download has been completed.
ByteArrayBuffer baf = new ByteArrayBuffer((int)filesize);
long current = 0;
long notificationSize = filesize / 100 * 5;
int notifyCount = 0;
while ((current = inStream.read()) != -1)
{
baf.append((byte) current);
count += current;
//only process update once for each kb
if(count > notificationSize * notifyCount)
{
notifier.processUpdate(count);
notifyCount++;;
}
}
The issue i'm running into is the data being returned from the input stream adds up to be more than the file size. Meaning my progress bar completes before the download completes.
For example i'm download a video that has a file size of 1,849,655 bytes, but the count of the download adds to 228,932,955.
Android Progress bars use a percentage of how much of the process is complete. How do i know how much is complete if the total byte count from the download is more than the size of the file.
Worked out the issue.
When downloading and keeping track of the amount of data that has been downloaded do not use read() from the BufferedInputStream.
Instead use read(buffer, offset, length);
I also changed my code to write out the data to a file as i go instead of storing the data in memory and outputting once all data has come down.
byte[] baf = new byte[filesize];
int actual = 0;
int count = 0;
long notificationSize = filesize / 100 * 5;
int notifyCount = 0;
while (actual != -1)
{
//write data to file
fos.write(baf, 0, actual);
count += actual;
//only process update once for each kb
if(count > notificationSize * notifyCount)
{
notifier.processUpdate(count);
notifyCount++;;
}
actual = inStream.read(baf, 0, filesize);
}
I'm not really sure why read() shows it has read multiple bytes when read() is only meant to read a byte at a time.
If you really want to use read() change
count += current;
to
count++;
It's a rather inefficient way to download though as the number of loops in the while loop is much greater. After some brief performance testing seems slower to download as well (as it needs to write out to the file for each byte instead of a chunk of bytes).

HttpEntity.getContent() Progress Indicator?

Is there any way in android API 7+ that I can report on the progress of a call to HttpEntity.getContent()?
In my case I am getting an image in the response stream so the transfer can take quite a while. I want to have a ProgressDialog with the style set to STYLE_HORIZONTAL be updated with the progress of the transfer.
Any way to do this?
Thanks!
Have you tried HttpEntity.getContentLength() to help you predetermine the size of the file? If you use that in conjunction with something like AsyncTask's onProgressUpdate(), you should be able to implement that.
Take a look at this link
Download a file with Android, and showing the progress in a ProgressDialog
It has pretty much what you're looking for. it uses urlConnection to get the InputStream so you would need to adapt that to use HttpEntity. So maybe you'd have something like this.
#Override
protected String doInBackground(String... aurl) {
int count;
long contentLength = <yourHttpEntity>.getContentLength();
InputStream input = new BufferedInputStream(<yourHttpEntity>.getContent());
OutputStream output = new FileOutputStream("/sdcard/your_photo.jpg");
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress(""+(int)((total*100)/contentLength));
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
}
and update your dialog in onProgressUpdate.

Categories

Resources