Завантажте файл з Android та показуючи прогрес у ProgressDialog


1046

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


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

1
Для швидкого виправлення зверніться до цієї відповіді
Річа

stackoverflow.com/a/43069239/3879214 перевірити цю відповідь
Ану Мартін

Відповіді:


1873

Існує багато способів завантаження файлів. Слідом я розміщу найпоширеніші способи; вирішувати, який метод краще для вашої програми, вирішувати саме вам.

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 - здійснювати дзвінки на зовнішній REST apis у фоновому режимі та легко розміщувати результати в інтерфейсі. Якщо ви робите щось подібне у своєму додатку, це може бути дуже корисним.

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 є частиною ОС, а це означає, що вона завжди буде доступна в GB + і не може бути видалена.
Крістіан

17
У відповіді Крістіана є проблема. Оскільки код на " 1. Використовуйте AsyncTask і показуйте хід завантаження в діалоговому вікні " does connection.connect (); потім InputStream input = новий BufferedInputStream (url.openStream ()); код здійснює 2 з'єднання з сервером. Мені вдалося змінити цю поведінку, оновивши код наступним чином InputStream input = new BufferedInputStream (connection.getInputStream ());
nLL

99
Я б хотів, щоб документація на Android була такою стислою.
Лу Морда

12
Запропоновано close()потокам ( inputі output) в, finallyа не в tryіншому випадку, якщо будь-який виняток буде кинуто раніше close(), у вас є незакриті потоки.
Панг

32
Не sdcard/використовуйте Environment.getExternalStorageDirectory()замість цього жорсткого коду / використання .
Nima G

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

32

Так код вище буде працювати. Якщо ви оновлюєте свій progressbarін onProgressUpdateз Asynctask і при натисканні кнопки назад або закінчити Вашу діяльність AsyncTaskвтрачає свій трек з UI й коли ви повернетеся до вашої діяльності, навіть якщо завантаження виконується в фоновому режимі , ви побачите відсутність оновлень на панелі прогресу Отже, OnResume()спробуйте запустити потік, як runOnUIThreadіз завданням таймера, який оновлює ур 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, що працює позаду
user1417127

2
OKK взяти глобальну або статичну змінну до того, де оновити свої значення з асинтактаска ... або ти можеш вставити його в DataBase для безпечної сторони ... так що закриття програми не буде перешкоджати. Інтерфейс користувача запускає потік інтерфейсу. Дивіться приклад нижче
sheetal

Користувальницький інтерфейс повинен мати нове посилання. Як у нещодавно ініталізованому ProgressBar у моєму випадку
sheetal

@sheetal, але це прекрасно працює без вашого коду! Чому ?! Мій пристрій - Xperia P з Android 4.0.4. Я визначив статичну булеву змінну, що onPreExecute встановлює її в true, а onPostExecute встановлює значення false. Це показує, що ми завантажуємо чи ні, тому ми можемо перевірити, чи відповідає змінна рівню true, показати попереднє діалогове вікно діапазону прогресу.
Бехзад

@sheetal ваш код мало затьмарений, чи можете ви порадити мені поради?
iSun

17

Я рекомендую вам використовувати мій проект Netroid , він заснований на Volley . Я додав до нього деякі функції, такі як зворотний виклик з кількома подіями, управління завантаженням файлів. Це може допомогти.


2
Чи підтримує це багаторазове завантаження файлів? Я працюю над проектом, який дозволяє користувачеві завантажувати заплановані програми. У певний час (наприклад, 12:00) служба завантажить все посилання, яке користувач обрав раніше. Я маю на увазі, що потрібна мені послуга повинна мати можливість чергувати посилання для завантаження, а потім завантажувати всі (по одному чи паралельно, залежно від користувача). Дякую
Hoang Trinh

@piavgh так, все, що ви хотіли, було задоволено, ви можете замовити мій зразок apk, він включає в себе демо-файл управління завантаженням файлів.
VinceStyling

Чудова бібліотека! Добре працює, хоча у мене виникають проблеми із завантаженням вмісту з URL-адреси HTTPS, тому що хоч можна встановити власне SSLSocketFactoryдодавання. HostnameVerifierНеможливо, і мені потрібна така перевірка. З цього питання виникло питання .
DarkCygnus

посилання на Воллі. -> Не знайдено
Rumit Patel

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

Ви не забули додати дозвіл блокування Wake <use-дозволу android: name = "android.permission.WAKE_LOCK" />
Hitesh Sahu

8

Не забудьте замінити "/ sdcard ..." на новий файл ("/ mnt / 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

Поки я починав вивчати розвиток андроїд, я дізнався, що ProgressDialogце шлях. Існує setProgressметод, за допомогою ProgressDialogякого можна застосувати оновлення рівня прогресу під час завантаження файлу.

Найкраще, що я бачив у багатьох програмах, - це те, що вони налаштовують атрибути цього діалогу прогресу, щоб надати кращому вигляду та відчуттю діалогу прогресу, ніж версії акцій. Добре підтримувати користувача, який займається анімацією, як жаба, слон чи милі коти / щенята. Будь-яка анімація з діалоговим вікном ходу приваблює користувачів, і їм не здається, що їх довго чекають.



5

Моя особиста порада - використовувати діалог прогресу та створювати його перед виконанням або ініціювати OnPreExecute(), часто публікувати прогрес, якщо ви використовуєте горизонтальний стиль рядка прогресу діалогового вікна прогресу. Решта - оптимізація алгоритму роботи doInBackground.


5

Використовуйте бібліотеку 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  
        } 
    }
});

Справа в тому, що я припинив його використовувати, тому що його невмілене так не вважаю, що це вже хороша відповідь.
Ренетик

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

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