I am creating an app for my client, one of his requirements is to download and install an external apk (size approx. 62mb) on the device. The devices will be rooted, so that's not a problem. But, while downloading the apk using AsyncTask, the progress bar resets to 0% after reaching 34% (exact 34% every time, even on different devices) and throws java.io.IOException: unexpected end of stream.
Here is the code I'm using :
public class InstallAPK extends AsyncTask<Void,Integer,Void> {
ProgressDialog progressDialog;
int status = 0;
private Context context;
public InstallAPK(Context context, ProgressDialog progress){
this.context = context;
this.progressDialog = progress;
}
public void onPreExecute() {
if(progressDialog!=null)
progressDialog.show();
}
#Override
protected Void doInBackground(Void... arg0) {
try {
URL url = new URL(context.getString(R.string.kodi_apk_link));
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();
// getting file length
int lenghtOfFile = c.getContentLength();
Log.e("File length", ""+lenghtOfFile);
File outputFile = new File(context.getFilesDir(), context.getString(R.string.kodi_apk_name));
if(outputFile.exists()){
if(outputFile.length() != lenghtOfFile)
outputFile.delete();
else {
publishProgress(-1);
final String libs = "LD_LIBRARY_PATH=/vendor/lib:/system/lib ";
final String commands = libs + "pm install -r " + context.getFilesDir().getAbsolutePath() + "/"
+ context.getString(R.string.kodi_apk_name);
installApk(commands);
return null;
}
}
FileOutputStream fos = new FileOutputStream(outputFile);
InputStream is = c.getInputStream();
//i tried both, with and without buffered reader
BufferedInputStream bufferedInputStream = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int len1 = 0, total=0;
if (lenghtOfFile != -1)
{
buffer = new byte[lenghtOfFile];
do {
len1 += bufferedInputStream.read(buffer, len1, lenghtOfFile-len1);
publishProgress((int)((len1*100)/lenghtOfFile));
} while (len1 < lenghtOfFile);
}
//I was using this code before, but it's not working too
/*while ((len1 = is.read(buffer)) != -1) {
total += len1;
publishProgress((int)((total*100)/lenghtOfFile));
fos.write(buffer, 0, len1);
}*/
fos.flush();
fos.close();
bufferedInputStream.close();
is.close();
//Log.e("Directory path", myDir.getAbsolutePath());
publishProgress(-1);
final String libs = "LD_LIBRARY_PATH=/vendor/lib:/system/lib ";
final String commands = libs + "pm install -r " + context.getFilesDir().getAbsolutePath() + "/"
+ context.getString(R.string.kodi_apk_name);
installApk(commands);
} catch (FileNotFoundException fnfe) {
status = 1;
Log.e("File", "FileNotFoundException! " + fnfe);
}
catch(Exception e)
{
Log.e("UpdateAPP", "Exception " + e);
}
return null;
}
protected void onProgressUpdate(Integer... progress) {
if(progress[0]!=-1) {
// setting progress percentage
progressDialog.setProgress(progress[0]);
} else {
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Installing Kodi...");
}
}
public void onPostExecute(Void unused) {
if(progressDialog!=null) {
progressDialog.dismiss();
}
if(status == 1)
Toast.makeText(context,"App Not Available",Toast.LENGTH_LONG).show();
else
Toast.makeText(context,"Successfully installed the app",Toast.LENGTH_LONG).show();
Intent LaunchIntent = context.getPackageManager().getLaunchIntentForPackage(context.getString(R.string.kodi_apk_package));
if(LaunchIntent!=null)
context.startActivity(LaunchIntent);
else
Toast.makeText(context, "Error in installig Kodi, Try again.", Toast.LENGTH_LONG).show();
}
private void installApk(String commands) {
try {
Process p = Runtime.getRuntime().exec("su");
InputStream es = p.getErrorStream();
DataOutputStream os = new DataOutputStream(p.getOutputStream());
os.writeBytes(commands + "\n");
os.writeBytes("exit\n");
os.flush();
int read;
byte[] buffer = new byte[4096];
String output = new String();
while ((read = es.read(buffer)) > 0) {
output += new String(buffer, 0, read);
}
Log.v("AutoUpdaterActivity", output.toString());
p.waitFor();
} catch (IOException e) {
Log.v("AutoUpdaterActivity", e.toString());
} catch (InterruptedException e) {
Log.v("AutoUpdaterActivity", e.toString());
}
}
}
I tried everything to make this code work. But, it didn't. Then I found an alternative to this. I tried IntentService to download the apk, and surprisingly it worked. I think AsyncTask might have some kind of limit for downloading. To download using IntentService I used this code. The answer is very informative. It also has some other alternatives for downloading.
You should add connection timeout for HttpURLConnection.
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
//set timeout to 5 seconds , set your time here
c.setConnectTimeout(5000);
c.connect();
Its work for me.I hope its work for you.
Related
Hi have implemented programatically downloading of file using inputstream and cipheroutputstream(for encryption). The download is happening very slow. Whereas if i try to download via download manager, it is very fast. What can i do to improve my code and increase the download speed of the file. Below is my code.
private void saveFileUsingEncryption(String aMineType, long length) throws Exception {
int bufferSize = 1024*4;
//byte[] buffer = new byte[1024];
byte[] buffer = new byte[bufferSize];
int bytesRead = 0;
long totalRead = 0;
FileOutputStream outStream = null;
File f = new File(Constants.DWLPATH);
if (!f.exists()) {
f.mkdirs();
}
try {
Cipher aes = Cipher.getInstance("ARC4");
aes.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("mykey".getBytes(), "ARC4"));
if(contDisp==null || contDisp.length()==0) {
// downloadFileName = downloadFileName.replaceAll("[^a-zA-Z0-9_]+", "");
downloadFileName = downloadFileName + "." + getFileExtension(aMineType);
}
outStream = new FileOutputStream(Constants.DWLPATH + downloadFileName,true);
CipherOutputStream out = new CipherOutputStream(outStream, aes);
while ((bytesRead = inputStream.read(buffer, 0, bufferSize)) >= 0) {
out.write(buffer, 0, bytesRead);
try{
// Adjust this value. It shouldn't be too small.
Thread.sleep(50);
}catch (InterruptedException e){
TraceUtils.logException(e);
}
totalRead += bytesRead;
sb=sb.append("\n Total bytes Read:"+totalRead);
Log.e("--",sb.toString());
/* if (this.length > 0) {
Long[] progress = new Long[5];
progress[0] = (long) ((double) totalRead / (double) this.length * 100.0);
publishProgress(progress);
}*/
if (this.isCancelled()) {
if (conn != null)
conn.disconnect();
conn = null;
break;
}
}
Log.e("Download completed","success");
out.flush();
//Utils.putDownloadLogs(requestUrl,mimeType,length, downloadFileName,"Download is Successful",sb.toString(), context);
outStream.close();
buffer = null;
} catch (Exception e) {
TraceUtils.logException( e);
file_newsize = storedFileSizeInDB + totalRead;
if (totalFileSize == 0)
totalFileSize = length;
callback.onRequestInterrupted(file_newsize,totalFileSize);
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
// Utils.putDownloadLogs(requestUrl,mimeType,length,downloadFileName,"failure---" + errors.toString(),sb.toString(), context);
throw e;
} finally {
if (outStream != null)
outStream.close();
outStream = null;
}
}
You can use default download manager to download the file because its very easy to implement and provide better features like respond to the internet connection , provide accessibility to add notification in status bar , by running the query on download manager object you can find the total bytes and remaining bytes so you can calculate the progress and after completion of download by tapping the notification one can perform the desired operation.
And also there are many libraries are available for to achieve this like
PRDOWNLOADER
FetchDownloader
This libraires provide you the feature of pause,download, resume download , tracking the progress and cancel download
Also you can customize it as per your need.
Here is the DownloadAndEncryptFileTask.class to download with encryption
public class DownloadAndEncryptFileTask extends AsyncTask<Void, Void, Void> {
private String mUrl;
private File mFile;
private Cipher mCipher;
InputStream inputStream;
FileOutputStream fileOutputStream;
CipherOutputStream cipherOutputStream;
public DownloadAndEncryptFileTask(String url, File file, Cipher cipher) {
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("You need to supply a url to a clear MP4 file to download and encrypt, or modify the code to use a local encrypted mp4");
}
mUrl = url;
mFile = file;
mCipher = cipher;
}
private void downloadAndEncrypt() throws Exception {
URL url = new URL(mUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (mFile.length() > 0) {
connection.setRequestProperty("Range", "bytes=" + mFile.length() + "-");
}
connection.connect();
Log.e("length", mFile.length() + "");
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("server error: " + connection.getResponseCode() + ", " + connection.getResponseMessage());
}
inputStream = connection.getInputStream();
if (mFile.length() > 0) {
//connection.connect();
fileOutputStream = new FileOutputStream(mFile, true);
} else {
fileOutputStream = new FileOutputStream(mFile);
}
CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, mCipher);
byte buffer[] = new byte[1024 * 1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
Log.d(getClass().getCanonicalName(), "reading from http...");
cipherOutputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
cipherOutputStream.close();
connection.disconnect();
}
#Override
protected Void doInBackground(Void... params) {
try {
downloadAndEncrypt();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
Log.d(getClass().getCanonicalName(), "done");
}
}
Call this class
new DownloadAndEncryptFileTask(
myFeedsModel.getVideo().getVideo360(),
new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), myFeedsModel.getFile_name()),
OBJECT OF YOUR CIPHER
I'm using this class to update my application programatically which works perfectly.
public static class UpdateApp extends AsyncTask < String, Void, Void > {
private Context context;
public void setContext(Context contextf) {
context = contextf;
}
#Override
protected Void doInBackground(String...arg0) {
try {
URL url = new URL(arg0[0]);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();
String PATH = "/mnt/sdcard/Download/";
File file = new File(PATH);
file.mkdirs();
File outputFile = new File(file, "update.apk");
if (outputFile.exists()) {
outputFile.delete();
}
FileOutputStream fos = new FileOutputStream(outputFile);
InputStream is = c.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.close();
is.close();
String filename = "/mnt/sdcard/Download/update.apk";
File filez = new File(filename);
if (filez.exists()) {
try {
final String command = "pm install -r " + filez.getAbsolutePath();
Process proc = Runtime.getRuntime().exec(new String[] {
"su",
"-c",
command
});
proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
//Log.e("UpdateAPP", "Update error! " + e.getMessage());
}
return null;
}
}
As you can see the class makes use of "pm install" command.
What I want to do is to run the updated app as soon as the installation is completed, so the user doesn't stay outside application. Is there any way I can achieve this through package manager or adb?
String cmd = "am start -n " + getApplicationContext().getPackageName() + "/." + getActivity().getClass().getSimpleName();
...
os.writeBytes("pm install -r " + filename + " && " + cmd);
...
Hi I need to show summary progress of AsyncTask. I want to show ProgressBar or ProgressDialog of Downloading, but I have know idea what to do, I know how to show dialog, but only when I download one file, and how do when I have a lot of files to download. Can somebody help????
Here is My AyncTaskClass
public class DownloadProgramTask extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
String path = sUrl[1];
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();
}
// 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(path);
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);
}
} 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;
}
}
And I create new instance of that class for every execute,
File voiceFile = new File(dirVoice, a.getPose_id() + ".mp3");
if (!voiceFile.exists()) {
voiceList.add(a.getVoice());
new DownloadProgramTask().execute(MYurl.BASE_URL + a.getVoice(), voiceFile.getPath());
Log.e("LINK", MYurl.BASE_URL + a.getVoice());
Log.e("Path voice", "" + voiceFile.getPath());
}
File imgLargeFile = new File(dirImageLarge, a.getId() + ".png");
if (!imgLargeFile.exists()) {
imgLargeList.add(a.getVoice());
new DownloadProgramTask().execute(MYurl.BASE_URL + "/" + a.getImgLarge(), imgLargeFile.getPath());
}
you can use the overridden method OnProgressUpdate of Async Task. call publishProgress in doingBackground of asynctask with interger
something like this..
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.i("makemachine", "onProgressUpdate(): " +
percentBar.setProgress((values[0] * 2) + "%");
}
This is my doInBackground method:
#Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
HttpURLConnection conection = null;
BufferedOutputStream bout = null;
FileOutputStream fos = null;
int downloaded = 0;
try {
URL url = new URL(sUrl[0]);
conection = (HttpURLConnection)url.openConnection();
int lenghtOfFile = conection.getContentLength();
if(STATUS) {
File file = new File(Environment.getExternalStorageDirectory().getPath() + "/myapp.apk");
if (file.exists()) {
downloaded = (int) file.length();
conection.setRequestProperty("Range", "bytes=" + (file.length()) + "-");
}
}
else {
conection.setRequestProperty("Range", "bytes=" + downloaded + "-");
}
conection.setDoInput(true);
conection.setDoOutput(true);
conection.connect();
input = new BufferedInputStream(url.openStream(), 8192);
fos=(downloaded==0)? new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/myapp.apk"): new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/myapp.apk",true);
bout = new BufferedOutputStream(fos, 1024);
byte data[] = new byte[1024];
long total = 0;
int count = 0;
while ((count = input.read(data, 0, 1024)) >= 0) {
if (isCancelled()) {
input.close();
return null;
}
bout.write(data, 0, count);
downloaded += count;
publishProgress((int)(downloaded * 100/ lenghtOfFile) );
total += count;
}
bout.flush();
input.close();
fos.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
if (fos != null)
fos.close();
if (bout != null)
bout.close();
} catch (IOException ignored) {
}
if (conection != null)
conection = null;
}
return null;
}
I start download task with this code (resume flag is false -> STATUS = FALSE):
dt = new DownloadTask(DownloadsActivity.this, false);
dt.execute("myurl.something.apk");
then when downloaded completely I launch apk file and all thing work correctly and apk installed correctly. But when pause my download with this code:
dt.cancel(true);
and then resume it with this code (resume flag is true-> STATUS = TRUE):
dt = new DownloadTask(DownloadsActivity.this, true);
dt.execute("myurl.something.apk");
This time apk size is equal to last downloaded before pause + apk total size, therefore my apk file is corrupted. Which means connection.setRequestProperty() not working for me. What is my code problem? Thanks in advance.
i am trying to download mp file in back ground using AsyncTask into the emulator it works properly.. but in divice it doesn't show the progressbar
this problem is due to the when i am running this code the
int lenghtOfFile = c.getContentLength();
Log.d("ANDRO_ASYNC", "Lenght of file: " + lenghtOfFile);
that gives me -1 why this happning? my code is
#Override
protected String doInBackground(String... arg0) {
// TODO Auto-generated method stub
// mp3load();
int len1 = 0;
int count;
try {
URL url = new URL(GlobalVariable.Getstr());
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();
int lenghtOfFile = c.getContentLength();
Log.d("ANDRO_ASYNC", "Lenght of file: " + lenghtOfFile);
String PATH = Environment.getExternalStorageDirectory()
+ "/download/";
Log.v(LOG_TAG, "PATH: " + PATH);
File file = new File(PATH);
file.mkdirs();
// String fileName = "workTest.mp3";
String fileName = GlobalVariable.Getstrpath().toString();
File outputFile = new File(file, fileName);
FileOutputStream fos = new FileOutputStream(outputFile);
InputStream is = c.getInputStream();
byte[] buffer = new byte[1024];
long total = 0;
while ((count = is.read(buffer)) != -1) {
total += count;
publishProgress("" + (int) ((total * 100) / lenghtOfFile));
// fos.write(buffer, 0, len1);
fos.write(buffer, 0, count);
}
fos.close();
is.close();
} catch (IOException e) {
Log.d(LOG_TAG, "Error: " + e);
}
return null;
}
protected void onProgressUpdate(String... progress) {
Log.d("ANDRO_ASYNC", progress[0]);
mProgressDialog.setProgress(Integer.parseInt(progress[0]));
}
protected void onPostExecute(String unused) {
dismissDialog(DIALOG_DOWNLOAD_PROGRESS);
refreshList();
Toast.makeText(
FindFilesByType.this,
"Downloading of " + GlobalVariable.Getstrpath()
+ " complete.", Toast.LENGTH_LONG).show();
// Intent i = new Intent(FindFilesByType.this, SongList.class);
// finish();
// startActivity(i);
try{
Intent i = new Intent(FindFilesByType.this, SongList.class);
finish();
startActivity(i);}catch (Exception e) {
// TODO: handle exception
}
}
}
the entiere code works fine for emulator
but it always doesn't display me progress but download happens sucessfully thanks Pragna
Somethimes java can't retrive the size of a remote file, and i think that is your problem.
But please check internet connection on your phone when debugging the code.
Got the same problem (on Android 4.0 only), and removing
c.setDoOutput(true);
fixed it for me.