Unduh file dengan Android, dan perlihatkan progresnya dalam ProgressDialog

1046

Saya mencoba menulis aplikasi sederhana yang diperbarui. Untuk ini saya memerlukan fungsi sederhana yang dapat mengunduh file dan menunjukkan kemajuan saat ini dalam ProgressDialog. Saya tahu bagaimana melakukan itu ProgressDialog, tetapi saya tidak yakin bagaimana menampilkan kemajuan saat ini dan bagaimana cara mengunduh file di tempat pertama.

Tom Leese
sumber
2
Saya harap tautan di bawah ini dapat membantu Anda ... androidhive.info/2012/04/
Ganesh Katikar
1
Untuk perbaikan cepat, lihat jawaban ini
Richa
stackoverflow.com/a/43069239/3879214 periksa jawaban ini
Anu Martin

Jawaban:

1873

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 AsyncTaskdan tunjukkan progres unduhan dalam dialog

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

Impor:

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

Ini adalah contoh kode:

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

The AsyncTaskakan 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) selalu berjalan di utas latar belakang. Anda seharusnya tidak melakukan tugas UI di sana. Di sisi lain, onProgressUpdatedan onPreExecutejalankan 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 dapat 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 saya memperbarui aktivitas saya dari suatu layanan? . Dalam contoh berikut kita akan menggunakan dua kelas yang mungkin tidak Anda sadari: ResultReceiverdan IntentService. ResultReceiveradalah yang akan memungkinkan kami untuk memperbarui utas kami dari suatu layanan; IntentServiceadalah subclass Serviceyang memunculkan thread untuk melakukan pekerjaan latar belakang dari sana (Anda harus tahu bahwa Servicerunning sebenarnya di thread yang sama dari aplikasi Anda; ketika Anda 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 {

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

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 ResultReceiverdatang 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"); //get the progress
            dialog.setProgress(progress);

            if (progress == 100) {
                dialog.dismiss();
            }
        }
    }
}

2.1 Gunakan perpustakaan Groundy

Groundy adalah pustaka yang pada dasarnya membantu Anda menjalankan potongan kode di layanan latar belakang, dan didasarkan padaResultReceiverkonsep yang ditunjukkan di atas. Perpustakaan ini sudah usang saat ini. Beginilah keseluruhan kode akan terlihat seperti:

Aktivitas tempat Anda menampilkan 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 GroundyTaskimplementasi digunakan oleh Groundy untuk men-download file dan menunjukkan kemajuan:

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 tambahkan ini ke manifes:

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

Tidak mungkin lebih mudah menurutku. Ambil saja toples terbaru dari Github dan Anda siap berangkat. Perlu diingat bahwa tujuan utama Groundy adalah untuk membuat panggilan ke apek REST eksternal dalam layanan latar belakang dan memposting hasil ke UI dengan mudah. Jika Anda melakukan hal seperti itu di aplikasi Anda, itu bisa sangat berguna.

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

3. Gunakan DownloadManagerkelas ( GingerBreaddan yang lebih baru saja)

GingerBread membawa fitur baru DownloadManager,, yang memungkinkan Anda mengunduh file dengan mudah dan mendelegasikan kerja keras menangani utas, stream, 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 yakin DownloadManagertersedia, 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);

Kemajuan unduhan akan ditampilkan di bilah notifikasi.

Pikiran terakhir

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

  • Anda harus memeriksa apakah pengguna memiliki koneksi internet yang tersedia
  • Pastikan Anda memiliki izin yang tepat ( INTERNETdan WRITE_EXTERNAL_STORAGE); juga ACCESS_NETWORK_STATEjika Anda ingin memeriksa ketersediaan internet.
  • Pastikan direktori yang akan Anda unduh ada file 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 mengganggu unduhan.

Kecuali jika Anda memerlukan kontrol terperinci dari proses unduhan, maka pertimbangkan untuk menggunakan DownloadManager(3) karena sudah menangani sebagian besar item yang tercantum di atas.

Tetapi juga pertimbangkan bahwa kebutuhan Anda dapat berubah. Misalnya, DownloadManager tidak ada caching respons . Ini akan membabi buta mengunduh file besar yang sama beberapa kali. Tidak ada cara mudah untuk memperbaikinya setelah fakta. Di mana jika Anda mulai dengan dasar HttpURLConnection(1, 2), maka yang Anda butuhkan adalah menambahkan HttpResponseCache. Jadi upaya awal mempelajari alat dasar dan standar dapat menjadi investasi yang baik.

Kelas ini sudah tidak digunakan lagi di API level 26. ProgressDialog adalah dialog modal, yang mencegah pengguna berinteraksi dengan aplikasi. Alih-alih menggunakan kelas ini, Anda harus menggunakan indikator progres seperti ProgressBar, yang dapat disematkan di UI aplikasi Anda. Atau, Anda dapat menggunakan notifikasi untuk memberi tahu pengguna tentang kemajuan tugas. Untuk lebih jelasnya, Tautkan

Cristian
sumber
8
DownloadManager adalah bagian dari OS, yang artinya akan selalu tersedia dalam GB + dan tidak dapat dihapus.
Cristian
17
Ada masalah dalam jawaban Cristian. Karena kode di " 1. Gunakan AsyncTask dan tampilkan kemajuan unduhan dalam dialog " lakukan connection.connect (); kemudian InputStream input = BufferedInputStream baru (url.openStream ()); kode membuat 2 koneksi ke server. Saya telah berhasil mengubah perilaku ini dengan memperbarui kode sebagai berikut InputStream input = BufferedInputStream baru (connection.getInputStream ());
nLL
99
Saya berharap dokumentasi Android ringkas ini.
Lou Morda
12
Disarankan untuk close()stream ( inputdan output) di finallyalih-alih try, jika tidak ada pengecualian yang dilemparkan sebelumnya close(), Anda memiliki stream tertutup yang berkeliaran.
Pang
32
Jangan hardcode / sdcard/gunakan Environment.getExternalStorageDirectory()saja.
Nima G
106

Jangan lupa untuk menambahkan izin ke file manifes Anda jika Anda akan mengunduh hal-hal 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>
Mimpi buruk
sumber
1
Saya cukup yakin Anda tidak perlu READ_PHONE_STATE dan Anda pasti tidak perlu WRITE_EXTERNAL_STORAGE; itu adalah izin berbahaya yang dapat dihindari dengan menggunakan Kerangka Akses Akses.
Pasang kembali Monica
32

Ya kode di atas akan bekerja .Tapi jika Anda memperbarui Anda progressbardi onProgressUpdatedari Asynctask dan Anda menekan tombol kembali atau menyelesaikan aktivitas Anda AsyncTaskkehilangan track dengan Anda UI .Dan ketika Anda kembali ke aktivitas Anda, bahkan jika download sedang berjalan di latar belakang Anda akan melihat tidak ada pembaruan di progressbar. Jadi, OnResume()cobalah menjalankan utas seperti runOnUIThreaddengan tugas pengatur waktu yang memutakhirkan ur Anda progressbardengan pembaruan nilai dari AsyncTasklatar belakang yang berjalan.

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 Hancurkan utas saat aktivitas Anda tidak terlihat.

private void destroyRunningThreads() {
    if (background != null) {
        background.interrupt();
        background=null;
    }
}
sheetal
sumber
1
Inilah masalah saya. Bisakah Anda memberi tahu saya cara melakukan tugas timer untuk memperbarui progressbar? Cara mendapatkan pembaruan nilai dari AsyncTask yang berjalan di belakang
user1417127
2
okk ambil variabel global atau statis ke tempat untuk memperbarui nilai-nilai Anda dari asynctask ... atau Anda dapat menyisipkannya di DataBase untuk sisi aman..jadi aplikasi penutupan tidak akan menghambat .. Dan ketika Anda memulai kembali aktivitas di mana Anda ingin memperbarui ur UI menjalankan utas UI..Lihat contoh di bawah ini
sheetal
UI harus memiliki referensi baru .. Seperti ProgressBar yang baru diinisialisasi dalam kasus saya
sheetal
@ Sheetal, Tapi Ini berfungsi dengan baik tanpa kode Anda! Mengapa?! Perangkat saya adalah Xperia P dengan Android 4.0.4. Saya telah mendefinisikan variabel boolean statis yang onPreExecute setel menjadi true dan onPostExecute menetapkannya menjadi false. Itu menunjukkan bahwa kita sedang mengunduh atau tidak, jadi kita dapat memeriksa apakah variabelnya sama dengan true, tampilkan dialog progressbar sebelumnya.
Behzad
@ Sheetal kode Anda sedikit tidak jelas, dapatkah Anda memberi tahu saya saran?
iSun
17

Saya akan merekomendasikan Anda untuk menggunakan Project Netroid saya , ini berdasarkan Volley . Saya telah menambahkan beberapa fitur untuk itu seperti panggilan balik multi-peristiwa, manajemen unduhan file. Ini bisa membantu.

VinceStyling
sumber
2
Apakah ini mendukung unduhan banyak file? Saya sedang mengerjakan proyek yang memungkinkan pengguna mengunduh terjadwal. Pada waktu tertentu (misalnya jam 12 pagi), layanan akan mengunduh semua tautan yang dipilih pengguna sebelumnya. Maksud saya layanan yang saya butuhkan harus dapat mengantri tautan unduhan, dan kemudian mengunduh semuanya (satu per satu atau paralel, tergantung pada pengguna). Terima kasih
Hoang Trinh
@piavgh ya, yang Anda inginkan sudah memuaskan, Anda dapat checkout apk sampel saya, ini termasuk demo manajemen unduhan file.
VinceStyling
Perpustakaan yang bagus! Berfungsi baik, meskipun saya mengalami masalah mengunduh konten dari HTTPS Url, seolah-olah orang dapat mengatur SSLSocketFactorymenambahkan Anda sendiri HostnameVerifiertidak mungkin dan saya perlu verifikasi tersebut. Mengangkat masalah tentang itu.
DarkCygnus
tautan untuk Volley. -> Tidak Ditemukan
Rumit Patel
14

Saya telah memodifikasi AsyncTaskkelas untuk menangani pembuatan progressDialogpada konteks yang sama. Saya pikir kode berikut akan lebih dapat digunakan kembali. (dapat dipanggil dari aktivitas apa pun hanya lewat 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();
    }
}
Suriah
sumber
Jangan lupa menambahkan izin penguncian Wake <menggunakan-izin android: name = "android.permission.WAKE_LOCK" />
Hitesh Sahu
8

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

ENSATE
sumber
Di bagian 2 Unduh dari dialog Kemajuan layanan tidak dapat menampilkan kenaikan progres. itu langsung mengembalikan 100, jadi jika 100 itu langsung periksa setprogress 100 dan lanjutkan, bagaimana cara meningkatkan kemajuan ?? itu hanya menampilkan 0 kemajuan tetapi sebenarnya mengunduh berjalan
Nirav Mehta
ini hanya menampilkan 0% dari 100 yang hanya berfungsi dengan baik
Nirav Mehta
14
Jangan lakukan itu! Ada Environment.getExternalStorageDirectory().getAbsolutePath()untuk mendapatkan jalur ke sdcard. Juga jangan lupa untuk memeriksa apakah penyimpanan eksternal sudah terpasang - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)( Mannaz )
naXa
8

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

chintan adatiya
sumber
7

Sementara saya mulai belajar pengembangan android, saya telah belajar bahwa itu ProgressDialogadalah cara untuk pergi. Ada setProgressmetode ProgressDialogyang dapat digunakan untuk memperbarui tingkat kemajuan saat file diunduh.

Yang terbaik yang saya lihat di banyak aplikasi adalah mereka menyesuaikan atribut dialog progres ini untuk memberikan tampilan dan nuansa yang lebih baik untuk dialog progres daripada versi stok. Baik untuk menjaga pengguna terlibat dengan beberapa animasi seperti katak, gajah atau kucing / anak anjing yang lucu. Animasi apa pun dengan dialog progres menarik pengguna dan mereka merasa tidak ingin menunggu lama.

Sandeep
sumber
5

Saran pribadi saya adalah menggunakan Dialog Kemajuan dan membangun sebelum eksekusi, atau memulai pada OnPreExecute(), sering mempublikasikan kemajuan jika Anda menggunakan gaya horizontal dari progress bar dari dialog progres. Bagian yang tersisa adalah untuk mengoptimalkan algoritma doInBackground.

Raju yourPepe
sumber
5

Gunakan pustaka Android Query, memang sangat keren. Anda dapat mengubahnya untuk digunakan ProgressDialogseperti yang Anda lihat dalam contoh lain, yang ini akan menampilkan tampilan progres dari tata letak Anda dan menyembunyikannya setelah selesai.

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  
        } 
    }
});
Renetik
sumber
Masalahnya, saya berhenti menggunakannya, karena tidak terawat jadi jangan kira ini jawaban yang baik lagi.
Renetik
3

Saya menambahkan jawaban lain untuk solusi lain yang saya gunakan sekarang karena Android Query begitu besar dan tidak terawat untuk tetap sehat. Jadi saya pindah ke https://github.com/amitshekhariitbhu/Fast-Android-Networking ini .

    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) {
            ...
        }
    });
Renetik
sumber
2

Izin

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

Menggunakan 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));  
  }
}
vishnuc156
sumber
1

Anda dapat mengamati perkembangan pengelola unduhan menggunakan LiveData dan coroutine, lihat intinya di bawah ini

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

}
Fahad Alotaibi
sumber
Bisakah Anda memberikan contoh usecase untuk kelas ini?
Karim Karimov
0

Kita bisa menggunakan coroutine dan work manager untuk mengunduh file di kotlin.

Tambahkan dependensi di build.gradle

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

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

Mulai mengunduh melalui manajer kerja di kelas aktivitas

 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

                                    }
                                }
                            }
                        }
                    })

        }
Kinjal
sumber