Pertanyaan Unduh file dengan Android, dan tunjukkan kemajuan dalam ProgressDialog


Saya mencoba menulis aplikasi sederhana yang diperbarui. Untuk ini saya membutuhkan fungsi sederhana yang bisa mengunduh file dan menunjukkan kemajuan saat ini di sebuah ProgressDialog. Saya tahu cara melakukan itu ProgressDialog, tapi saya tidak yakin bagaimana menampilkan kemajuan saat ini dan cara mengunduh file di tempat pertama.


914
2018-06-12 10:22


asal


Jawaban:


Ada banyak cara untuk mengunduh file. Mengikuti saya akan memposting cara yang paling umum; terserah Anda untuk memutuskan metode mana yang lebih baik untuk aplikasi Anda.

1. Gunakan AsyncTask dan tunjukkan kemajuan unduhan dalam dialog

Metode ini akan memungkinkan Anda untuk menjalankan beberapa proses latar belakang dan memperbarui UI pada saat yang bersamaan (dalam hal ini, kami akan memperbarui bilah kemajuan).

Ini adalah kode contoh:

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

Itu AsyncTask akan terlihat seperti ini:

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

Metode di atas (doInBackground) berjalan selalu pada utas latar belakang. Anda seharusnya tidak melakukan tugas UI di sana. Di sisi lain, itu onProgressUpdate dan onPreExecute jalankan di utas UI, sehingga Anda dapat mengubah bilah kemajuan:

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

Agar ini berjalan, Anda memerlukan izin WAKE_LOCK.

<uses-permission android:name="android.permission.WAKE_LOCK" />

2. Unduh dari Layanan

Pertanyaan besar di sini adalah: bagaimana cara memperbarui aktivitas saya dari layanan?. Dalam contoh berikut, kita akan menggunakan dua kelas yang mungkin tidak Anda ketahui: ResultReceiver dan IntentService. ResultReceiver adalah salah satu yang akan memungkinkan kami memperbarui untaian kami dari layanan; IntentService adalah subkelas dari Service yang memunculkan utas untuk melakukan pekerjaan latar belakang dari sana (Anda harus tahu bahwa a Service sebenarnya berjalan di utas aplikasi Anda yang sama; ketika kamu meluas Service, Anda harus secara manual menelurkan thread baru untuk menjalankan operasi pemblokiran CPU).

Layanan unduhan dapat terlihat seperti ini:

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 {
            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());
            OutputStream output = new FileOutputStream("/sdcard/BarcodeScanner-debug.apk");

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

            output.flush();
            output.close();
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Bundle resultData = new Bundle();
        resultData.putInt("progress" ,100);
        receiver.send(UPDATE_PROGRESS, resultData);
    }
}

Tambahkan layanan ke manifes Anda:

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

Dan aktivitasnya akan terlihat seperti ini:

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

Ini dia ResultReceiver datang untuk bermain:

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");
            mProgressDialog.setProgress(progress);
            if (progress == 100) {
                mProgressDialog.dismiss();
            }
        }
    }
}

2.1 Gunakan pustaka Beralas

Berbobot adalah pustaka yang pada dasarnya membantu Anda menjalankan potongan kode di layanan latar belakang, dan itu didasarkan pada ResultReceiver konsep yang ditunjukkan di atas. Perpustakaan ini tidak lagi digunakan saat ini. Beginilah caranya seluruh kode akan terlihat seperti:

Kegiatan di mana Anda menunjukkan dialog ...

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

SEBUAH GroundyTask implementasi yang digunakan oleh Berbobot untuk mengunduh file dan menunjukkan perkembangannya:

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

Dan cukup tambahkan ini ke manifes:

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

Tidak bisa lebih mudah menurut saya. Ambil saja guci terbaru dari Github dan Anda siap untuk pergi. Ingat itu BerbobotTujuan utamanya adalah untuk membuat panggilan ke apsis REST eksternal dalam layanan latar belakang dan mengirim hasil ke UI dengan mudah. Jika Anda melakukan sesuatu seperti itu di aplikasi Anda, itu bisa sangat berguna.

2.2 Penggunaan https://github.com/koush/ion

3. Gunakan DownloadManager kelas (GingerBread dan yang lebih baru)

GingerBread membawa fitur baru, DownloadManager, yang memungkinkan Anda mengunduh file dengan mudah dan mendelegasikan kerja keras menangani untaian, aliran, dll. ke sistem.

Pertama, mari kita lihat metode utilitas:

/**
 * @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;
}

Nama metode menjelaskan semuanya. Setelah kamu yakin DownloadManager tersedia, Anda dapat melakukan sesuatu seperti ini:

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

Proses pengunduhan akan ditampilkan di bilah notifikasi.

Pikiran akhir

Metode pertama dan kedua hanyalah puncak gunung es. Ada banyak hal yang harus diingat jika Anda ingin aplikasi Anda menjadi kuat. Berikut ini daftar singkatnya:

  • Anda harus memeriksa apakah pengguna memiliki koneksi internet yang tersedia
  • Pastikan Anda memiliki izin yang tepat (INTERNET dan WRITE_EXTERNAL_STORAGE); juga ACCESS_NETWORK_STATE jika Anda ingin memeriksa ketersediaan internet.
  • Pastikan direktori itu Anda akan mengunduh file yang ada dan memiliki izin menulis.
  • Jika unduhan terlalu besar, Anda mungkin ingin menerapkan cara untuk melanjutkan unduhan jika upaya sebelumnya gagal.
  • Pengguna akan berterima kasih jika Anda mengizinkan mereka menghentikan pengunduhan.

Kecuali Anda memerlukan kontrol rinci dari proses pengunduhan, maka pertimbangkan untuk menggunakannya DownloadManager (3) karena sudah menangani sebagian besar item yang tercantum di atas.

Tetapi juga pertimbangkan bahwa kebutuhan Anda dapat berubah. Sebagai contoh, DownloadManager  tidak ada caching respons. Ini akan men-download file besar yang sama beberapa kali. Tidak ada cara mudah untuk memperbaikinya setelah kejadian itu. Di mana jika Anda memulai dengan dasar HttpURLConnection (1, 2), maka yang Anda butuhkan hanyalah menambahkan HttpResponseCache. Jadi upaya awal untuk mempelajari alat standar dasar dapat menjadi investasi yang baik.

Kelas ini tidak lagi digunakan di API level 26. ProgressDialog adalah modal   dialog, yang mencegah pengguna berinteraksi dengan aplikasi. Sebagai gantinya   menggunakan kelas ini, Anda harus menggunakan indikator kemajuan seperti   ProgressBar, yang dapat disematkan di UI aplikasi Anda. Kalau tidak,   Anda dapat menggunakan pemberitahuan untuk memberi tahu pengguna tentang kemajuan tugas. Untuk lebih jelasnya Link


1726
2017-08-29 14:34



Jangan lupa menambahkan izin ke file manifes Anda jika Anda ingin mengunduh barang dari internet!

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

98
2018-06-12 14:21



Ya kode di atas akan berfungsi. Tetapi jika Anda memperbarui progressbar di onProgressUpdate dari Asynctask  dan Anda menekan tombol kembali atau menyelesaikan aktivitas Anda AsyncTask kehilangan jalurnya dengan UI Anda. Dan ketika Anda kembali ke aktivitas Anda, bahkan jika unduhan berjalan di latar Anda tidak akan melihat pembaruan pada progressbar. Begitu seterusnya OnResume() coba jalankan utas seperti runOnUIThread dengan tugas pewaktu yang memperbarui ur progressbar dengan memperbarui nilai dari AsyncTask menjalankan latar belakang.

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

Jangan lupa untuk Menghancurkan thread saat aktivitas ur tidak terlihat.

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

27
2018-05-20 23:29



Saya telah dimodifikasi AsyncTask kelas untuk menangani pembuatan progressDialog pada konteks yang sama. Saya pikir kode berikut akan lebih dapat digunakan kembali. (Dapat dipanggil dari aktivitas apa pun hanya melewati konteks, file target, pesan dialog)

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

15
2018-05-07 15:56



Saya akan merekomendasikan Anda untuk menggunakan Proyek saya Netroid, Ini didasarkan pada Tembakan. Saya telah menambahkan beberapa fitur seperti callback multi-kejadian, manajemen unduhan file. Ini bisa membantu.


14
2018-02-10 00:17



Jangan lupa ganti "/ sdcard ..." oleh file baru("/ mnt / sdcard / ...") jika tidak, Anda akan mendapatkan FileNotFoundException


8
2017-08-01 14:55



saya menemukan ini posting blog sangat membantu, itu menggunakan loopJ untuk mengunduh file, hanya memiliki satu fungsi sederhana, akan membantu untuk beberapa orang baru android.


8
2017-09-19 13:41