三種方式讓 Android WebView 支持文件下載

三種方式讓 Android WebView 支持文件下載

 

最近在開發的過程中遇到一個需求,那就是讓 WebView 支持文件下載,比如說下載 apk。WebView 默認是不支持下載的,需要開發者自己實現。既然 PM 提出了需求,那咱就擼起袖子幹唄,於是乎在網上尋找了幾種方法,主要思路有這麼幾種:

  • 跳轉瀏覽器下載
  • 使用系統的下載服務
  • 自定義下載任務

有了思路就好辦了,下面介紹具體實現。
要想讓 WebView 支持下載,需要給 WebView 設置下載監聽器 setDownloadListener,DownloadListener 裏面只有一個方法 onDownloadStart,每當有文件需要下載時,該方法就會被回調,下載的 URL 通過方法參數傳遞,我們可以在這裏處理下載事件。


 

 

mWebView.setDownloadListener(new DownloadListener() {

@Override

public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {

// TODO: 2017-5-6 處理下載事件

}

});

1. 跳轉瀏覽器下載

這種方式最爲簡單粗暴,直接把下載任務拋給瀏覽器,剩下的就不用我們管了。缺點是無法感知下載完成,當然就沒有後續的處理,比如下載 apk 完成後打開安裝界面。

private void downloadByBrowser(String url) {

Intent intent = new Intent(Intent.ACTION_VIEW);

intent.addCategory(Intent.CATEGORY_BROWSABLE);

intent.setData(Uri.parse(url));

startActivity(intent);

}

 

 

2. 使用系統的下載服務

DownloadManager 是系統提供的用於處理下載的服務,使用者只需提供下載 URI 和存儲路徑,並進行簡單的設置。DownloadManager 會在後臺進行下載,並且在下載失敗、網絡切換以及系統重啓後嘗試重新下載。


 
private void downloadBySystem(String url, String contentDisposition, String mimeType) {

// 指定下載地址

DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));

// 允許媒體掃描,根據下載的文件類型被加入相冊、音樂等媒體庫

request.allowScanningByMediaScanner();

// 設置通知的顯示類型,下載進行時和完成後顯示通知

request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

// 設置通知欄的標題,如果不設置,默認使用文件名

// request.setTitle("This is title");

// 設置通知欄的描述

// request.setDescription("This is description");

// 允許在計費流量下下載

request.setAllowedOverMetered(false);

// 允許該記錄在下載管理界面可見

request.setVisibleInDownloadsUi(false);

// 允許漫遊時下載

request.setAllowedOverRoaming(true);

// 允許下載的網路類型

request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);

// 設置下載文件保存的路徑和文件名

String fileName = URLUtil.guessFileName(url, contentDisposition, mimeType);

log.debug("fileName:{}", fileName);

request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);

// 另外可選一下方法,自定義下載路徑

// request.setDestinationUri()

// request.setDestinationInExternalFilesDir()

final DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);

// 添加一個下載任務

long downloadId = downloadManager.enqueue(request);

log.debug("downloadId:{}", downloadId);

}

這樣我們就添加了一項下載任務,然後就靜靜等待系統下載完成吧。還要注意一點,別忘了添加讀寫外置存儲權限和網絡權限哦~
那怎麼知道文件下載成功呢?系統在下載完成後會發送一條廣播,裏面有任務 ID,告訴調用者任務完成,通過 DownloadManager 獲取到文件信息就可以進一步處理。


 
private class DownloadCompleteReceiver extends BroadcastReceiver {



@Override

public void onReceive(Context context, Intent intent) {

log.verbose("onReceive. intent:{}", intent != null ? intent.toUri(0) : null);

if (intent != null) {

if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {

long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);

log.debug("downloadId:{}", downloadId);

DownloadManager downloadManager = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);

String type = downloadManager.getMimeTypeForDownloadedFile(downloadId);

log.debug("getMimeTypeForDownloadedFile:{}", type);

if (TextUtils.isEmpty(type)) {

type = "*/*";

}

Uri uri = downloadManager.getUriForDownloadedFile(downloadId);

log.debug("UriForDownloadedFile:{}", uri);

if (uri != null) {

Intent handlerIntent = new Intent(Intent.ACTION_VIEW);

handlerIntent.setDataAndType(uri, type);

context.startActivity(handlerIntent);

}

}

}

}

}



// 使用

DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();

IntentFilter intentFilter = new IntentFilter();

intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);

registerReceiver(receiver, intentFilter);

Ok,到這裏,利用系統服務下載就算結束了,簡單總結一下。我們只關心開始和完成,至於下載過程中的暫停、重試等機制,系統已經幫我們做好了,是不是非常友好?

3. 自定義下載任務

有了下載鏈接就可以自己實現網絡部分,我在這兒自定義了一個下載任務,使用 HttpURLConnection 和 AsyncTask 實現,代碼還是比較簡單的。


 
private class DownloadTask extends AsyncTask<String, Void, Void> {

// 傳遞兩個參數:URL 和 目標路徑

private String url;

private String destPath;



@Override

protected void onPreExecute() {

log.info("開始下載");

}



@Override

protected Void doInBackground(String... params) {

log.debug("doInBackground. url:{}, dest:{}", params[0], params[1]);

url = params[0];

destPath = params[1];

OutputStream out = null;

HttpURLConnection urlConnection = null;

try {

URL url = new URL(params[0]);

urlConnection = (HttpURLConnection) url.openConnection();

urlConnection.setConnectTimeout(15000);

urlConnection.setReadTimeout(15000);

InputStream in = urlConnection.getInputStream();

out = new FileOutputStream(params[1]);

byte[] buffer = new byte[10 * 1024];

int len;

while ((len = in.read(buffer)) != -1) {

out.write(buffer, 0, len);

}

in.close();

} catch (IOException e) {

log.warn(e);

} finally {

if (urlConnection != null) {

urlConnection.disconnect();

}

if (out != null) {

try {

out.close();

} catch (IOException e) {

log.warn(e);

}

}

}

return null;

}



@Override

protected void onPostExecute(Void aVoid) {

log.info("完成下載");

Intent handlerIntent = new Intent(Intent.ACTION_VIEW);

String mimeType = getMIMEType(url);

Uri uri = Uri.fromFile(new File(destPath));

log.debug("mimiType:{}, uri:{}", mimeType, uri);

handlerIntent.setDataAndType(uri, mimeType);

startActivity(handlerIntent);

}

}



private String getMIMEType(String url) {

String type = null;

String extension = MimeTypeMap.getFileExtensionFromUrl(url);

log.debug("extension:{}", extension);

if (extension != null) {

type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);

}

return type;

}



// 使用

mWebView.setDownloadListener(new DownloadListener() {

@Override

public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {

String fileName = URLUtil.guessFileName(url, contentDisposition, mimeType);

String destPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)

.getAbsolutePath() + File.separator + fileName;

new DownloadTask().execute(url, destPath);

}

});

優勢是我們可以感知下載進度,處理開始、取消、失敗、完成等事件,不足之處是對下載的控制不如系統服務,必須自己處理網絡帶來的問題。

可以看出,這三種下載方式各有特點,大家可以根據需要選擇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章