Скачайте файл с Android и покажите прогресс в ProgressDialog


1047

Я пытаюсь написать простое приложение, которое обновляется. Для этого мне нужна простая функция, которая может загрузить файл и показать текущий прогресс в ProgressDialog. Я знаю, как это сделать ProgressDialog, но я не уверен, как отобразить текущий прогресс и как загрузить файл в первую очередь.


2
Я надеюсь, что ниже ссылка может помочь вам ... androidhive.info/2012/04/…
Ганеш Катикар

1
Для быстрого исправления обратитесь к этому ответу
Рича

stackoverflow.com/a/43069239/3879214 проверить этот ответ
Anu Martin

Ответы:


1874

Есть много способов загрузки файлов. После я опубликую наиболее распространенные способы; Вам решать, какой метод лучше подходит для вашего приложения.

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"/>

Это не может быть проще, я думаю. Просто возьмите последнюю банку с 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, который может быть встроен в пользовательский интерфейс вашего приложения. Кроме того, вы можете использовать уведомление, чтобы проинформировать пользователя о ходе выполнения задачи. Для более подробной информации Ссылка


8
DownloadManager является частью ОС, что означает, что он всегда будет доступен в ГБ + и не может быть удален.
Кристиан

17
В ответе Кристиана есть проблема. Потому что код « 1. Используйте AsyncTask и покажите прогресс загрузки в диалоге » делает connection.connect (); затем InputStream input = new BufferedInputStream (url.openStream ()); Код делает 2 подключения к серверу. Мне удалось изменить это поведение, обновив код следующим образом InputStream input = new BufferedInputStream (connection.getInputStream ());
НЛЛ

99
Я хочу, чтобы документация по Android была такой лаконичной.
Лу Морда

12
Рекомендуется close()использовать потоки ( inputи output) finallyвместо try, в противном случае, если возникнет какое-либо исключение close(), у вас будут незакрытые потоки.
Pang

32
Не используйте жесткий код / sdcard/используйте Environment.getExternalStorageDirectory()вместо этого.
Нима Г

106

Не забудьте добавить разрешения в ваш файл манифеста, если вы собираетесь загружать материалы из Интернета!

<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>

1
Я уверен, что вам не нужен READ_PHONE_STATE, и вам определенно не нужен WRITE_EXTERNAL_STORAGE; это опасное разрешение, которого можно избежать с помощью Storage Access Framework.
Восстановить Монику

32

Да код выше будет работать. Если вы обновляете свой progressbarин onProgressUpdateиз Asynctask и при нажатии кнопки назад или закончить Вашу деятельность AsyncTaskтеряет свой трек с UI й когда вы вернетесь к вашей деятельности, даже если загрузка выполняется в фоновом режиме , вы увидите Нет обновления на прогрессбар. Итак, 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;
    }
}

1
Это именно моя проблема. Можете ли вы сказать мне, как сделать задачу таймера для обновления индикатора выполнения? Как получить обновление значений из запущенной
AsyncTask

2
okk возьмите глобальную или статическую переменную, чтобы обновить значения из asynctask ... или вы можете вставить ее в базу данных для безопасной стороны ... так что закрытие приложения не помешает ... и когда вы перезапустите действие, где вы хотите обновить свой UI запустить поток пользовательского интерфейса .. см. Пример ниже
Sheal

Пользовательский интерфейс должен иметь свежую ссылку .. Как недавно инициализированный ProgressBar в моем случае
Sheal

@sheetal, но работает без вашего кода! Почему?! Мое устройство Xperia P с Android 4.0.4. Я определил статическую логическую переменную, для которой onPreExecute устанавливает значение true, а onPostExecute устанавливает значение false. Он показывает, загружаем мы или нет, поэтому мы можем проверить, равна ли переменная значению true, показать предыдущий диалог индикатора выполнения.
Бехзад

@ лист твой код немного мракобесен, можешь дать мне совет?
iSun

17

Я бы порекомендовал вам использовать мой Project Netroid , он основан на Volley . Я добавил некоторые функции, такие как обратный вызов нескольких событий, управление загрузкой файлов. Это может помочь.


2
Поддерживает ли он загрузку нескольких файлов? Я работаю над проектом, который позволяет пользователю загружать по расписанию. В определенное время (например, в 12:00) служба загрузит все ссылки, выбранные пользователем ранее. Я имею в виду, что сервис, который мне нужен, должен иметь возможность ставить в очередь ссылки на скачивание, а затем загружать все из них (по одному или параллельно, в зависимости от пользователя). Спасибо
Хоанг Тринь

@piavgh да, все, что вы хотели, это удовлетворить, вы можете проверить мой пример apk, он включал демонстрацию управления загрузкой файлов.
VinceStyling

Отличная библиотека! Работает хорошо, хотя у меня возникают проблемы с загрузкой контента с URL-адресов HTTPS, так как хотя вы можете установить собственное SSLSocketFactoryдобавление, это HostnameVerifierневозможно, и мне нужен такой верификатор. Возник вопрос по этому поводу.
DarkCygnus

ссылка на залп. -> Не найдено
Румит Патель

14

Я изменил 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();
    }
}

Не забыли добавить разрешение блокировки пробуждения <использование-разрешения android: name = "android.permission.WAKE_LOCK" />
Hitesh Sahu

8

Не забудьте заменить «/ SDCard ...» на новый файл ( «/ шоссе / SDCard / ...») в противном случае вы получите FileNotFoundException


В части 2 диалоговое окно «Загрузка из сервиса» не может отображать приращение прогресса. это прямое возвращение 100, так что если 100 это прямая проверка setprogress 100 и прогресс, как увеличить прогресс, он отображает только 0 прогресса, но на самом деле загрузка выполняется
Nirav Mehta

он показывает только 0% из 100 только другой работы должным образом
Нирав Мехта

14
Не делать это! Существует Environment.getExternalStorageDirectory().getAbsolutePath()для получения пути к SDCard. Также не забудьте проверить, смонтировано ли внешнее хранилище - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)( Mannaz )
naXa

8

Я нашел этот пост очень полезным, он использует loopJ для загрузки файла, он имеет только одну простую функцию, будет полезен для некоторых новых ребят из Android.


7

Когда я начинал изучать разработку Android, я понял, что ProgressDialogэто путь. Существует setProgressметод, ProgressDialogкоторый можно вызвать для обновления уровня прогресса при загрузке файла.

Лучшее, что я видел во многих приложениях, это то, что они настраивают атрибуты этого диалогового окна прогресса, чтобы лучше видеть и чувствовать диалоговое окно прогресса, чем стандартная версия. Хорошо, когда пользователь увлекается анимацией, такой как лягушка, слон или милые кошки / щенки. Любая анимация в диалоге прогресса привлекает пользователей, и им не хочется долго ждать.


Ссылка не работает -> Показывать
индикатор

5

Мой личный совет - использовать диалоговое окно « Прогресс» и создавать его перед выполнением или OnPreExecute()часто начинать с него , если вы используете горизонтальный стиль индикатора выполнения диалогового окна прогресса. Оставшаяся часть заключается в оптимизации алгоритма doInBackground.


5

Используйте библиотеку запросов 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  
        } 
    }
});

Дело в том, что я перестал его использовать, потому что он не поддерживается, поэтому не думайте, что это хороший ответ.
Ренетик

3

Я добавляю другой ответ для другого решения, которое я использую сейчас, потому что 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) {
            ...
        }
    });

2

права доступа

  <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));  
  }
}

1

Вы можете наблюдать за прогрессом менеджера загрузок, используя 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: 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()
    }

}

Можете ли вы привести пример использования этого класса?
Карим Каримов

0

Важный

AsyncTask устарела в Android 11.

Для получения дополнительной информации, пожалуйста, проверьте следующие сообщения

Вероятно, следует перейти к Concorency Framework, как предложено Google


0

Мы можем использовать сопрограмму и рабочий менеджер для загрузки файлов в 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.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

                                    }
                                }
                            }
                        }
                    })

        }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.