Android: download large file
I'm trying to download large file from Internet (>20Mb)
private class DownloadTask extends AsyncTask<DatabaseInfo, Integer, String> {
private DatabaseInfo info;
protected String doInBackground(DatabaseInfo... dbInfo) {
int count;
info = dbInfo[0];
try {
URL url = new URL(dbInfo[0].dbPath);
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream("/sdcard/db.zip");
byte data[] = new byte[1024];
int total = 0;
while ((count = input.read(data)) != -1) {
//output.write(data, 0, count);
total += count;
if (total % 10240 == 0) {
publishProgress(total);
}
}
output.flush();
output.close();
input.close();
}
catch (Exception e) {
Log.e("err", e.getMessage());
}
return null;
}
protected void onProgressUpdate(Integer... total) {
int perc = (int) ((float) total[0] / (float) info.dbZipSize * 100);
mProgressDialog.setProgre开发者_StackOverflow社区ss(perc);
}
protected void onPostExecute(String s) {
dismissDialog(DIALOG_PROGRESS);
Log.e("err", "finish!");
}
}
If I uncomment line
//output.write(data, 0, count);
after 7-15% downloading progressbar dialog dismiss and I see "finish!" in Log. Why?
You should look at implementing HTTP Ranges. This will allow you to re-start your download when it fails.
As to why it stops, some carriers implement download limits which will drop a connection after a certain time or download amount. I've seen this first hand on one UK carrier and been told about it on others. It's usually easy to verify the limit by trying to download the file via Androids browser and if you see it stall or stop you know it's most likely a carrier issue (and yes, the download can be terminated without throwing an Exception).
An Http Range implementation will allow you to continue the download from where it left off and thus your doInBackground method to use a hold-off and resume algorithm so that every time the connection is interrupted you wait for an amount of time then try to resume where the download left off (and, of course, implement a retry limit so you don't end up with an infinite loop when the phone really can't download the file).
There is a very good library that handles all that plus has resume capability for cases when the network goes offline for some unknown reason. The android library is called PRDownloader and is available on Github here: https://github.com/MindorksOpenSource/PRDownloader
first you need to add it to your gradle file, like this (the current version os 0.6.0)
implementation 'com.mindorks.android:prdownloader:0.6.0'
After that, one can use it as shown below in a class I've implemented f the apps i developed. It uses a progress dialog and shows the percentage downloaded and, as a said above, has the ability to pickup the download later for cases when the network goes offline or the user quits the app. One can adjust it according to specific needs, bellow is the code:
public class DownloadManager {
private MainActivity activity;
private ProgressDialog progressDialog;
private int downloadStatus;
private String filename="";
private static String dirPath;
private String URL = "https://someurl.com/dummyfile.pdf";
public DownloadManager(MainActivity _activity){
this.activity=_activity;
dirPath = Utils.getRootDirPath(activity);
downloadStatus=0;
}
public void setDownloadUrl(String url){
this.URL=url;
}
public String getFileNameDirPath(){
return dirPath+"/"+filename;
};
public void setFilename(String filename){
this.filename=filename;
}
public String getFilename(){
return this.filename;
}
public String getDirectoryPath(){
return this.filename;
}
public void startFileDownload(){
if (Status.RUNNING == PRDownloader.getStatus(downloadStatus)) {
PRDownloader.pause(downloadStatus);
return;
}
this.progressDialog = new ProgressDialog(this.activity);
this.progressDialog.setIndeterminate(true);
this.progressDialog.setTitle("Title");
this.progressDialog.setMessage("Downloading file...");
this.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
this.progressDialog.setCancelable(false);
this.progressDialog.setMax(100);
this.progressDialog.show();
if (Status.PAUSED == PRDownloader.getStatus(downloadStatus)) {
PRDownloader.resume(downloadStatus);
return;
}
downloadStatus = PRDownloader.download(URL, dirPath, this.filename)
.build()
.setOnStartOrResumeListener(new OnStartOrResumeListener() {
@Override
public void onStartOrResume() {
progressDialog.setIndeterminate(false);
}
})
.setOnPauseListener(new OnPauseListener() {
@Override
public void onPause() {
}
})
.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel() {
progressDialog.setProgress(0);
downloadStatus = 0;
progressDialog.setIndeterminate(false);
progressDialog.dismiss();
}
})
.setOnProgressListener(new OnProgressListener() {
@Override
public void onProgress(Progress progress) {
long progressPercent = progress.currentBytes * 100 / progress.totalBytes;
progressDialog.setProgress((int) progressPercent);
progressDialog.setIndeterminate(false);
}
})
.start(new OnDownloadListener() {
@Override
public void onDownloadComplete() {
progressDialog.dismiss();
activity.fragmentManagement.setCurrentFragment("MapFragment");
}
@Override
public void onError(Error error) {
Toast.makeText(activity, "Error downloading file", Toast.LENGTH_SHORT).show();
progressDialog.setProgress(0);
downloadStatus = 0;
progressDialog.setIndeterminate(false);
progressDialog.dismiss();
}
});
}
public static final class Utils {
private Utils() {
// no instance
}
public static String getRootDirPath(Context context) {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
File file = ContextCompat.getExternalFilesDirs(context.getApplicationContext(),
null)[0];
return file.getAbsolutePath();
} else {
return context.getApplicationContext().getFilesDir().getAbsolutePath();
}
}
public static String getProgressDisplayLine(long currentBytes, long totalBytes) {
return getBytesToMBString(currentBytes) + "/" + getBytesToMBString(totalBytes);
}
private static String getBytesToMBString(long bytes){
return String.format(Locale.ENGLISH, "%.2fMb", bytes / (1024.00 * 1024.00));
}
}
}
精彩评论