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 AsyncTask
và 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 AsyncTask
sẽ 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, onProgressUpdate
và onPreExecute
chạ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: ResultReceiver
và IntentService
. ResultReceiver
là 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ụ; IntentService
là 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 Service
lầ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ênResultReceiver
khá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 GroundyTask
triể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.
3. Sử dụng DownloadManager
lớp ( GingerBread
và chỉ mới hơn)
GingerBread mang đến một tính năng mới, DownloadManager
cho 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 DownloadManager
có 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 (
INTERNET
và WRITE_EXTERNAL_STORAGE
) đúng; Ngoài ra ACCESS_NETWORK_STATE
nế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