Tải xuống một tệp với Android và hiển thị tiến trình trong ProgressDialog


1046

Tôi đang cố gắng viết một ứng dụng đơn giản được cập nhật. Đối với điều này, tôi cần một chức năng đơn giản có thể tải xuống một tệp và hiển thị tiến trình hiện tại trong một ProgressDialog. Tôi biết cách thực hiện ProgressDialog, nhưng tôi không chắc cách hiển thị tiến trình hiện tại và cách tải xuống tệp ở vị trí đầu tiên.


2
Tôi hy vọng liên kết dưới đây có thể giúp bạn ... androidhive.info/2012/04/ Kẻ
Ganesh Katikar

1
Để khắc phục nhanh, hãy tham khảo câu trả lời này
Richa

stackoverflow.com/a/43069239/3879214 kiểm tra câu trả lời này
Anu Martin

Câu trả lời:


1873

Có nhiều cách để tải tập tin. Sau đây tôi sẽ đăng những cách phổ biến nhất; tùy thuộc vào bạn để quyết định phương pháp nào tốt hơn cho ứng dụng của bạn.

1. Sử dụng AsyncTaskvà hiển thị tiến trình tải xuống trong hộp thoại

Phương pháp này sẽ cho phép bạn thực hiện một số quy trình nền và cập nhật giao diện người dùng cùng một lúc (trong trường hợp này, chúng tôi sẽ cập nhật thanh tiến trình).

Nhập khẩu:

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

Đây là một mã ví dụ:

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

Các AsyncTasksẽ trông như thế này:

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

Phương thức trên ( doInBackground) luôn chạy trên một luồng nền. Bạn không nên thực hiện bất kỳ nhiệm vụ UI nào ở đó. Mặt khác, onProgressUpdateonPreExecutechạy trên luồng UI, do đó bạn có thể thay đổi thanh tiến trình:

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

Để chạy cái này, bạn cần có quyền WAKE_LOCK.

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

2. Tải xuống từ Dịch vụ

Câu hỏi lớn ở đây là: làm cách nào để cập nhật hoạt động của tôi từ một dịch vụ? . Trong ví dụ tiếp theo, chúng tôi sẽ sử dụng hai lớp mà bạn có thể không biết: ResultReceiverIntentService. ResultReceiverlà một trong đó sẽ cho phép chúng tôi cập nhật chủ đề của chúng tôi từ một dịch vụ; IntentServicelà một lớp con trong Serviceđó sinh ra một luồng để thực hiện công việc nền từ đó (bạn nên biết rằng một Servicelần chạy thực sự trong cùng một luồng của ứng dụng của bạn; khi bạn mở rộngService , bạn phải tự tạo ra các luồng mới để chạy các hoạt động chặn CPU).

Dịch vụ tải xuống có thể trông như thế này:

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

Thêm dịch vụ vào bảng kê khai của bạn:

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

Và hoạt động sẽ như thế này:

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

Đây là ResultReceiverđể chơi:

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 Sử dụng thư viện Groundy

Groundy là một thư viện về cơ bản giúp bạn chạy các đoạn mã trong dịch vụ nền và nó dựa trênResultReceiverkhái niệm được hiển thị ở trên. Thư viện này không được chấp nhận tại thời điểm này. Đây là cách toàn bộ mã sẽ như thế nào:

Hoạt động nơi bạn đang hiển thị hộp thoại ...

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

Một GroundyTasktriển khai được sử dụng bởi Groundy để tải xuống tệp và hiển thị tiến trình:

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

Và chỉ cần thêm điều này vào bảng kê khai:

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

Nó không thể dễ dàng hơn tôi nghĩ. Chỉ cần lấy bình mới nhất từ Github và bạn đã sẵn sàng để đi. Hãy nhớ rằng mục đích chính của Groundy là thực hiện các cuộc gọi đến apis REST bên ngoài trong một dịch vụ nền và gửi kết quả lên UI một cách dễ dàng. Nếu bạn đang làm một cái gì đó như thế trong ứng dụng của bạn, nó có thể thực sự hữu ích.

2.2 Sử dụng https://github.com/koush/ion

3. Sử dụng DownloadManagerlớp ( GingerBreadvà chỉ mới hơn)

GingerBread mang đến một tính năng mới, DownloadManagercho phép bạn tải xuống các tệp dễ dàng và ủy thác công việc khó khăn trong việc xử lý các luồng, luồng, v.v. cho hệ thống.

Trước tiên, hãy xem một phương thức tiện ích:

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

Tên của phương thức giải thích tất cả. Một khi bạn chắc chắn DownloadManagercó sẵn, bạn có thể làm một cái gì đó như thế này:

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

Tiến trình tải xuống sẽ được hiển thị trên thanh thông báo.

Suy nghĩ cuối cùng

Phương pháp thứ nhất và thứ hai chỉ là phần nổi của tảng băng chìm. Có rất nhiều điều bạn phải ghi nhớ nếu bạn muốn ứng dụng của mình mạnh mẽ. Đây là một danh sách ngắn gọn:

  • Bạn phải kiểm tra xem người dùng có kết nối internet không
  • Hãy chắc chắn rằng bạn có quyền ( INTERNETWRITE_EXTERNAL_STORAGE) đúng; Ngoài ra ACCESS_NETWORK_STATEnếu bạn muốn kiểm tra tính khả dụng của Internet.
  • Hãy chắc chắn rằng thư mục bạn sẽ tải xuống các tệp tồn tại và có quyền ghi.
  • Nếu tải xuống quá lớn, bạn có thể muốn thực hiện một cách để tiếp tục tải xuống nếu các lần thử trước không thành công.
  • Người dùng sẽ biết ơn nếu bạn cho phép họ làm gián đoạn quá trình tải xuống.

Trừ khi bạn cần kiểm soát chi tiết quá trình tải xuống, sau đó xem xét sử dụng DownloadManager(3) vì nó đã xử lý hầu hết các mục được liệt kê ở trên.

Nhưng cũng xem xét rằng nhu cầu của bạn có thể thay đổi. Ví dụ, DownloadManager không có bộ nhớ đệm đáp ứng . Nó sẽ mù quáng tải xuống cùng một tệp lớn nhiều lần. Không có cách nào dễ dàng để sửa nó sau khi thực tế. Nếu bạn bắt đầu với một cơ bản HttpURLConnection(1, 2), thì tất cả những gì bạn cần là thêm một HttpResponseCache. Vì vậy, nỗ lực ban đầu của việc học các công cụ cơ bản, tiêu chuẩn có thể là một khoản đầu tư tốt.

Lớp này không được dùng ở cấp độ API 26. ProgressDialog là hộp thoại theo chế độ, ngăn người dùng tương tác với ứng dụng. Thay vì sử dụng lớp này, bạn nên sử dụng một chỉ báo tiến trình như ProgressBar, có thể được nhúng trong giao diện người dùng của ứng dụng. Ngoài ra, bạn có thể sử dụng thông báo để thông báo cho người dùng về tiến trình của nhiệm vụ. Để biết thêm chi tiết Liên kết


8
DownloadManager là một phần của HĐH, có nghĩa là nó sẽ luôn có sẵn trong GB + và không thể gỡ cài đặt.
Cristian

17
Có một vấn đề trong câu trả lời của Cristian. Bởi vì mã tại " 1. Sử dụng AsyncTask và hiển thị tiến trình tải xuống trong hộp thoại " không kết nối.connect (); sau đó InputStream input = new BufferedInputStream (url.openStream ()); mã tạo 2 kết nối đến máy chủ. Tôi đã quản lý để thay đổi hành vi này bằng cách cập nhật mã như sau InputStream input = new BufferedInputStream (Connection.getInputStream ());
nLL

99
tôi muốn tài liệu Android là súc tích này.
Lou Morda

12
Được đề xuất cho close()các luồng ( inputoutput) finallythay vì try, nếu không, nếu có bất kỳ ngoại lệ nào được ném trước đó close(), bạn có các luồng không rõ ràng treo xung quanh.
Pang

32
Đừng dùng hardcode / sdcard/sử dụng Environment.getExternalStorageDirectory()thay thế.
Nima G

106

Đừng quên thêm quyền vào tệp kê khai của bạn nếu bạn sẽ tải xuống nội dung từ 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>

1
Tôi khá chắc chắn rằng bạn không cần READ_PHONE_STATE và bạn chắc chắn không cần WRITE_EXTERNAL_STORAGE; đó là một quyền nguy hiểm có thể tránh được bằng cách sử dụng Khung truy cập lưu trữ.
Phục hồi Monica

32

Có các mã trên sẽ làm việc .Nhưng nếu bạn đang cập nhật của bạn progressbartrong onProgressUpdatecác Asynctask và bạn nhấn nút quay lại hoặc kết thúc hoạt động của bạnAsyncTask thua ca khúc của mình với giao diện người dùng .Và bạn khi bạn quay trở lại hoạt động của bạn, ngay cả khi tải đang chạy ở chế độ nền, bạn sẽ thấy không cập nhật trên thanh tiến trình. Vì vậy, OnResume()hãy thử chạy một luồng như runOnUIThreadvới một tác vụ hẹn giờ cập nhật ur progressbarvới các giá trị được cập nhật từ AsyncTasknền đang chạy.

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

Đừng quên Phá hủy chuỗi khi hoạt động của bạn không hiển thị.

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

1
Đây chính xác là vấn đề của tôi. Bạn có thể cho tôi biết làm thế nào để thực hiện một nhiệm vụ hẹn giờ để cập nhật tiến trình? Cách nhận các giá trị cập nhật từ AsyncTask chạy phía sau
user1417127

2
okk đưa biến toàn cục hoặc tĩnh đến nơi cập nhật giá trị của bạn từ asynctask ... hoặc bạn có thể chèn nó vào DataBase cho mặt an toàn..vì việc đóng ứng dụng sẽ không cản trở..Và khi bạn khởi động lại hoạt động mà bạn muốn cập nhật UI chạy luồng UI..xem ví dụ bên dưới
sheet

Giao diện người dùng nên có reference..Like tươi mới được initalized ProgressBar trong trường hợp của tôi
Sheetal

@sheetal, nhưng nó hoạt động tốt mà không cần mã của bạn! Tại sao?! Thiết bị của tôi là Xperia P với Android 4.0.4. Tôi đã xác định một biến boolean tĩnh mà onPreExecute đặt nó thành true và onPostExecute đặt nó thành false. Nó cho thấy rằng chúng tôi đang tải xuống hay không, vì vậy chúng tôi có thể kiểm tra xem biến có bằng đúng không, hiển thị hộp thoại thanh tiến trình trước đó.
Behzad

@sheetal mã của bạn hơi tối nghĩa, bạn có thể cho tôi một lời khuyên?
iSun

17

Tôi khuyên bạn nên sử dụng Project Netroid của tôi , Nó dựa trên Volley . Tôi đã thêm một số tính năng cho nó như gọi lại nhiều sự kiện, quản lý tải xuống tệp. Điều này có thể là một số trợ giúp.


2
Nó có hỗ trợ tải nhiều tập tin không? Tôi đang làm việc trên một dự án cho phép người dùng tải xuống theo lịch trình. Tại một thời điểm cụ thể (ví dụ 12 giờ sáng), dịch vụ sẽ tải xuống tất cả các liên kết mà người dùng đã chọn trước đó. Ý tôi là dịch vụ tôi cần phải có khả năng xếp hàng các liên kết tải xuống, sau đó tải xuống tất cả chúng (từng cái một hoặc song song, tùy thuộc vào người dùng). Cảm ơn bạn
Hoàng Trinh

@piavgh vâng, tất cả những gì bạn muốn là thỏa mãn, bạn có thể kiểm tra apk mẫu của tôi, nó bao gồm một bản demo quản lý tải xuống tệp.
VinceStyling

Thư viện tuyệt vời! Hoạt động tốt, mặc dù tôi gặp sự cố khi tải xuống nội dung từ HTTPS Url, vì mặc dù người ta có thể đặt SSLSocketFactorythêm một cái riêng của bạn HostnameVerifierlà không thể và tôi cần người xác minh như vậy. Đưa ra một vấn đề liên quan đến điều đó.
DarkCygnus

liên kết cho Volley. -> Không tìm thấy
Rumit Patel 24/12/18

14

Tôi đã sửa đổi AsyncTasklớp để xử lý việc tạo trong progressDialogcùng một bối cảnh. Tôi nghĩ rằng đoạn mã sau sẽ dễ sử dụng lại hơn. (nó có thể được gọi từ bất kỳ hoạt động nào chỉ cần chuyển ngữ cảnh, tệp đích, thông báo hộp thoại)

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

Đừng quên thêm quyền khóa Wake <allow-allow android: name = "ERIC.WAKE_LOCK" />
Hitesh Sahu

8

Đừng quên thay thế "/ sdcard ..." bằng Tệp mới ("/ mnt / sdcard / ...") nếu không bạn sẽ nhận được FileNotFoundException


Trong phần 2 Tải xuống từ hộp thoại Tiến trình dịch vụ không thể hiển thị gia tăng tiến độ. nó trực tiếp trả về 100, vì vậy nếu 100 nó kiểm tra trực tiếp 100 và tiến hành, làm thế nào để tăng tiến độ ?? Nó chỉ hiển thị 0 tiến trình nhưng thực sự tải xuống đang chạy
Nirav Mehta

nó chỉ hiển thị 0% trong số 100 chỉ hoạt động khác
Nirav Mehta

14
Đừng làm việc đó! Có Environment.getExternalStorageDirectory().getAbsolutePath()để có được một đường dẫn đến sdcard. Cũng đừng quên kiểm tra xem bộ lưu trữ ngoài có được gắn không - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)( Mannaz )
naXa

8

Tôi thấy bài đăng trên blog này rất hữu ích, Nó sử dụng loopJ để tải xuống tệp, nó chỉ có một chức năng Đơn giản, sẽ hữu ích cho một số người dùng Android mới.


7

Trong khi tôi bắt đầu học phát triển Android, tôi đã học được rằng đó ProgressDialoglà con đường để đi. Có một setProgressphương pháp ProgressDialogcó thể được gọi để cập nhật mức tiến trình khi tệp được tải xuống.

Điều tốt nhất tôi thấy trong nhiều ứng dụng là chúng tùy chỉnh các thuộc tính của hộp thoại tiến trình này để mang lại cái nhìn và cảm nhận tốt hơn cho hộp thoại tiến trình so với phiên bản chứng khoán. Tốt để giữ cho người dùng tham gia với một số hoạt hình như ếch, voi hoặc mèo / chó con dễ thương. Bất kỳ hình ảnh động nào trong hộp thoại tiến trình đều thu hút người dùng và họ không cảm thấy như bị chờ đợi lâu.



5

Lời khuyên cá nhân của tôi là sử dụng Hộp thoại Tiến trình và xây dựng trước khi thực hiện hoặc bắt đầu tại OnPreExecute(), xuất bản tiến trình thường xuyên nếu bạn sử dụng kiểu thanh ngang tiến trình của hộp thoại tiến trình. Phần còn lại là để tối ưu hóa thuật toán của doInBackground.


5

Sử dụng thư viện Truy vấn Android, thực sự rất tuyệt. Bạn có thể thay đổi nó để sử dụng ProgressDialognhư bạn thấy trong các ví dụ khác, ứng dụng này sẽ hiển thị chế độ xem tiến trình từ bố cục của bạn và ẩn nó sau khi hoàn thành.

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

Vấn đề là, tôi đã ngừng sử dụng nó, vì nó không rõ ràng nên không cho rằng đây là câu trả lời tốt nữa.
Renetik

3

Tôi đang thêm một câu trả lời khác cho giải pháp khác mà tôi hiện đang sử dụng vì Android Query quá lớn và không rõ ràng để giữ sức khỏe. Vì vậy, tôi đã chuyển sang https://github.com/amitshekhariitbhu/Fast-Android-Networking này .

    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

Quyền

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

Sử dụng kết nối httpURLC

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

Bạn có thể quan sát tiến trình của trình quản lý tải xuống bằng LiveData và coroutines, xem ý chính bên dưới

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

}

Bạn có thể cung cấp một ví dụ usecase cho lớp này không?
Karim Karimov


0

Chúng tôi có thể sử dụng coroutine và trình quản lý công việc để tải xuống các tệp trong kotlin.

Thêm một phụ thuộc trong build.gradle

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

Lớp 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)
        }
    }

Bắt đầu tải xuống thông qua trình quản lý công việc trong lớp hoạt động

 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

                                    }
                                }
                            }
                        }
                    })

        }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.