Android 网络编程系列(3)WebView 详解
前言
在上一篇關于WebView的文章中,介紹了 WebView 的基本使用方法、WebView 頁面處理和歷史記錄以及和 JS 調用本地代碼的相關內容。今天就在上一篇文章的基礎上,補充一些 WebView 的更深入的東西。
主要從如下幾點來進行補充:
加載本地 HTML 頁面
前一篇文章中說到使用 WebView 需要開啟 Internet 的權限,這個說法其實是不準確的。開啟權限只是因為我們使用 loadUrl() 方法來獲取 URL 的內容時需要聯網。當我們選擇加載本地的 HTML 頁面(比如 assets 目錄下的 HTML 文件或者使用 loadData() 方法加載一段 HTML 代碼),并不需要網絡權限。
加載 assets 目錄下的 HTML 文件的方式和加載網頁是一致的,都是使用 WebView 的 loadUrl() 方法。
mWebView.loadUrl("file:///android_asset/index.html");復制代碼WebView 部分方法詳解
上篇文章在介紹 WebView 的歷史記錄時,列舉了部分相關的方法并做了簡單的描述。這部分主要補充幾個 load 方法的介紹。
void loadData (String data, String mimeType, String encoding)
加載指定的 data 數據,有時需要加載的不是一個完整的網頁,而是一個 HTML 片段。
data 表示給定編碼的 String 類型 HTML 文本。mimeType 表示 data 的 MIMIE 類型,比如 “text/html”。encoding 表示 data 的編碼格式。
這個方法中有兩個地方需要注意:
- JavaScript 有同源限制。意味著在此頁面中運行的腳本無法訪問任何不是通過 data 加載的數據。避免同源限制可以采用 loadDataWithBaseURL() 方法。
- encoding 指定了 data 是使用 base64 編碼還是使用 URL 編碼。如果 data 是使用 base64 編碼的,encoding 的值一定為 “base64”,如果是其他值的編碼方式(包括 null),則默認使用 ASCII 編碼方式。對于超出 ASCII 范圍的字符比如 ‘#’,‘%’,'\','?',應該轉化為 ”%+原字符的十六進制值“ 如 ‘%23’、‘%25’、‘%27’、‘%3f’。
示例如下:
String htmlString = "<html><title>this is title</title><body>this is web content.</body></html>"; mWebView.loadData(htmlString, "text/html","utf-8");復制代碼void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
這個方法比上面那個方法只多了兩個參數,一個是 baseUrl,一個是 historyUrl。baseUrl 指定了 data 數據的基準地址。我們知道,在 HTML 代碼中通常會有超鏈接或者圖片,而這些鏈接或者圖片地址通常使用的都是相對路徑,如果沒有一個基準,WebView 就無法訪問到這些資源。
下面我們在 assets 目錄下模擬網站資源的相對路徑,來幫助我們更好地理解:
assets 目錄下創建了 web 目錄,里面有一張圖片名為 logo.png
String htmlStr = "this is our logo:<img src="/logo.png"/>" mWebView.loadData(htmlStr, "text/html","utf-8"); mWebView.loadDataWithBaseUrl("file:///android_asset/web",htmlstr,"text/html","utf-8",null);復制代碼使用 loadData() 方法無法加載圖片,只有使用 loadDataWithBaseUrl() 方法才能加載。
void loadUrl(String url)
加載指定 URL 的內容。
void loadUrl(String url, Map additionalHttpHeaders)
加載指定的 URL 的內容,并攜帶 http header 數據。
void reload()
重新加載當前頁面。頁面中所有資源會重新加載。
WebSettings getSettings()
獲取對這個 WebView 進行設置的 WebSettings 對象。
WebSettings 是管理 WebView 設置狀態的類。當首次創建 WebView 時,它會獲得一組默認設置。這些默認設置可以通過 WebSettings 的任何 getter 方法調用返回。從 WebView.getSettings() 獲取的WebSettings 對象與 WebView 的使用壽命相關。如果 WebView 已被破壞,WebSettings 上的任何方法調用都將拋出一個 IllegalStateException 異常。
上一篇文章中提到 WebView 本身不支持 JavaScript,需要通過 WebSettings 來設置,即:
mWebView.getSettings().setJavaScriptEnabled(true);復制代碼這里介紹其他幾個常用的 WebSettings 的 setter 方法,來對 WebView 進行設置。
setCacheMode(int mode)
設置是否支持緩存及緩存模式,默認值為 LOAD_DEFAULT。
setDefaultFontSize(int size)
設置默認的字體大小。有效值在 1~72 之間,默認值為 16。
setSupportZoom(boolean support)
設置是否支持縮放,默認支持。
setDisplayZoomControls(boolean enabled)
設置是否顯示縮放按鈕。默認值為 true。
WebView 與 WebViewClient、WebChromeClient 的關系和作用
在上篇文章中介紹了通過覆蓋 WebViewClient 的shouldOverrideUrlLoading() 方法來使用當前應用展示頁面,而不是通過手機瀏覽器來打開。這里就系統地講講 WebView 和 WebViewClient、WebChromeClient 的關系以及它們各自的作用。
WebView 在加載網頁時會產生各種事件,并回調給應用程序以便在網頁加載過程進行一些額外的處理。如果所有的工作都由 WebView 本身來完成,那么它的工作量就太大了,因此 Android 中引入了WebVIewClient 和 WebChromeClient 類來幫助 WebView 分擔這些事件處理的回調。
WebView 負責加載網頁以及網頁渲染。
WebViewClient 的主要職責是幫助 WebView 處理各種通知、回調事件。
WebChromeClient 的作用是監聽網頁加載進度以及對網頁標題,網頁圖標、JS 對話框進行處理等等。
WebViewClient 和 WebChromeCient 的方法介紹
WebViewClient 部分方法說明
//網頁開始加載時的回調函數 void onPageStarted(WebView view, String url, Bitmap favicon) //網頁加載結束時的回調函數 void onPageFinished(WebView view, String url) //網頁加載出錯時的回調函數 void onReceivedError(WebView view, int errorCode, String description, String failingUrl) //選擇使用應用程序進行頁面展示時需要覆蓋該方法 boolean shouldOverrideUrlLoading(WebView view, String url)復制代碼WebChromeClient 部分方法說明
//接收到頁面 title 時回調,獲取到 title 可以設置到應用的 ActionBar 上。 void onReceivedTitle(WebView view, String title) //接收到頁面的圖標時回調 void onReceivedIcon(WebView view, Bitmap icon) //當頁面加載進度發生變化時回調,可以設置一個 ProgressBar 來顯示加載進度 void onProgressChanged(WebView view, int newProgress) //當頁面彈出 Javascript 警報對話框時調用 //客戶端可以設置是否對這個對話框進行處理 boolean onJsAlert(WebView view, String url, String message, JsResult result) //當頁面彈出 Javascript 確認對話框時調用 //客戶端可以設置是否對這個對話框進行處理 boolean onJsConfirm(WebView view, String url, String message, JsResult result) //當頁面彈出 Javascript 提示對話框時調用 //客戶端可以設置是否對這個對話框進行處理 boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)復制代碼錯誤碼的處理
在瀏覽網頁時經常會遇到 404 錯誤,也就是訪問的頁面不存在,這里就簡單介紹一下如何處理 WebView 中的 404 錯誤。對錯誤碼的處理需要覆蓋 WebViewClient 類中的 onReceiveError() 方法,對于 404 錯誤,有兩個常用的處理方法:
第一,加載一個 assets 目錄下的寫好的 404 頁面
class MyWebViewClient extends WebViewClient{@Overridepublic void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {view.loadUrl("file:///android_asset/404.html");} } mWebView.setWebViewClient(new MyWebViewClient());復制代碼第二,使用 Android 中的 ImageView 或者 TextVIew 來顯示錯誤信息。
TextView tvError = (TextView) findViewById(R.id.tv_error); class MyWebViewClient extends WebViewClient{@Overridepublic void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {mWebView.setVisibility(View.GONE);tvError.setVisibility(View.VISIBLE);tvError.setText("something goes wrong");} } mWebView.setWebViewClient(new MyWebViewClient());復制代碼利用 WebView 下載文件
使用 WebView 進行文件下載同樣有兩種常用的方法,一種是使用隱式 Intent,調用系統瀏覽器進行下載任務;一種是獲取到下載文件的 URL,創建線程進行異步下載。
我們這里就簡單介紹一下使用隱式 Intent 的方法來下載文件,第二種方法將在后面一篇關于 HttpUrlConnection 的文章中進行介紹。
WebView 有一個 setDownloadListener() 方法,當 URL 的內容不能被 WebView 引擎渲染時(文件),應該被下載。需要新建一個 DownloadListener 的實現類。在該實現類的 onDownloadStart() 方法中,可以獲取到當前下載文件的 URL。之后就可以通過隱式 Intent 來調用系統瀏覽器進行下載。
class MyDownloadListener implements DownloadListener{@Overridepublic void onDownloadStart(String url, String userAgent,String contentDisposition,String mimetype, long contentLength) {if(url.endsWith(".apk")){Uri uri = Uri.parse(url);Intent i = new Intent(Intent.ACTION_VIEW, uri);startActivity(i);}} } mWebView.loadUrl("http://www.wandoujia.com/"); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.setDownloadListener(new MyDownloadListener());復制代碼上述代碼實現了在豌豆莢網站中下載 apk 文件的效果。
遠程注入漏洞的解決方案
上篇文章中講到了在 Android 4.2 版本之前使用 addJavascriptInterface() 方法會存在安全隱患,攻擊者可以通過反射機制來對客戶端進行攻擊。那么在 4.2 之前,這個漏洞有沒有什么解決方案呢?
答案是有的,在網上看了一些資料,找到了對應的方案。在上面的 WebChromeClient 的方法介紹時提到了一個 onJsPrompt() 方法,這個方法在 JS 調用 prompt 方法時會在本地回調。通過這個方法,JS 能把信息(文本)傳遞到 Java,而 Java 也能把信息(文本)傳遞到 JS 中??蛻舳撕途W頁前端的開發者之間協商好一個小型的協議,通過 onJsPrompt() 將遠程調用所需的信息按照協議封裝成文本發給客戶端,客戶端根據協議解析這段文本,提取出所需的信息,利用反射機制進行本地的方法調用,如果有返回值就將返回值通過onJsPrompt() 傳遞給 JS。就能夠實現 JS 遠程調用 Java 代碼了。
這樣的解決方案我也只是一知半解,所以只能介紹到這里,在參考資料中會有更加詳細的介紹。
結束語
這篇文章結束后,對于 WebView 的一些常見的方法和這些方法背后的原理都略作介紹。由于是在學習階段,很多知識和原理并不能做到多么深入,但是通過寫這兩篇關于 WebView 的文章,去看了很多的博客,研究了一番官方文檔,收獲還是非常大的。接下來就要去學習 HttpUrlConnection 的相關用法,下篇文章見。
參考文章
以下是在學習過程中看的一些博客,不斷向這些作者學習,努力深入知識點,然后寫出盡量高質量的博客。
blog.csdn.net/leehong2005…
Android WebView 的 Js 對象注入漏洞解決方案
blog.csdn.net/typename/ar…
blog.csdn.net/typename/ar…
blog.csdn.net/typename/ar…
Android WebView 開發詳解(共3篇)
www.jianshu.com/p/93cea79a2…
JS 與 WebView 交互存在的一些問題
總結
以上是生活随笔為你收集整理的Android 网络编程系列(3)WebView 详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaScript正则替换去除字符串中
- 下一篇: 非农日汇市如何交易?美元脆弱性加剧,欧元