I found a snippet online for showing a download's progress. The error I'm getting is android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. My goal is to have the phone download a file and then when completed, switch to the chaptermenu intent
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.widget.TextView;
public class CheckDownload extends Activity {
/** Called when the activity is first created. */
#Override
protected void onCreate(Bundle SavedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(SavedInstanceState);
setContentView(R.layout.downloadscreen);
Thread timer = new Thread() {
public void run() {
try {
// set the download URL, a url that points to a file on the
// internet
// this is the file to be downloaded
URL url = new URL(
"http://www.android.com/media/wallpaper/gif/android_logo.gif");
// create the new connection
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
// set up some things on the connection
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
// and connect!
urlConnection.connect();
// set the path where we want to save the file
// in this case, going to save it on the root directory of
// the
// sd card.
File SDCardRoot = Environment.getExternalStorageDirectory();
// create a new file, specifying the path, and the filename
// which we want to save the file as.
File file = new File(SDCardRoot, "android_logo.gif");
// this will be used to write the downloaded data into the
// file we created
FileOutputStream fileOutput = new FileOutputStream(file);
// this will be used in reading the data from the internet
InputStream inputStream = urlConnection.getInputStream();
// this is the total size of the file
int totalSize = urlConnection.getContentLength();
// variable to store total downloaded bytes
int downloadedSize = 0;
// create a buffer...
byte[] buffer = new byte[1024];
int bufferLength = 0; // used to store a temporary size of
// the buffer
// now, read through the input buffer and write the contents
// to the file
while ((bufferLength = inputStream.read(buffer)) > 0) {
// add the data in the buffer to the file in the file
// output stream (the file on the sd card
fileOutput.write(buffer, 0, bufferLength);
// add up the size so we know how much is downloaded
downloadedSize += bufferLength;
// this is where you would do something to report the
// prgress, like this maybe
updateProgress(downloadedSize, totalSize);
}
// close the output stream when done
fileOutput.close();
// catch some possible errors...
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
Intent chapterMenuIntentObject = new Intent(
"com.blah.blah.CHAPTERMENU");
startActivity(chapterMenuIntentObject);
}
}
};
timer.start();
}
public void updateProgress(int downloadedSize, int totalSize) {
int percentage = downloadedSize / totalSize * 100;
String stringy = "Download Progress: " + percentage + "%";
TextView textytext = (TextView) findViewById(R.id.downloadscreentextview);
textytext.setText(stringy);
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
finish();
}
}
One easier way to do this is to use runOnUIThread(Runnable) function. This is a function of Activity class for updating UI. I find using it more convenient than creating handlers/messages for communicating with UI thread. Your code can be changed to (I have not test yet!):
public void updateProgress(int downloadedSize, int totalSize) {
runOnUiThread(new Runnable() {
public void run() {
int percentage = downloadedSize / totalSize * 100;
String stringy = "Download Progress: " + percentage + "%";
TextView textytext = (TextView) findViewById(R.id.downloadscreentextview);
textytext.setText(stringy);
}});
}
Let me know if you have any error.
You are trying to update the progress from worker(or background thread) where it doesnot get reference to your views. It has to be done on UI thread.
Try using AsynTask , makes life easier with threads.
http://developer.android.com/reference/android/os/AsyncTask.html
or
if you want to stick with normal threads,
Check this code,
http://huuah.com/android-progress-bar-and-thread-updating/
You might have to use messageHandler to send message to progressbar, and then update.
Related
I am new in android and i ask a question about my thread that i create. I think it is stupid question but I am sorry.I have a onClick button listener. Its job is get the URL download link and stores in a variable.
/**
* this method invoke from setPositiveButton's dialog
*
* #param rootView
*/
private void addURLToList(View rootView) {
editTextAddURL = (EditText) rootView.findViewById(R.id.editText_add_url);
Log.i("===", "addURLToList: " + editTextAddURL.getText());
stringUrl = editTextAddURL.getText().toString();
*start GetSizeOfFile thread for getting size file and store
* in lenghtOfFile variable
*/
new GetSizeOfFile().start();
Log.i("====", "size of file after Thread: " + lenghtOfFile);
}
I create a Thread because I want to get file size.
private class GetSizeOfFile extends Thread {
#Override
public void run() {
super.run();
try {
URL url = new URL(stringUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
lenghtOfFile = connection.getContentLength();
Log.i("====", "size of file in Thread: " + lenghtOfFile);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
everything is ok but when thread is started ,after few second my lenghtOfFile variable is initialized and I got 0 in lenghtOfFile in this Line Log.i("====", "size of file after Thread: " + lenghtOfFile);
this is my logcat:
02-22 10:02:11.352 11333-11333/com.example.manifest.simplefiledownloadmanager I/===: addURLToList: http://dl2.soft98.ir/soft/a/Adobe.Shockwave.Player.12.2.7.197.IE.rar
02-22 10:02:11.352 11333-11333/com.example.manifest.simplefiledownloadmanager I/====: file name : Adobe.Shockwave.Player.12.2.7.197.IE.rar
02-22 10:02:11.352 11333-11333/com.example.manifest.simplefiledownloadmanager I/====: size of file after Thread: 0
02-22 10:02:36.544 11333-11495/com.example.manifest.simplefiledownloadmanager I/====: size of file in Thread: 13524394
I want to get file's size from thread first.is it correct that I have to sleep the thread or exit standard way?sorry I am new in android
When working with threads you cannot assume their order of execution.
What happens in your case I presume is that while your new thread is waiting on the connection to be established, the original thread is being run with the uninitialized lenghtOfFile variable so the log looks like it does. Another possibility is that the new thread didn't even begin running when the lenghtOfFile=0 line was logged. That is just how threads work.
For this exact purpose the ASyncTask class exists in Android.
Your code should be somewhat like this:
private class GetSizeOfFile extends AsyncTask<String, Void, Long> {
// runs on a background thread
protected Long doInBackground(String... stringUrls) {
String stringUrl = stringUrls[0];
try {
URL url = new URL(stringUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
long lenghtOfFile = connection.getContentLength();
return lenghtOfFile;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
// runs on main thread
protected void onPostExecute(Long lenghtOfFile) {
if (lenghtOfFile == -1) {
// something went wrong
} else {
Log.i("====", "size of file: " + lenghtOfFile);
// whatever else you want to do
}
}
}
new GetSizeOfFile().execute(stringUrl);
We are currently in need of a tool to upload photos shot from an android device directly to our server without creating an android app. Can this be done in a web application? I already tried getusermedia API without success.
Now you can make this task simpler and more useful i have designed one class named ImageUploaderUtility, which uploads images to a remote webserver. ImageUploadUtility has simple methods where you just need to pass the name of your image to upload.
Apart from this the key point here is this class needs to be executed in an AsynTask so it ensures the whole image upload process doesn't freeze your UI.
I have used this same past project and expanded it.
The first thing to understand is i have made inner class in my BlogPostExampleActivity which is extended from AsyncTask, this inner class will call the method to upload image which is declared and defined in ImageUploader Utility that's all isn't that simple.
Have a look at the code and let me know if you can't understand anything in this.
private class ImageUploaderTask extends AsyncTask<String, Integer, Void>{
#Override
protected void onPreExecute()
{
simpleWaitDialog = ProgressDialog.show(BlogPostExamplesActivity.this, "Wait", "Uploading Image");
}
#Override
protected Void doInBackground(String... params){
new ImageUploadUtility().uploadSingleImage(params[0]);
return null;
}
#Override
protected void onPostExecute(Void result){
simpleWaitDialog.dismiss();
}
}
And Now ImageUploaderUtility.java
package gargi.blogpostexamples.webservice;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import org.apache.commons.httpclient.methods.PostMethod;import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;import org.apache.commons.httpclient.methods.multipart.FilePart;import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;import org.apache.commons.httpclient.methods.multipart.Part;import org.apache.commons.httpclient.methods.multipart.StringPart;import android.os.Environment;import android.util.Log;class ImageUploadUtility {/** * Simple Utility Method gets called from other class to start uploading the image * #param fileNameToUpload name of the file to upload */public void uploadSingleImage(String fileNameToUpload){ try { doUploadinBackground(getBytesFromFile(new File(Environment.getExternalStorageDirectory(),fileNameToUpload)), fileNameToUpload); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }}/** * Method uploads the image using http multipart form data. * We are not using the default httpclient coming with android we are using the new from apache * they are placed in libs folder of the application * * #param imageData * #param filename * #return * #throws Exception */static boolean doUploadinBackground(final byte[] imageData, String filename) throws Exception{ String responseString = null; PostMethod method; method = new PostMethod("your url to upload"); org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient(); client.getHttpConnectionManager().getParams().setConnectionTimeout( 100000); FilePart photo = new FilePart("userfile", new ByteArrayPartSource( filename, imageData)); photo.setContentType("image/jpeg"); photo.setCharSet(null); String s = new String(imageData); Part[] parts = { new StringPart("latitude", "123456"), new StringPart("longitude","12.123567"), new StringPart("imei","1234567899"), new StringPart("to_email","some email"), photo }; method.setRequestEntity(new MultipartRequestEntity(parts, method .getParams())); client.executeMethod(method); responseString = method.getResponseBodyAsString(); method.releaseConnection(); Log.e("httpPost", "Response status: " + responseString); if (responseString.equals("SUCCESS")) { return true; } else { return false; } }/** * Simple Reads the image file and converts them to Bytes * * #param file name of the file * #return byte array which is converted from the image * #throws IOException */public static byte[] getBytesFromFile(File file) throws IOException { InputStream is = new FileInputStream(file); // Get the size of the file long length = file.length(); // You cannot create an array using a long type. // It needs to be an int type. // Before converting to an int type, check // to ensure that file is not larger than Integer.MAX_VALUE. if (length > Integer.MAX_VALUE) { // File is too large } // Create the byte array to hold the data byte[] bytes = new byte[(int)length]; // Read in the bytes int offset = 0; int numRead = 0; while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) { offset += numRead; } // Ensure all the bytes have been read in if (offset < bytes.length) { throw new IOException("Could not completely read file "+file.getName()); } // Close the input stream and return bytes is.close(); return bytes;}}
So that's it for now, the bad thing in this is i have used to Inner Class in this activity and i don't know why but i am not feeling good about that, so next time i will try to make a single class extended from AysncTask and use that in existing examples.
Apart from this if you feel it can be improved in some other ways please don't forget to leave that in comment.
Last but not the list you can download the complete example from this link.
I'm new to Android and developing a file downloading app with a ProgressDialog which shows the downloading percentage.
I use AsyncTask and here is the trouble part of my code.
protected String doInBackground(String... f_url){
int count;
try {
URL url = new URL(f_url[0]);
URLConnection conn = url.openConnection();
conn.connect();
// getting file length
int lenghtOfFile = conn.getContentLength();
// input stream to read file - with 8k buffer
InputStream input = new BufferedInputStream(url.openStream(), 8192);
File direct = new File(folder);
if(!direct.exists()) {
direct.mkdirs();
}
// Output stream to write file
OutputStream output = new FileOutputStream(apkPath);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
// After this onProgressUpdate will be called
publishProgress(""+(int)((total*100)/lenghtOfFile));
// writing data to file
output.write(data, 0, count);
}
// flushing output
output.flush();
// closing streams
output.close();
input.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return null;
}
My issue is this code works really well on Android API 16 (JB) but not on API 19 (KitKat). On KitKat devices, the progress bar percentage does not update (always 0). After checking the codes, I found conn.getContentLength() returns -1 when I run it on KitKat. So it can not update the progress. But it returns correct file size when I run it on API 16 (JB).
Can somebody please help me to solve this?
Thank you in advance.
Have you read Migrating to WebView in Android 4.4: http://developer.android.com/guide/webapps/migrating.html
Blockquote
If you call methods on WebView from any thread other than your app's UI thread, it can cause unexpected results. For example, if your app uses multiple threads, you can use the runOnUiThread() method to ensure your code executes on the UI thread:
runOnUiThread(new Runnable() {
#Override
public void run() {
// Code for WebView goes here
}
});
You can try this:
conn.setRequestProperty("Accept-Encoding", "identity");
I have followed some online tutorials and created this code to download the files that i have hosted in dropbox
I am using async task to do this
// AsyncTask to download a file
private class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
public DownloadTask(Context context) {
this.context = context;
}
#Override
protected String doInBackground(String... sUrl) {
// 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);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
wl.acquire();
try {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
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 "Server returned HTTP "
+ connection.getResponseCode() + " "
+ connection.getResponseMessage();
// TODO
File file = new File(Environment
.getExternalStorageDirectory().getPath()
+ "/kathmandu.map");
if (file.exists()) {
Log.i("File Exists", "Code Gets here, file exists");
return "exists";
// if (connection.getResponseCode() ==
// HttpURLConnection.HTTP_NOT_MODIFIED) {
//
// return null;
// }
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
Log.i("Length", String.valueOf(fileLength));
// download the file
input = connection.getInputStream();
output = new FileOutputStream(Environment
.getExternalStorageDirectory().getPath()
+ "/kathmandu.map");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled())
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);
}
} 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();
}
} finally {
wl.release();
}
return null;
}
I call the download code when the download options menu is clicked.
final DownloadTask downloadTask = new DownloadTask(MapActivity.this);
downloadTask
.execute("https://dl.dropboxusercontent.com/u/95497883/kathmandu-2013-8-12.map");
mProgressDialog
.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
downloadTask.cancel(true);
}
});
The code works fine but at the times the outputstream does not write full file and exits. Everything looks okay. The file is downloaded but it is corrupted.
The getContentLength() also returns -1 so i cannot check if the whole file has been downloaded using the content length. The file is a offline vector map and i need it to display offline maps. The corrupted file causes a runtime exception while trying to access it. Is there is any way to ensure that the file has been downloaded correctly.
Also i would like to provide the data with the app itself. Can i put this in the assets folder of my app. What is the best way to access the files in the assets folder during runtime.
Your assets folder is not writable as it is a part of the apk. you can of course use your application's sandbox storage (using Environment.getDir() ) or external storage (using Environment.getExternalStorageDirectory()) like you have done in your code.
I think using the DownloadManager would be a great idea to achieve exactly what you want please refer : http://developer.android.com/reference/android/app/DownloadManager.html
a short solution
DownloadManager.Request req=new DownloadManager.Request(url);
req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI
| DownloadManager.Request.NETWORK_MOBILE)
.setTitle("Downloading")
.setDescription("Map is Being Downloaded")
.setDestinationInExternalPublicDir(Environment.getExternalStorageDirectory,
"+/maps_app/something.map");
I am using AsyncTask to download ~50 MB files from internet. Sometimes, when I download this file, progress bar gain is very slow (even when I am on Wi-Fi). And after minute, phone shows me, download complete, but the file itself has only ~100kB, no more. But when I restart device, and try to download file, download is executed briefly and quick. Has anyone faced same problem? Do I need to erase same download memory before downloading new file? I am downloading file to Environment.externalStoryDirectory().
Thx
Calling download from activity:
mProgressDialog = new ProgressDialog(ItemDetails.this);
mProgressDialog.setTitle("Downloading");
mProgressDialog.setMessage("Downloading sth...");
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(100);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
DownloadMapTask downloadFile = new DownloadMapTask(ItemDetails.this);
downloadFile.execute(web_location_url);
mProgressDialog.show();
Download Async Task (two methods):
#Override
protected String doInBackground(String... urls) {
int count;
PATH=maps_loc+"/Android/data/test/maps/";
try {
URL url = new URL(urls[0]);
HttpURLConnection connection2 = (HttpURLConnection) url.openConnection();
connection2.setRequestMethod("GET");
connection2.setDoOutput(true);
connection2.connect();
int lenghtOfFile = connection2.getContentLength();
File apkdir = new File(PATH);
apkdir.mkdirs();
File newInstall = new File(PATH, name+".tmp");
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(newInstall);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1 && running==true) {
total += count;
publishProgress((int) (total * 100 / lenghtOfFile));
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void onProgressUpdate(Integer... args) {
ItemDetails.mProgressDialog.setProgress(args[0]);
}
Some servers will close the connection if the client has slow speed and the download takes long time, which can be the case if your program is connected to the Internet through mobile data not Wi-Fi.
You should consider supporting download resume in your program to not start from scratch every time.
I do not think there is sort of download memory that you need to clear. I have an app that can easily downloads over 50MB with no problems.
Also, you might consider obtaining a lock for both Wi-Fi and processor to keep your program running until the download finishes.
Edit
In your code, try to print the value lenghtOfFile after the line int lenghtOfFile = connection2.getContentLength(); to make sure that it is the same as the actual file size you are downloading.
Below is alternative example code which supports resume that I am using in my projects. (it is just to illustrate the idea, you will need to modify the code to your needs)
HttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(new URI(fileURL)));
HttpResponse response;
InputStream is = null;
FileOutputStream fos = null;
try {
boolean continueDownloading = false;
String tmpFileName = fileName + "_tmp";
outputFile = new File(downloadFolder, tmpFileName);
if (outputFile.exists()) {
localFileLength = outputFile.length();
if (localFileLength > 0) {
continueDownloading = true;
}
if (continueDownloading) {
request.addHeader("Range", "bytes=" + localFileLength + "-");
}
response = httpClient.execute(request);
long remoteFileLength = 0;
Header contentLengthHeader = response.getFirstHeader("Content-Length");
if (contentLengthHeader != null) {
remoteFileLength = Integer.parseInt(contentLengthHeader.getValue());
}
long downloaded = 0;
if (continueDownloading) {
downloaded = localFileLength;
}
long fullFileLength = downloaded + remoteFileLength;
fos = new FileOutputStream(outputFile, true);
is = response.getEntity().getContent();
byte[] buffer = new byte[DOWNLOAD_BUFFER_SIZE];
int len = 0;
while ((len = is.read(buffer)) != -1 && isDownloading) {
fos.write(buffer, 0, len);
downloaded += len;
}
fos.flush();
boolean success = downloaded == fullFileLength;
if (success) {
outputFile.renameTo(new File(downloadFolder, fileName));
}
} catch (Throwable ex) {
ex.printStackTrace();
} finally {
// clean up resources
}
Try using downloadManager instead of downloading manually , there are many advantages to using it.
Here is an example for it : DownloadManager Example
and take a look at the documentations : DownloadManager