用Android下载一个文件,并在ProgressDialog中显示进度

我试图写一个简单的应用程序得到更新。为此,我需要一个简单的函数,可以下载文件和显示当前进度ProgressDialog。我知道如何执行ProgressDialog,但我不确定如何显示当前进度以及如何首先下载文件。

613442 次浏览

下载文件的方法有很多。下面我将列出最常见的方法;由你来决定哪种方法更适合你的应用程序。

  1. 使用AsyncTask并在对话框中显示下载进度=============================================================

此方法将允许您执行一些后台进程并同时更新UI(在本例中,我们将更新进度条)。

进口:

import android.os.PowerManager;import java.io.InputStream;import java.io.OutputStream;import java.io.FileOutputStream;import java.net.HttpURLConnection;

这是一个示例代码:

// declare the dialog as a member field of your activityProgressDialog mProgressDialog;
// instantiate it within the onCreate methodmProgressDialog = new ProgressDialog(YourActivity.this);mProgressDialog.setMessage("A message");mProgressDialog.setIndeterminate(true);mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);mProgressDialog.setCancelable(true);
// execute this when the downloader must be firedfinal DownloadTask downloadTask = new DownloadTask(YourActivity.this);downloadTask.execute("the url to the file you want to download");
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Overridepublic void onCancel(DialogInterface dialog) {downloadTask.cancel(true); //cancel the task}});

AsyncTask看起来是这样的:

// usually, subclasses of AsyncTask are declared inside the activity class.// that way, you can easily modify the UI thread from hereprivate class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;private PowerManager.WakeLock mWakeLock;
public DownloadTask(Context context) {this.context = context;}
@Overrideprotected String doInBackground(String... sUrl) {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 fileif (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 lengthint fileLength = connection.getContentLength();
// download the fileinput = connection.getInputStream();output = new FileOutputStream("/sdcard/file_name.extension");
byte data[] = new byte[4096];long total = 0;int count;while ((count = input.read(data)) != -1) {// allow canceling with back buttonif (isCancelled()) {input.close();return null;}total += count;// publishing the progress....if (fileLength > 0) // only if total length is knownpublishProgress((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;}

上面的方法(doInBackground)总是在后台线程上运行。你不应该在那里做任何UI任务。另一方面,onProgressUpdateonPreExecute在UI线程上运行,所以你可以在那里更改进度条:

    @Overrideprotected void onPreExecute() {super.onPreExecute();// take CPU lock to prevent CPU from going off if the user// presses the power button during downloadPowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,getClass().getName());mWakeLock.acquire();mProgressDialog.show();}    
@Overrideprotected void onProgressUpdate(Integer... progress) {super.onProgressUpdate(progress);// if we get here, length is known, now set indeterminate to falsemProgressDialog.setIndeterminate(false);mProgressDialog.setMax(100);mProgressDialog.setProgress(progress[0]);}
@Overrideprotected void onPostExecute(String result) {mWakeLock.release();mProgressDialog.dismiss();if (result != null)Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();elseToast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();}}

要运行此操作,您需要WAKE_LOCK权限。

<uses-permission android:name="android.permission.WAKE_LOCK" />
  1. 从服务下载========================

这里最大的问题是:第7点。在下一个例子中,我们将使用两个你可能不知道的类:ResultReceiverIntentServiceResultReceiver是允许我们从服务中更新线程的线程;IntentServiceService的一个子类,它生成一个线程从那里做后台工作(你应该知道Service实际上在你的应用程序的同一个线程中运行;当你扩展Service时,你必须手动生成新的线程来运行CPU阻塞操作)。

下载服务可以是这样的:

public class DownloadService extends IntentService {public static final int UPDATE_PROGRESS = 8344;
public DownloadService() {super("DownloadService");}@Overrideprotected void onHandleIntent(Intent intent) {
String urlToDownload = intent.getStringExtra("url");ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");try {            
//create url and connectURL url = new URL(urlToDownload);URLConnection connection = url.openConnection();connection.connect();
// this will be useful so that you can show a typical 0-100% progress barint fileLength = connection.getContentLength();
// download the fileInputStream input = new BufferedInputStream(connection.getInputStream());
String path = "/sdcard/BarcodeScanner-debug.apk" ;OutputStream output = new FileOutputStream(path);
byte data[] = new byte[1024];long total = 0;int count;while ((count = input.read(data)) != -1) {total += count;
// publishing the progress....Bundle resultData = new Bundle();resultData.putInt("progress" ,(int) (total * 100 / fileLength));receiver.send(UPDATE_PROGRESS, resultData);output.write(data, 0, count);}
// close streamsoutput.flush();output.close();input.close();
} catch (IOException e) {e.printStackTrace();}
Bundle resultData = new Bundle();resultData.putInt("progress" ,100);
receiver.send(UPDATE_PROGRESS, resultData);}}

将服务添加到您的清单:

<service android:name=".DownloadService"/>

这个活动看起来是这样的:

// initialize the progress dialog like in the first example
// this is how you fire the downloadermProgressDialog.show();Intent intent = new Intent(this, DownloadService.class);intent.putExtra("url", "url of the file to download");intent.putExtra("receiver", new DownloadReceiver(new Handler()));startService(intent);

下面是0号上场的情况:

private class DownloadReceiver extends ResultReceiver{
public DownloadReceiver(Handler handler) {super(handler);}
@Overrideprotected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == DownloadService.UPDATE_PROGRESS) {
int progress = resultData.getInt("progress"); //get the progressdialog.setProgress(progress);
if (progress == 100) {dialog.dismiss();}}}}

2.1使用Groundy库

< >强Groundy < / >强是一个帮助您在后台服务中运行代码段的库,它基于上面所示的ResultReceiver概念。这个库目前排名第二。这是整个代码的样子:

您正在显示对话框的活动…

public class MainActivity extends Activity {
private ProgressDialog mProgressDialog;
@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);
findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener() {public void onClick(View view) {String url = ((EditText) findViewById(R.id.edit_url)).getText().toString().trim();Bundle extras = new Bundler().add(DownloadTask.PARAM_URL, url).build();Groundy.create(DownloadExample.this, DownloadTask.class).receiver(mReceiver).params(extras).queue();
mProgressDialog = new ProgressDialog(MainActivity.this);mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);mProgressDialog.setCancelable(false);mProgressDialog.show();}});}
private ResultReceiver mReceiver = new ResultReceiver(new Handler()) {@Overrideprotected void onReceiveResult(int resultCode, Bundle resultData) {super.onReceiveResult(resultCode, resultData);switch (resultCode) {case Groundy.STATUS_PROGRESS:mProgressDialog.setProgress(resultData.getInt(Groundy.KEY_PROGRESS));break;case Groundy.STATUS_FINISHED:Toast.makeText(DownloadExample.this, R.string.file_downloaded, Toast.LENGTH_LONG);mProgressDialog.dismiss();break;case Groundy.STATUS_ERROR:Toast.makeText(DownloadExample.this, resultData.getString(Groundy.KEY_ERROR), Toast.LENGTH_LONG).show();mProgressDialog.dismiss();break;}}};}

Groundy使用GroundyTask实现来下载文件并显示进度:

public class DownloadTask extends GroundyTask {public static final String PARAM_URL = "com.groundy.sample.param.url";
@Overrideprotected boolean doInBackground() {try {String url = getParameters().getString(PARAM_URL);File dest = new File(getContext().getFilesDir(), new File(url).getName());DownloadUtils.downloadFile(getContext(), url, dest, DownloadUtils.getDownloadListenerForTask(this));return true;} catch (Exception pokemon) {return false;}}}

把这个加到清单上

<service android:name="com.codeslap.groundy.GroundyService"/>

我觉得这再容易不过了。只要拿起最新的0号罐子,你就可以开始了。请记住,Groundy的主要目的是在后台服务中调用外部REST api,并轻松地将结果发布到UI。如果你在你的应用程序中做类似的事情,它可能真的很有用。

2.2使用https://github.com/koush/ion

    使用DownloadManager类(仅限GingerBread和更新的类)=============================================================

GingerBread带来了一个新特性DownloadManager,它允许您轻松地下载文件,并将处理线程、流等的艰苦工作委托给系统。

首先,让我们看一个实用方法:

/*** @param context used to check the device version and DownloadManager information* @return true if the download manager is available*/public static boolean isDownloadManagerAvailable(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {return true;}return false;}

方法的名称说明了这一切。一旦你确定DownloadManager是可用的,你可以这样做:

String url = "url you want to download";DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));request.setDescription("Some descrition");request.setTitle("Some title");// in order for this if to run, you must use the android 3.2 to compile your appif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {request.allowScanningByMediaScanner();request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);}request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "name-of-the-file.ext");
// get download service and enqueue fileDownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);manager.enqueue(request);

下载进度将显示在通知栏中。

最终的想法

第一种和第二种方法只是冰山一角。如果你想让你的应用健壮,有很多事情你必须记住。以下是一个简短的列表:

  • 您必须检查用户是否有可用的互联网连接
  • 确保你有正确的权限(INTERNETWRITE_EXTERNAL_STORAGE);如果你想查看网络可用性,还有第二条。
  • 确保你要下载文件的目录存在并且有写权限。
  • 如果下载太大,你可能想要实现一种方法来恢复下载,如果以前的尝试失败。
  • 如果你允许用户中断下载,他们会很感激的。

除非您需要详细控制下载过程,否则可以考虑使用DownloadManager(3),因为它已经处理了上面列出的大部分项目。

但也要考虑到你的需求可能会改变。例如,DownloadManager 没有响应缓存。它会盲目地多次下载同一个大文件。事后补救是不容易的。如果你从基本的HttpURLConnection(1,2)开始,那么你所需要的就是添加HttpResponseCache。因此,最初努力学习基本的标准工具是一项很好的投资。

这个类在API级别26中已弃用。ProgressDialog是一个模式对话框,这会阻止用户与应用程序进行交互使用这个类时,您应该使用一个进度指示器ProgressBar,它可以嵌入到你的应用的UI中。另外,您可以使用通知通知用户任务的进度。链接

如果你要从网上下载东西,别忘了给你的manifest文件添加权限!

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.helloandroid"android:versionCode="1"android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission><uses-permission android:name="android.permission.INTERNET"></uses-permission><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission><uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<applicationandroid:icon="@drawable/icon"android:label="@string/app_name"android:debuggable="true">
</application>
</manifest>

是的,上面的代码可以工作。但是如果你正在更新progressbarAsynctaskonProgressUpdate中,你按下后退按钮或完成你的活动AsyncTask将与你的UI失去跟踪。当你回到你的活动,即使下载正在后台运行,你也不会在进度条上看到更新。因此,在OnResume()上尝试运行一个像runOnUIThread一样的线程,使用一个计时器任务,用从AsyncTask运行后台更新的值更新progressbar

private void updateProgressBar(){Runnable runnable = new updateProgress();background = new Thread(runnable);background.start();}
public class updateProgress implements Runnable {public void run() {while(Thread.currentThread()==background)//while (!Thread.currentThread().isInterrupted()) {try {Thread.sleep(1000);Message msg = new Message();progress = getProgressPercentage();handler.sendMessage(msg);} catch (InterruptedException e) {Thread.currentThread().interrupt();} catch (Exception e) {}}}
private Handler handler = new Handler(){@Overridepublic void handleMessage(Message msg) {progress.setProgress(msg.what);}};

不要忘记摧毁线程时,你的活动是不可见的。

private void destroyRunningThreads() {if (background != null) {background.interrupt();background=null;}}

我个人的建议是使用进度对话框并在执行前构建,或者从OnPreExecute()开始,如果你使用进度对话框的水平风格的进度条,则经常发布进度。剩下的部分是优化doInBackground的算法。

不要忘记将“/sdcard…”替换为< / em > < em >新文件(“/mnt/sdcard/…”),否则您将得到FileNotFoundException

我建议你使用我的项目Netroid,它是基于凌空抽射的。我添加了一些功能,如多事件回调,文件下载管理。这可能会有帮助。

当我开始学习android开发时,我知道ProgressDialog才是正确的方法。有ProgressDialogsetProgress方法,可以在文件下载时调用它来更新进度级别。

我在许多应用程序中看到的最好的情况是,它们自定义了进度对话框的属性,使进度对话框的外观和感觉比库存版本更好。很好地保持用户的一些动画,如青蛙,大象或可爱的猫/小狗。任何有进度对话框的动画都能吸引用户,他们不喜欢长时间等待。

使用Android查询库,确实很酷。你可以将它改为使用ProgressDialog,就像你在其他例子中看到的那样,这个将从布局中显示进度视图,并在完成后隐藏它。

File target = new File(new File(Environment.getExternalStorageDirectory(), "ApplicationName"), "tmp.pdf");new AQuery(this).progress(R.id.progress_view).download(_competition.qualificationScoreCardsPdf(), target, new AjaxCallback<File>() {public void callback(String url, File file, AjaxStatus status) {if (file != null) {// do something with file}}});
我已经修改了AsyncTask类,以处理在相同上下文中创建progressDialog。我认为以下代码将更可重用。(它可以从任何传递上下文,目标文件,对话框消息的活动调用)

public static class DownloadTask extends AsyncTask<String, Integer, String> {private ProgressDialog mPDialog;private Context mContext;private PowerManager.WakeLock mWakeLock;private File mTargetFile;//Constructor parameters :// @context (current Activity)// @targetFile (File object to write,it will be overwritten if exist)// @dialogMessage (message of the ProgresDialog)public DownloadTask(Context context,File targetFile,String dialogMessage) {this.mContext = context;this.mTargetFile = targetFile;mPDialog = new ProgressDialog(context);
mPDialog.setMessage(dialogMessage);mPDialog.setIndeterminate(true);mPDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);mPDialog.setCancelable(true);// reference to instance to use inside listenerfinal DownloadTask me = this;mPDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {@Overridepublic void onCancel(DialogInterface dialog) {me.cancel(true);}});Log.i("DownloadTask","Constructor done");}
@Overrideprotected String doInBackground(String... sUrl) {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 fileif (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {return "Server returned HTTP " + connection.getResponseCode()+ " " + connection.getResponseMessage();}Log.i("DownloadTask","Response " + connection.getResponseCode());
// this will be useful to display download percentage// might be -1: server did not report the lengthint fileLength = connection.getContentLength();
// download the fileinput = connection.getInputStream();output = new FileOutputStream(mTargetFile,false);
byte data[] = new byte[4096];long total = 0;int count;while ((count = input.read(data)) != -1) {// allow canceling with back buttonif (isCancelled()) {Log.i("DownloadTask","Cancelled");input.close();return null;}total += count;// publishing the progress....if (fileLength > 0) // only if total length is knownpublishProgress((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;}@Overrideprotected void onPreExecute() {super.onPreExecute();// take CPU lock to prevent CPU from going off if the user// presses the power button during downloadPowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,getClass().getName());mWakeLock.acquire();
mPDialog.show();
}
@Overrideprotected void onProgressUpdate(Integer... progress) {super.onProgressUpdate(progress);// if we get here, length is known, now set indeterminate to falsemPDialog.setIndeterminate(false);mPDialog.setMax(100);mPDialog.setProgress(progress[0]);
}
@Overrideprotected void onPostExecute(String result) {Log.i("DownloadTask", "Work Done! PostExecute");mWakeLock.release();mPDialog.dismiss();if (result != null)Toast.makeText(mContext,"Download error: "+result, Toast.LENGTH_LONG).show();elseToast.makeText(mContext,"File Downloaded", Toast.LENGTH_SHORT).show();}}

权限

  <uses-permission android:name="android.permission.INTERNET" /><uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE" />

使用# 0

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.app.Dialog;import android.os.Bundle;import android.os.Environment;import android.view.View;import android.view.Window;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;
public class DownloadFileUseHttpURLConnection extends Activity {
ProgressBar pb;Dialog dialog;int downloadedSize = 0;int totalSize = 0;TextView cur_val;String dwnload_file_path ="http://coderzheaven.com/sample_folder/sample_file.png";@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);
Button b = (Button) findViewById(R.id.b1);b.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {showProgress(dwnload_file_path);
new Thread(new Runnable() {public void run() {downloadFile();}}).start();}});}
void downloadFile(){
try {URL url = new URL(dwnload_file_path);HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setRequestMethod("GET");urlConnection.setDoOutput(true);
//connecturlConnection.connect();
//set the path where we want to save the fileFile SDCardRoot = Environment.getExternalStorageDirectory();//create a new file, to save the downloaded fileFile file = new File(SDCardRoot,"downloaded_file.png");
FileOutputStream fileOutput = new FileOutputStream(file);
//Stream used for reading the data from the internetInputStream inputStream = urlConnection.getInputStream();
//this is the total size of the file which we are downloadingtotalSize = urlConnection.getContentLength();
runOnUiThread(new Runnable() {public void run() {pb.setMax(totalSize);}});
//create a buffer...byte[] buffer = new byte[1024];int bufferLength = 0;
while ( (bufferLength = inputStream.read(buffer)) > 0 ) {fileOutput.write(buffer, 0, bufferLength);downloadedSize += bufferLength;// update the progressbar //runOnUiThread(new Runnable() {public void run() {pb.setProgress(downloadedSize);float per = ((float)downloadedSize/totalSize) *100;cur_val.setText("Downloaded " + downloadedSize +
"KB / " + totalSize + "KB (" + (int)per + "%)" );}});}//close the output stream when complete //fileOutput.close();runOnUiThread(new Runnable() {public void run() {// pb.dismiss(); // if you want close it..}});
} catch (final MalformedURLException e) {showError("Error : MalformedURLException " + e);e.printStackTrace();} catch (final IOException e) {showError("Error : IOException " + e);e.printStackTrace();}catch (final Exception e) {showError("Error : Please check your internet connection " +e);}}
void showError(final String err){runOnUiThread(new Runnable() {public void run() {Toast.makeText(DownloadFileDemo1.this, err,Toast.LENGTH_LONG).show();}});}
void showProgress(String file_path){dialog = new Dialog(DownloadFileDemo1.this);dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);dialog.setContentView(R.layout.myprogressdialog);dialog.setTitle("Download Progress");
TextView text = (TextView) dialog.findViewById(R.id.tv1);text.setText("Downloading file from ... " + file_path);cur_val = (TextView) dialog.findViewById(R.id.cur_pg_tv);cur_val.setText("Starting download...");dialog.show();
pb = (ProgressBar)dialog.findViewById(R.id.progress_bar);pb.setProgress(0);pb.setProgressDrawable(getResources().getDrawable(R.drawable.green_progress));}}

我正在为我现在使用的其他解决方案添加另一个答案,因为Android查询太大了,无法保持健康。所以我移动到https://github.com/amitshekhariitbhu/Fast-Android-Networking

    AndroidNetworking.download(url,dirPath,fileName).build().setDownloadProgressListener(new DownloadProgressListener() {public void onProgress(long bytesDownloaded, long totalBytes) {bar.setMax((int) totalBytes);bar.setProgress((int) bytesDownloaded);}}).startDownload(new DownloadListener() {public void onDownloadComplete() {...}
public void onError(ANError error) {...}});

您可以使用LiveData和协程请看下面的要点来观察下载管理器的进度

https://gist.github.com/FhdAlotaibi/678eb1f4fa94475daf74ac491874fc0e

data class DownloadItem(val bytesDownloadedSoFar: Long = -1, val totalSizeBytes: Long = -1, val status: Int)
class DownloadProgressLiveData(private val application: Application, private val requestId: Long) : LiveData<DownloadItem>(), CoroutineScope {
private val downloadManager by lazy {application.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager}
private val job = Job()
override val coroutineContext: CoroutineContextget() = Dispatchers.IO + job
override fun onActive() {super.onActive()launch {while (isActive) {val query = DownloadManager.Query().setFilterById(requestId)val cursor = downloadManager.query(query)if (cursor.moveToFirst()) {val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))Timber.d("Status $status")when (status) {DownloadManager.STATUS_SUCCESSFUL,DownloadManager.STATUS_PENDING,DownloadManager.STATUS_FAILED,DownloadManager.STATUS_PAUSED -> postValue(DownloadItem(status = status))else -> {val bytesDownloadedSoFar = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))val totalSizeBytes = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))postValue(DownloadItem(bytesDownloadedSoFar.toLong(), totalSizeBytes.toLong(), status))}}if (status == DownloadManager.STATUS_SUCCESSFUL || status == DownloadManager.STATUS_FAILED)cancel()} else {postValue(DownloadItem(status = DownloadManager.STATUS_FAILED))cancel()}cursor.close()delay(300)}}}
override fun onInactive() {super.onInactive()job.cancel()}
}

重要的

AsyncTask在Android 11中已弃用。

欲了解更多信息,请查看以下帖子

可能应该移动到谷歌建议的并发框架

我们可以使用协程和工作管理器在kotlin中下载文件。

在build.gradle中添加一个依赖

    implementation "androidx.work:work-runtime-ktx:2.3.0-beta01"implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1"

WorkManager类

    import android.content.Contextimport android.os.Environmentimport androidx.work.CoroutineWorkerimport androidx.work.WorkerParametersimport androidx.work.workDataOfimport com.sa.chat.utils.Const.BASE_URL_IMAGEimport com.sa.chat.utils.Constantsimport kotlinx.coroutines.delayimport java.io.BufferedInputStreamimport java.io.Fileimport java.io.FileOutputStreamimport java.net.URL
class DownloadMediaWorkManager(appContext: Context, workerParams: WorkerParameters): CoroutineWorker(appContext, workerParams) {
companion object {const val WORK_TYPE = "WORK_TYPE"const val WORK_IN_PROGRESS = "WORK_IN_PROGRESS"const val WORK_PROGRESS_VALUE = "WORK_PROGRESS_VALUE"}
override suspend fun doWork(): Result {
val imageUrl = inputData.getString(Constants.WORK_DATA_MEDIA_URL)val imagePath = downloadMediaFromURL(imageUrl)
return if (!imagePath.isNullOrEmpty()) {Result.success(workDataOf(Constants.WORK_DATA_MEDIA_URL to imagePath))} else {Result.failure()}}
private suspend fun downloadMediaFromURL(imageUrl: String?): String? {
val file = File(getRootFile().path,"IMG_${System.currentTimeMillis()}.jpeg")
val url = URL(BASE_URL_IMAGE + imageUrl)val connection = url.openConnection()connection.connect()
val lengthOfFile = connection.contentLength// download the fileval input = BufferedInputStream(url.openStream(), 8192)// Output streamval output = FileOutputStream(file)
val data = ByteArray(1024)var total: Long = 0var last = 0
while (true) {
val count = input.read(data)if (count == -1) breaktotal += count.toLong()
val progress = (total * 100 / lengthOfFile).toInt()
if (progress % 10 == 0) {if (last != progress) {setProgress(workDataOf(WORK_TYPE to WORK_IN_PROGRESS,WORK_PROGRESS_VALUE to progress))}last = progressdelay(50)}output.write(data, 0, count)}
output.flush()output.close()input.close()
return file.path
}
private fun getRootFile(): File {
val rootDir = File(Environment.getExternalStorageDirectory().absolutePath + "/AppName")
if (!rootDir.exists()) {rootDir.mkdir()}
val dir = File("$rootDir/${Constants.IMAGE_FOLDER}/")
if (!dir.exists()) {dir.mkdir()}return File(dir.absolutePath)}}

开始下载通过工作管理器在活动类

 private fun downloadImage(imagePath: String?, id: String) {
val data = workDataOf(WORK_DATA_MEDIA_URL to imagePath)val downloadImageWorkManager = OneTimeWorkRequestBuilder<DownloadMediaWorkManager>().setInputData(data).addTag(id).build()
WorkManager.getInstance(this).enqueue(downloadImageWorkManager)
WorkManager.getInstance(this).getWorkInfoByIdLiveData(downloadImageWorkManager.id).observe(this, Observer { workInfo ->
if (workInfo != null) {when {workInfo.state == WorkInfo.State.SUCCEEDED -> {progressBar?.visibility = View.GONEivDownload?.visibility = View.GONE}workInfo.state == WorkInfo.State.FAILED || workInfo.state == WorkInfo.State.CANCELLED || workInfo.state == WorkInfo.State.BLOCKED -> {progressBar?.visibility = View.GONEivDownload?.visibility = View.VISIBLE}else -> {if(workInfo.progress.getString(WORK_TYPE) == WORK_IN_PROGRESS){val progress = workInfo.progress.getInt(WORK_PROGRESS_VALUE, 0)progressBar?.visibility = View.VISIBLEprogressBar?.progress = progressivDownload?.visibility = View.GONE
}}}}})
}
    private class DownloadTask extends AsyncTask<String, Integer, String> {
private PowerManager.WakeLock mWakeLock;String onlinePathBundle, onlinePathMusic, offlinePathBundle, offlinePathMusic;
CircleProgressBar progressBar;RelativeLayout rl_progress;ImageView btn_download;TextView tv_progress;
public DownloadTask(String onlinePathBundle, String onlinePathMusic, String offlinePathBundle, String offlinePathMusic, CircleProgressBar progressBar, RelativeLayout rl_progress, ImageView btn_download,TextView tv_progress) {this.offlinePathBundle = offlinePathBundle;this.offlinePathMusic = offlinePathMusic;this.progressBar = progressBar;this.rl_progress = rl_progress;this.btn_download = btn_download;this.tv_progress = tv_progress;this.onlinePathBundle = onlinePathBundle;this.onlinePathMusic = onlinePathMusic;}
@Overrideprotected void onPreExecute() {super.onPreExecute();progressBar.setMax(100);rl_progress.setVisibility(View.VISIBLE);btn_download.setVisibility(View.GONE);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());mWakeLock.acquire();}
@Overrideprotected String doInBackground(String... sUrl) {String firstResult = downTask(onlinePathBundle, 1, offlinePathBundle);String seondResult = downTask(onlinePathMusic, 2, offlinePathMusic);if (firstResult != null) {Toast.makeText(SplashActivity.this, "Download error: " + firstResult, Toast.LENGTH_LONG).show();return firstResult;} else {return seondResult;}
}
@Overrideprotected void onProgressUpdate(Integer... progress) {super.onProgressUpdate(progress);progressBar.setProgress(progress[0]);tv_progress.setText(progress[0] + "%");
}
@Overrideprotected void onPostExecute(String result) {super.onPostExecute(result);rl_progress.setVisibility(View.GONE);btn_download.setVisibility(View.VISIBLE);if (result != null) {Toast.makeText(SplashActivity.this, "Download error: " + result, Toast.LENGTH_LONG).show();} else {rl_progress.setVisibility(View.GONE);btn_download.setVisibility(View.GONE);}
}
public String downTask(String onlineNewPath, int numberOfTask, String offlineNewPath) {InputStream input = null;OutputStream output = null;HttpURLConnection connection = null;try {URL url = new URL(onlineNewPath);connection = (HttpURLConnection) url.openConnection();connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report// instead of the fileif (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 lengthint fileLength = connection.getContentLength();
// download the fileinput = connection.getInputStream();output = new FileOutputStream(offlineNewPath);
byte data[] = new byte[4096];long total = 0;int count;while ((count = input.read(data)) != -1) {// allow canceling with back buttonif (isCancelled()) {input.close();return null;}total += count;// publishing the progress....if (fileLength > 0) {// only if total length is knownif (numberOfTask == 1) {publishProgress((int) (total * 50 / fileLength));} else if (numberOfTask == 2) {publishProgress(((int) (total * 50 / fileLength)) + 50);}
}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;}}
我遇到了一个简单文件下载获取,重要的是它有存储访问框架内容提供者URI的支持。如果有人还在寻找,可能会发现它有帮助

实现“androidx.tonyodev.fetch2: xfetch2:3.1.6"/ / AndroidX

实现“com.tonyodev.fetch2: fetch2:3.0.12"/ /支持自由

需要的权限,如果你不是使用应用程序特定的目录。

# 0

# 0

网络权限

# 0

private Fetch fetch;FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this).setDownloadConcurrentLimit(3)//Concurrent Download limit.build();
fetch = Fetch.Impl.getInstance(fetchConfiguration);
String url = "http:www.example.com/test.txt";//URL of fileString file = "/downloads/test.txt";//Path of file        
final Request request = new Request(url, file);request.setPriority(Priority.HIGH);request.setNetworkType(NetworkType.ALL);//Preferred network typerequest.addHeader("clientKey", "SD78DF93_3947&MVNGHE1WONG");//Auth header if any        
fetch.enqueue(request, updatedRequest -> {//Request was successfully enqueued for download.}, error -> {//An error occurred enqueuing the request.});
}

倾听更新和进展

FetchListener fetchListener = new FetchListener() {@Overridepublic void onQueued(@NotNull Download download, boolean waitingOnNetwork) {if (request.getId() == download.getId()) {showDownloadInList(download);}}
@Overridepublic void onCompleted(@NotNull Download download) {
}
@Overridepublic void onError(@NotNull Download download) {Error error = download.getError();}
@Overridepublic void onProgress(@NotNull Download download, long etaInMilliSeconds, long downloadedBytesPerSecond) {if (request.getId() == download.getId()) {updateDownload(download, etaInMilliSeconds);}int progress = download.getProgress();}
@Overridepublic void onPaused(@NotNull Download download) {
}
@Overridepublic void onResumed(@NotNull Download download) {
}
@Overridepublic void onCancelled(@NotNull Download download) {
}
@Overridepublic void onRemoved(@NotNull Download download) {
}
@Overridepublic void onDeleted(@NotNull Download download) {
}};
fetch.addListener(fetchListener);
//Remove listener when done.fetch.removeListener(fetchListener);

此示例代码取自所有者页面,所有信用都指向Tonyo弗朗西斯