hi to all
how can i make this code to wait until it finishes downloading the image
with what can i replace doInBackground(URL... paths) to make it wait to download then continue with the rest of the code
private class DownloadImageTask extends AsyncTask<URL, Integer, Bitmap> {
// This class definition states that DownloadImageTask will take String
// parameters, publish Integer progress updates, and return a Bitmap
protected Bitmap doInBackground(URL... paths) {
URL url;
try {
url = paths[0];
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
int length = connection.getContentLength();
InputStream is = (InputStream) url.getContent();
byte[] imageData = new byte[length];
int buffersize = (int) Math.ceil(length / (double) 100);
int downloaded = 0;
int read;
while (downloaded < length) {
if (length < buffersize) {
read = is.read(imageData, downloaded, length);
} else if ((length - downloaded) <= buffersize) {
read = is.read(imageData, downloaded, length
- downloaded);
} else {
read = is.read(imageData, downloaded, buffersize);
}
downloaded += read;
publishProgress((downloaded * 100) / length);
}
Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0,
length);
if (bitmap != null) {
Log.i(TAG, "Bitmap created");
} else {
Log.i(TAG, "Bitmap not created");
}
is.close();
return bitmap;
} catch (MalformedURLException e) {
Log.e(TAG, "Malformed exception: " + e.toString());
} catch (IOException e) {
Log.e(TAG, "IOException: " + e.toString());
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.toString());
}
return null;
}
protected void onPostExecute(Bitmap result) {
String name = ImageLink.substring(ImageLink
.lastIndexOf("/") + 1);
if (result != null) {
hasExternalStoragePublicPicture(name);
saveToSDCard(result, name);
isImage = true;
} else {
isImage = false;
}
}
}
doInBackground()) executes in the background. The part that waits for the download and continues with the rest of the code is onPostExecute(). This is the function you're probably asking for.
The point about AsyncTask is the main code in your Activity (which creates the AsyncTask) doesn't wait. Async is short for asynchronous - that means something which happens without a pre-determined time-frame.
If you want to have a download or multiple downloads complete before other code can execute then you either need to perform things in a synchronous manner (not good in Android Activities) or you need to write your code to wait on a callback.
Related
My app is a media player, it plays media by downloading the appropriate files from the Internet. I am using AsyncTask to do this, however the task takes longer to execute when multiple files need to be downloaded which results in a media player delay.
The desired behavior is to start playing a file after it has been downloaded while continuing to download any other files.
My code is as follows:
public class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
private PowerManager.WakeLock mWakeLock;
private String folder;
private ProgressDialog mProgressDialog;
private int noOfURLs;
private int noUrlLoad;
public DownloadTask(Context context, String folder, ProgressDialog mProgressDialog) {
this.context = context;
this.folder = folder;
}
#Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
noOfURLs = sUrl.length;
for (int i = 0; i < sUrl.length; i++) {
URL url = new URL(sUrl[i]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Máy chủ trả về HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
output = new FileOutputStream(Environment.getExternalStorageDirectory() + "/" + folder + "/File" + (i + 1) + "." + sUrl[i].charAt(sUrl[i].length() - 3) + sUrl[i].charAt(sUrl[i].length() - 2) + sUrl[i].charAt(sUrl[i].length() - 1));
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
noUrlLoad++;
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
}
#Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to false
}
#Override
protected void onPostExecute(String result) {
mWakeLock.release();
mProgressDialog.dismiss();
if (result != null)
Toast.makeText(context, context.getString(R.string.error) + result, Toast.LENGTH_LONG).show();
}
}
Inside your doInBackground method, call publishProgress(Integer) to send your updates to the UI thread. This will trigger the onProgressUpdate method to be called, and you'll be able to see when the first download has finished.
http://developer.android.com/reference/android/os/AsyncTask.html#publishProgress(Progress...)
I just implemented a HTTP Downloader. It has 4 button to do some operation: start, pause, resume, cancel download. When pressing pause or cancel button, I use AsyncTask.cancel() to cancel the download AsyncTask and disable all UI widget temporarily. When AsyncTask onCancelled triggers, I enable the UI widget.
Here's a question: I found inputStram.close() somehow is very slow sometimes. Cause it is called before onCancelled(), it will block UI for a while. I found some article discussed about it but none of their answer really work. It confuses me so bad...
Related questions:
Sometimes HttpURLConnection.getInputStream executes too slowly
InputStream won't close, or takes forever to
Below is my code in AsyncTask. Wish someone give some help. I will appreciate that a lot.
#Override
protected String doInBackground(URL... urls) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection httpURLConnection = null;
try {
URL url = urls[0];
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(CONNECT_TIMEOUT);
httpURLConnection.setInstanceFollowRedirects(true);
httpURLConnection.setRequestProperty("Range", "bytes=" + mDownloadedBytes + "-");
httpURLConnection.setUseCaches(false);
httpURLConnection.setReadTimeout(5000);
httpURLConnection.setRequestProperty("Connection", "close");
System.setProperty("http.keepAlive", "false");
httpURLConnection.connect();
if (!(httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK // 200
|| httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_CREATED // 201
|| httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_ACCEPTED // 202
|| httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_NOT_AUTHORITATIVE // 203
|| httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_NO_CONTENT // 204
|| httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_RESET //205
|| httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_PARTIAL)) { //206
return "Download fail, server returned HTTP " + httpURLConnection.getResponseCode()
+ " " + httpURLConnection.getResponseMessage();
}
String fileName = getFileName();
String fileExtension = getFileExtension(httpURLConnection);
if(mDownloadFile == null) {
mDownloadFile = getDownloadFile(fileName, fileExtension);
output = new FileOutputStream(mDownloadFile);
} else {
if(mDownloadFile.exists()) {
output = new FileOutputStream(mDownloadFile, true);
} else {
cancel(true);
return null;
}
}
int fileLength = httpURLConnection.getContentLength();
if(sTotalFileLength == -1 && fileLength != -1){
sTotalFileLength = fileLength;
}
// update download state depending on fileLength
updateUI(fileLength);
// check free space if server respond this value
if(fileLength != -1){
if(!isFreeSpaceEnough(fileLength)){
return "Download fail, you don't have enough free space to save the file";
}
}
// download the file
input = httpURLConnection.getInputStream();
byte data[] = new byte[BUFFER_SIZE];
int count;
while ((count = input.read(data)) != -1) {
if (isCancelled()) {
return null;
}
mDownloadedBytes += count;
if(sTotalFileLength > 0){
int progress = (int)(mDownloadedBytes * 100 / sTotalFileLength);
publishProgress(progress);
}
try {
output.write(data, 0, count);
} catch (IOException e){
return "No enough free space to save file!";
} catch (IndexOutOfBoundsException e){
return "Write to file error";
}
}
// write a record to Download DB after download complete
// just record image in DownloadDb
if(!fileExtension.equals("") && isInImageExtensionList(fileExtension)) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mDbRow = new DownloadDbRow(dateFormat.format(new Date()), fileName+"."+fileExtension);
mDb.insert(mDbRow);
}
// Tell system to scan for media file change
mMediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(mDownloadFile);
mMediaScanIntent.setData(contentUri);
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null) {
output.flush();
output.close();
}
/////////////////////
// //
// problems here!! //
// //
/////////////////////
if (input != null) {
input.close();
}
} catch (IOException e) {
return e.toString();
}
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
return DOWNLOAD_SUCCESSFULLY;
}
public class PreviewDownload extends AsyncTask<String, Void, String> {
public static final String TAG = "PreviewDownload";
public String inputPath = null;
public String outputFolder = null;
public IRIssue issue = null;
#Override
protected String doInBackground(String... parms) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
issue = Broker.model.issueDataStore.getIRIssue(parms[0]);
outputFolder = IRConstant.issueFolder(issue.year, issue.month, issue.day, issue.pubKey);
try {
inputPath = IRConstant.downloadFile(issue.year, issue.month, issue.day, issue.pubKey, "preview", "0");
URL url = new URL(inputPath);
Log.d (TAG,"input: " + inputPath);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
return null;
// return "Server returned HTTP " + connection.getResponseCode()
// + " " + connection.getResponseMessage();
// download the file
input = connection.getInputStream();
output = new FileOutputStream(outputFolder + "/preview.zip");
Log.d (TAG,"output: " + output);
byte data[] = new byte[1024];
int count;
while ((count = input.read(data)) != -1) {
output.write(data, 0, count);
}
} catch (Exception e) {
// return e.toString();
return null;
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return outputFolder;
}
#Override
protected void onPostExecute(String outputFolder) {
// TODO Auto-generated method stub
super.onPostExecute(outputFolder);
if (outputFolder != null) {
File zipFile = new File (outputFolder + "/preview.zip");
if (Utils.unzip(outputFolder,outputFolder + "/preview.zip" )) {
zipFile.delete();
issue.isThumbDownloaded = 1;
} else {
issue.isThumbDownloaded = 0;
}
} else {
Toast.makeText(Broker.launcherActivity.getBaseContext(), R.string.wordCantDownload, Toast.LENGTH_LONG).show();
issue.isThumbDownloaded = 0;
}
issue.updateProgress(issue.progress);
}
}
Here is the downloader I implemented , the problem is , when the network lost, the output become null and show error message, however, if I would like to retry two times before showing error message, are there any way to do this? If I perfer not to pass in an object instead of string ,is it not recommended? thanks
What prevents you from re-instanciating and re-executing a "Downloader" from your catch blocks in case of errors ?
You could use a single common shared object between dowloader instances to count the attempts, or better, pass a parameter to each of them. In the catch block, you would then retry if you didn't reach the limit, and increase the value passed to a new downloader... Something recursive.
int expectedLength = connection.getContentLength();
can you compare with the expectedLength & downloaded length and retry?
I am developing an Android application to download images from my web server. All the Code is running fine. I am using Asynctask to download the images to my sdcard.
I am on a 4mbps connection but my application is taking about 8 mins to download 3 images (2.5 MB). I have read else where that Asynctask automatically manages Thread creation, so now what I can do to achieve concurrency ?
I am posting my code below. The code Below is for my Asynctask activity that downloads the image from server to sdcard.
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
Bitmap bitmap1;
String sdCard;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
#Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
bitmap1 = downloadBitmap(params[0]);
boolean avail = isMemorySizeAvailableAndroid(bitmap1.getRowBytes(),
Environment.isExternalStorageEmulated());
if (avail) {
try {
sdCard = Environment.getExternalStorageDirectory().toString()
+ "/MyCatalogue";
File f1 = new File(sdCard);
if (!f1.exists()) {
f1.mkdirs();
}
String filename1 = params[0].substring(params[0]
.lastIndexOf("/") + 1);
File file1 = new File(f1.toString(), filename1);
OutputStream stream1 = new FileOutputStream(file1);
bitmap1.compress(CompressFormat.JPEG, 100, stream1);
Log.w("Abhishek", "card is " + sdCard);
} catch (Exception e) {
e.printStackTrace();
}
}
Log.w("ImageDownloader", "Success bitmap is" + bitmap1);
return downloadBitmap(params[0]);
}
protected static boolean isMemorySizeAvailableAndroid(long download_bytes,
boolean isExternalMemory) {
boolean isMemoryAvailable = false;
long freeSpace = 0;
// if isExternalMemory get true to calculate external SD card available
// size
if (isExternalMemory) {
try {
StatFs stat = new StatFs(Environment
.getExternalStorageDirectory().getPath());
freeSpace = (long) stat.getAvailableBlocks()
* (long) stat.getBlockSize();
if (freeSpace > download_bytes) {
isMemoryAvailable = true;
} else {
isMemoryAvailable = false;
}
} catch (Exception e) {
e.printStackTrace();
isMemoryAvailable = false;
}
} else {
// find phone available size
try {
StatFs stat = new StatFs(Environment.getDataDirectory()
.getPath());
freeSpace = (long) stat.getAvailableBlocks()
* (long) stat.getBlockSize();
if (freeSpace > download_bytes) {
isMemoryAvailable = true;
} else {
isMemoryAvailable = false;
}
} catch (Exception e) {
e.printStackTrace();
isMemoryAvailable = false;
}
}
return isMemoryAvailable;
}
#Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
static Bitmap downloadBitmap(String url) {
final AndroidHttpClient client = AndroidHttpClient
.newInstance("Android");
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode
+ " while retrieving bitmap from " + url);
return null;
} else {
Log.w("ImageDownloader", "Success " + statusCode
+ " while retrieving bitmap from " + url);
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
final Bitmap bitmap = BitmapFactory
.decodeStream(inputStream);
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or
// IllegalStateException
getRequest.abort();
Log.w("ImageDownloader", "Error while retrieving bitmap from "
+ url);
} finally {
if (client != null) {
client.close();
}
}
return null;
}
}
Why you download image twice in doInBackground() at the start and the end? You can return the bitmap just downloaded directly.
if your min sdk level >= 11, you can call executeOnExecutor of AsyncTask with param " THREAD_POOL_EXECUTOR" for concurrency.
if your min sdk level < 11, you can implements AsyncTask new API by reference the source code of AsyncTask.
Use a executeOnExecutor
http://developer.android.com/reference/java/util/concurrent/Executor.html
new BitmapDownloaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your urls");
Quoting from docs
http://developer.android.com/reference/android/os/AsyncTask.html
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
I have an AsyncTask to download files one by one, and I make it as a queue, when it's running on android 2.x, good. In android 4.0+ it stop working. Here I passed a ProgressBar to AsyncTask, so it will update the loading progress bar, and indicate where it is.
The strange part is the progress bar will go 100% very quick not match the real size of file. And the length of file output in logcat also wrong...
All tasks will execute serially so it won't hurt the parallel limitation above SDK 11. I guess the problem might be inside the download part, just don't know where it is.
public function download ()
{
.....
if (task != null) {
task.cancel (true);
}
task = new OnlineDownloadTask (progress);
task.execute (url, path);
.....
}
class OnlineDownloadTask extends AsyncTask<String, String, String> {
private final WeakReference<OfflineQueueIndicatorView> progressbarReference;
public OnlineDownloadTask(OfflineQueueIndicatorView progress) {
progressbarReference = new WeakReference<OfflineQueueIndicatorView>(
progress);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... aurl) {
int count;
try {
URL url = new URL(aurl[0]);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
conn.setRequestMethod("GET");
conn.setAllowUserInteraction(false);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.connect();
int lengthOfFile = conn.getContentLength();
android.util.Log.v("offline.downloader", lengthOfFile + "");
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(aurl[1]);
try {
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress(""
+ (int) ((total * 100) / lengthOfFile));
if (stopoffline) {
android.util.Log.v("file.downloader", "stopped");
break;
}
output.write(data, 0, count);
}
if (stopoffline) {
output.flush();
output.close();
input.close();
conn.disconnect();
File file = new File(aurl[1]);
if (file.exists()) {
file.delete();
}
stopoffline = false;
return null;
} else {
output.flush();
output.close();
input.close();
conn.disconnect();
if (DiskCache.getInstance().offlineDirectoryExist(
DiskCache.getInstance().offlineCurrentFolder)) {
} else {
if (!DiskCache
.getInstance()
.makeOfflineFolder(
DiskCache.getInstance().offlineCurrentFolder)) {
return null;
}
}
android.util.Log.v("offline",
DiskCache.getInstance().offlineCurrentFolder);
unzip(aurl[1],
DiskCache.getInstance().offlineCurrentFolder);
DiskCache.getInstance().deleteFile(aurl[1]);
return "succ";
}
} finally {
if (output != null) {
output.flush();
output.close();
}
if (input != null) {
input.close();
}
if (conn != null) {
conn.disconnect();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void onProgressUpdate(String... progress) {
try {
if (progressbarReference != null) {
OfflineQueueIndicatorView p = progressbarReference.get();
if (p != null) {
int i = Integer.parseInt(progress[0]);
p.setProgress(i);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onPostExecute(String ret) {
try {
if (progressbarReference != null) {
if (ret != null) {
queue.get(currentId).put("state", "complete");
} else {
if (queue != null) {
if (currentId != null) {
queue.get(currentId).put("state", "failed");
}
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
download();
}
}
It's possible that the newer version of HttpUrlConnection in Android 4.0 is causing the server to use Chunked Transfer Encoding, which is supported in HTTP/1.1. The Android 2.x version may not have supported CTE. When sending a response with CTE (e.g., during file/video streaming), the server will not return a content length. As such, you may want to show an indeterminate ProgressBar when the content length is not available.
I finally found what's wrong after I remove conn.setDoOutput(true), it working well on both android 2.x and 4.x emulator, I think also acj has the point, sometimes Chunked Transfer Encoding is the reason too.