Я пытаюсь написать простое приложение, которое обновляется. Для этого мне нужна простая функция, которая может загрузить файл и показать текущий прогресс в ProgressDialog
. Я знаю, как это сделать ProgressDialog
, но я не уверен, как отобразить текущий прогресс и как загрузить файл в первую очередь.
Загрузите файл с Android и покажите прогресс в ProgressDialog
Ответы (15)
Есть много способов скачать файлы. Ниже я опубликую наиболее распространенные способы; Вам решать, какой метод лучше для вашего приложения.
1. Используйте AsyncTask
и покажите прогресс загрузки в диалоговом окне.
Этот метод позволит вам одновременно выполнять некоторые фоновые процессы и обновлять пользовательский интерфейс (в этом случае мы обновим индикатор выполнения).
Импорт:
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 activity
ProgressDialog mProgressDialog;
// instantiate it within the onCreate method
mProgressDialog = 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 fired
final DownloadTask downloadTask = new DownloadTask(YourActivity.this);
downloadTask.execute("the url to the file you want to download");
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public 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 here
private class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
private PowerManager.WakeLock mWakeLock;
public DownloadTask(Context context) {
this.context = context;
}
@Override
protected 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 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("/sdcard/file_name.extension");
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;
}
Вышеупомянутый метод (doInBackground
) всегда выполняется в фоновом потоке. Вы не должны выполнять здесь никаких задач пользовательского интерфейса. С другой стороны, onProgressUpdate
и onPreExecute
выполняются в потоке пользовательского интерфейса, поэтому вы можете изменить индикатор выполнения:
@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();
mProgressDialog.show();
}
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to false
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(100);
mProgressDialog.setProgress(progress[0]);
}
@Override
protected void onPostExecute(String result) {
mWakeLock.release();
mProgressDialog.dismiss();
if (result != null)
Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
else
Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
}
Для этого вам нужно разрешение WAKE_LOCK.
<uses-permission android:name="android.permission.WAKE_LOCK" />
2. Загрузить из службы
Большой вопрос здесь: как мне обновить мои действия из службы?. В следующем примере мы собираемся использовать два класса, о которых вы можете не знать: ResultReceiver
и IntentService
. ResultReceiver
- это тот, который позволит нам обновлять наш поток из службы; IntentService
является подклассом Service
, который порождает поток для выполнения фоновой работы оттуда (вы должны знать, что Service
выполняется фактически в том же потоке вашего приложения; когда вы расширяете Service
, вы должны вручную создавать новые потоки для выполнения операций блокировки ЦП) .
Сервис загрузки может выглядеть так:
public class DownloadService extends IntentService {
public static final int UPDATE_PROGRESS = 8344;
public DownloadService() {
super("DownloadService");
}
@Override
protected void onHandleIntent(Intent intent) {
String urlToDownload = intent.getStringExtra("url");
ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");
try {
//create url and connect
URL url = new URL(urlToDownload);
URLConnection connection = url.openConnection();
connection.connect();
// this will be useful so that you can show a typical 0-100% progress bar
int fileLength = connection.getContentLength();
// download the file
InputStream 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 streams
output.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 downloader
mProgressDialog.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);
Вот какие ResultReceiver
вступают в игру:
private class DownloadReceiver extends ResultReceiver{
public DownloadReceiver(Handler handler) {
super(handler);
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == DownloadService.UPDATE_PROGRESS) {
int progress = resultData.getInt("progress"); //get the progress
dialog.setProgress(progress);
if (progress == 100) {
dialog.dismiss();
}
}
}
}
2.1 Использование библиотеки Groundy
Groundy - это библиотека, которая в основном помогает запускать фрагменты кода в фоновой службе. , и он основан на концепции ResultReceiver
, показанной выше. В настоящее время эта библиотека устарела. Вот как будет выглядеть весь код:
Действие, в котором вы показываете диалог ...
public class MainActivity extends Activity {
private ProgressDialog mProgressDialog;
@Override
public 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()) {
@Override
protected 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;
}
}
};
}
Реализация GroundyTask
, используемая Groundy для загрузки файла и отображения прогресса:
public class DownloadTask extends GroundyTask {
public static final String PARAM_URL = "com.groundy.sample.param.url";
@Override
protected 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"/>
Я думаю, что нет ничего проще. Просто возьмите последнюю версию jar из Github, и все готово. Имейте в виду, что основная цель Groundy - выполнять вызовы внешнего API REST в фоновой службе и легко публиковать результаты в пользовательском интерфейсе. Если вы делаете что-то подобное в своем приложении, это может быть действительно полезно.
2.2 Используйте https://github.com/koush/ion
3. Используйте класс 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 app
if (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 file
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
Ход загрузки будет отображаться на панели уведомлений.
Последние мысли
Первый и второй методы - лишь верхушка айсберга. Если вы хотите, чтобы ваше приложение было надежным, нужно помнить о многих вещах. Вот краткий список:
- Вы должны проверить, есть ли у пользователя доступ к Интернету.
- Убедитесь, что у вас есть необходимые разрешения (
INTERNET
иWRITE_EXTERNAL_STORAGE
); такжеACCESS_NETWORK_STATE
, если вы хотите проверить наличие интернета. - Убедитесь, что каталог, в который вы собираетесь загружать файлы, существует и имеет права на запись.
- Если загрузка слишком велика, вы можете реализовать способ возобновления загрузки, если предыдущие попытки не удались.
- Пользователи будут благодарны, если вы позволите им прервать загрузку.
Если вам не нужен подробный контроль над процессом загрузки, рассмотрите возможность использования DownloadManager
(3), потому что он уже обрабатывает большинство элементов, перечисленных выше.
Но также учтите, что ваши потребности могут измениться. Например, DownloadManager
не кэширует ответы. Он будет слепо загружать один и тот же большой файл несколько раз. Нет простого способа исправить это постфактум. Если вы начинаете с базового HttpURLConnection
(1, 2), тогда все, что вам нужно, это добавить HttpResponseCache
. Таким образом, начальные усилия по изучению основных стандартных инструментов могут быть хорошей инвестицией.
Этот класс объявлен устаревшим на уровне API 26. ProgressDialog - это модальное диалоговое окно, которое не позволяет пользователю взаимодействовать с приложением. Вместо использования этого класса вы должны использовать индикатор выполнения, например ProgressBar, который можно встроить в пользовательский интерфейс вашего приложения. Кроме того, вы можете использовать уведомление, чтобы информировать пользователя о ходе выполнения задачи. Для получения дополнительных сведений Ссылка
https
URL после conexion.connect();
. Во время отладки я подтвердил, что в объекте conecion
с именем connected
есть флаг, установленный на false
. Вы знаете, как это решить? Или какие дополнительные шаги нужно сделать для успешной загрузки https? спасибо!
- person Felipe Sabino; 06.12.2011
close()
потоки (input
и output
) в finally
вместо try
, иначе, если какое-либо исключение будет выброшено до close()
, у вас будут незакрытые потоки.
- person Pang; 26.01.2013
WRITE_EXTERNAL_STORAGE
разрешения? объявление. stackoverflow.com/q/16210894/492624
- person Marek Sebera; 27.04.2013
DownloadManager
Content Provider: stackoverflow.com/questions/5069919/
- person Cristian; 21.06.2013
sdcard/
вместо этого Environment.getExternalStorageDirectory()
.
- person Nima G; 07.04.2014
progressbar
и пользователь покидает страницу. В это время индикатор выполнения отключает окно, и информация о ходе выполнения теряется. Любое решение проблемы? БЛАГОДАРНОСТЬ!
- person Alston; 30.09.2014
Exception
на IOException
в блоке catch
- person zackygaurav; 22.11.2015
Byte[] data = new byte[4096]
Не могли бы вы объяснить функцию массива данных, более конкретно его размер (4096), назначение (что, если я использую 1024) и лучшие практики?
- person Muhammad Babar; 01.08.2016
java.io.IOException: unexpected end of stream
какие-нибудь идеи?
- person Muhammad Babar; 02.08.2016
IllegalArgumentException: Can only download HTTP/HTTPS URIs
при попытке запросить ресурс в локальной сети в URL-адресе, кто-нибудь знает, как заставить его работать таким образом?
- person user1676874; 11.01.2017
Не забудьте добавить разрешения к вашему файлу манифеста, если вы собираетесь загружать что-то из Интернета!
<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>
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:debuggable="true">
</application>
</manifest>
Да, приведенный выше код будет работать. Но если вы обновляете свой progressbar
в onProgressUpdate
из Asynctask
и нажимаете кнопку «Назад» или завершаете свою деятельность, AsyncTask
теряет свой след в вашем пользовательском интерфейсе. И когда вы вернетесь к своей деятельности, даже если загрузка выполняется в фон вы не увидите обновлений на индикаторе выполнения. Итак, на OnResume()
попробуйте запустить поток, такой как runOnUIThread
, с задачей таймера, которая обновляет ur progressbar
значениями, обновляющимися из AsyncTask
рабочего фона.
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(){
@Override
public void handleMessage(Message msg) {
progress.setProgress(msg.what);
}
};
Не забудьте удалить цепочку, если ваша активность не отображается.
private void destroyRunningThreads() {
if (background != null) {
background.interrupt();
background=null;
}
}
Я бы порекомендовал вам использовать мой проект Netroid, он основан на Volley. Я добавил к нему некоторые функции, такие как обратный вызов нескольких событий, управление загрузкой файлов. Это могло бы помочь.
SSLSocketFactory
, добавление HostnameVerifier
невозможно, и мне нужен такой верификатор. Поднял проблему по этому поводу.
- person DarkCygnus; 23.05.2018
Я изменил класс 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 listener
final DownloadTask me = this;
mPDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
me.cancel(true);
}
});
Log.i("DownloadTask","Constructor done");
}
@Override
protected 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 file
if (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 length
int fileLength = connection.getContentLength();
// download the file
input = 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 button
if (isCancelled()) {
Log.i("DownloadTask","Cancelled");
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;
}
@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) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
mPDialog.show();
}
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to false
mPDialog.setIndeterminate(false);
mPDialog.setMax(100);
mPDialog.setProgress(progress[0]);
}
@Override
protected 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();
else
Toast.makeText(mContext,"File Downloaded", Toast.LENGTH_SHORT).show();
}
}
Не забудьте заменить "/ sdcard ..." на новый файл ("/ mnt / sdcard / ..."), иначе вы получите FileNotFoundException
Environment.getExternalStorageDirectory().getAbsolutePath()
для получения пути к sdcard. Также не забудьте проверить, смонтировано ли внешнее хранилище - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
(Mannaz < / а>)
- person naXa; 15.04.2014
Я нашел это сообщение в блоге очень полезным. Его использование loopJ для загрузки файла, он имеет только одну простую функцию, которая будет полезна некоторым новичкам в Android.
Когда я начинал изучать разработку для Android, я понял, что ProgressDialog
- это правильный путь. Существует setProgress
метод ProgressDialog
, который можно вызвать для обновления уровня выполнения по мере загрузки файла.
Лучшее, что я видел во многих приложениях, - это то, что они настраивают атрибуты этого диалога прогресса, чтобы он выглядел лучше, чем стандартная версия. Хорошо держать пользователя в руках анимацией лягушки, слона или милых кошек / щенков. Любая анимация в диалоговом окне прогресса привлекает пользователей, и они не хотят, чтобы их заставляли долго ждать.
Мой личный совет - использовать Диалог выполнения и накапливать перед выполнением или начинать с OnPreExecute()
, часто публиковать прогресс, если вы используете горизонтальный стиль индикатора выполнения в диалоговом окне прогресса. Осталось оптимизировать алгоритм doInBackground
.
Используйте библиотеку Android Query, это действительно здорово. Вы можете изменить ее, чтобы использовать 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
}
}
});
Мы можем использовать сопрограмму и менеджер работы для загрузки файлов в котлине.
Добавить зависимость в 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.Context
import android.os.Environment
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.sa.chat.utils.Const.BASE_URL_IMAGE
import com.sa.chat.utils.Constants
import kotlinx.coroutines.delay
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import 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 file
val input = BufferedInputStream(url.openStream(), 8192)
// Output stream
val output = FileOutputStream(file)
val data = ByteArray(1024)
var total: Long = 0
var last = 0
while (true) {
val count = input.read(data)
if (count == -1) break
total += 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 = progress
delay(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.GONE
ivDownload?.visibility = View.GONE
}
workInfo.state == WorkInfo.State.FAILED || workInfo.state == WorkInfo.State.CANCELLED || workInfo.state == WorkInfo.State.BLOCKED -> {
progressBar?.visibility = View.GONE
ivDownload?.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.VISIBLE
progressBar?.progress = progress
ivDownload?.visibility = View.GONE
}
}
}
}
})
}
Разрешения
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Использование HttpURLConnection
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";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button) findViewById(R.id.b1);
b.setOnClickListener(new OnClickListener() {
@Override
public 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);
//connect
urlConnection.connect();
//set the path where we want to save the file
File SDCardRoot = Environment.getExternalStorageDirectory();
//create a new file, to save the downloaded file
File file = new File(SDCardRoot,"downloaded_file.png");
FileOutputStream fileOutput = new FileOutputStream(file);
//Stream used for reading the data from the internet
InputStream inputStream = urlConnection.getInputStream();
//this is the total size of the file which we are downloading
totalSize = 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 Query настолько велик и не обслуживается, чтобы оставаться здоровым. Итак, я перешел на эту 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/678eb1f4fa9441874daf74ac
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: CoroutineContext
get() = 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.
Для получения дополнительной информации, пожалуйста, ознакомьтесь со следующими сообщениями
- Android AsyncTask API не поддерживается в Android 11. Каковы альтернативы?
- https://developer.android.com/reference/android/os/AsyncTask
Вероятно, следует перейти на платформу Concorency Framework, как предлагает Google