微信,QQ等大量軟件都內嵌了H5,不得不說是一種趨勢。Android與H5互調可讓咱們的實現混合開發,一部分原生的功能改成Html 5來開發。使用H5實現的功能可以在不升級App的狀況下動態更新,並且能夠在Android或iOS的App上同時運行,節約了成本,提升了開發效率。而原理其實就是 Java代碼 和 JavaScript 之間的調用。要實現Android與H5互調,WebView是一個很重要的控件,WebView 能夠很好地幫助咱們展現 html頁面,因此有必要先了解一下 WebView(官方文檔)。javascript
WebView經常使用方法 | 說明 |
---|---|
loadUrl | 加載網絡資源 (注意要加上網絡權限) |
setWebViewClient() | 設置目標網頁在WebView中顯示,而不是打開系統瀏覽器 |
goBack() | 後退,經過重寫 onKeyDown() 實現點擊返回鍵返回上一瀏覽頁面而非退出程序 |
goForward() | 前進 |
ZoomIn() | 放大網頁 |
zoomOut() | 縮小網頁 |
咱們能夠簡單加載一個頁面:css
webView.getSettings().setJavaScriptEnabled(true); // 設置可使用js webView.setWebViewClient(new WebViewClient()); if(url.indexOf("http")==-1){ url="http://"+url; } // 加載網絡資源(注意要加上網絡權限) webView.loadUrl("https://www.baidu.com"); // 加載assets目錄下的test.html文件 //webView.loadUrl("file:///android_asset/test.html");
1.獲取WebSettings對象html
WebSettings webSettings = webView.getSettings();
2.WebSettings經常使用設置方法java
經常使用設置方法 | 說明 |
---|---|
setJavaScriptEnabled(true) | 支持js |
setCacheMode(WebSettings.LOAD_NO_CACHE) | 設置緩存方式(LOAD_CACHE_ONLY:不使用網絡,只讀取本地緩存數據;LOAD_DEFAULT:根據cache-control決定是否從網絡上取數據;LOAD_NO_CACHE:不使用緩存,只從網絡獲取數據;LOAD_CACHE_ELSE_NETWORK:只要本地有,不管是否過時,或者no-cache,都使用緩存中的數據。) |
setDomStorageEnabled(true) | 開啓DOM storage API功能 |
setDatabasePath(cacheDirPath) | 設置數據庫緩存路徑 |
setAppCachePath(cacheDirPath) | 設置Application Caches緩存目錄 |
setDefaultTextEncodingName("utf-8") | 設置默認編碼 |
setUseWideViewPort(false) | 將圖片調整到適合webview的大小 |
setSupportZoom(true) | 支持縮放 |
setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN) | 支持內容從新佈局 |
supportMultipleWindows() | 多窗口 |
setAllowFileAccess(true) | 設置能夠訪問文件 |
setNeedInitialFocus(true) | 當webview調用requestFocus時爲webview設置節點 |
setBuiltInZoomControls(true); | 設置支持縮放 |
setJavaScriptCanOpenWindowsAutomatically(true) | 支持經過JS打開新窗口 |
setLoadWithOverviewMode(true) | 縮放至屏幕的大小 |
setLoadsImagesAutomatically(true) | 支持自動加載圖片 |
WebViewClient主要用來輔助WebView處理各類通知、請求等事件,經過 setWebViewClient 方法設置。WebViewClient 的回調方法以下:android
回調方法 | 說明 |
---|---|
doUpdateVisitedHistory() | 更新歷史記錄。 |
onFormResubmission() | 應用程序從新請求網頁數據。 |
onLoadResource() | 在加載頁面資源時會調用,每個資源的加載都會調用一次。 |
onPageStarted() | 開始載入頁面調用,一般咱們能夠在這設定一個 loading 條,告訴用戶程序在等待網絡響應。 |
onPageFinished() | 在頁面加載結束時調用,關閉 loading 條,切換程序動做。 |
onReceivedError() | 報告錯誤信息。 |
onReceivedHttpAuthRequest() | 獲取返回信息受權請求。 |
onReceivedSslError() | 重寫此方法可讓webview處理https請求。 |
onScaleChanged() | WebView發生改變時調用。 |
onUnhandledKeyEvent() | Key事件未被加載時調用。 |
shouldOverrideKeyEvent() | 重寫此方法纔可以處理在瀏覽器中的按鍵事件。 |
shouldOverrideUrlLoading() | 在網頁跳轉時調用,屏蔽某些特殊的URL而不作跳轉,進行預先定義的其餘操做,通常每跳轉一次只調用一次。 |
shouldInterceptRequest() | 在加載某個網頁的資源的時屢次調用。 |
webView.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if("http://www.163.com/".equals(url)) { view.loadUrl("https://www.baidu.com/"); } // return true: 點擊H5頁面中的超連接時不會再加載,全部處理都須要在WebView中操做。 // return false: 點擊H5頁面中的超連接時會加載,默認return false。 return true; } });
webView.setWebViewClient(new WebViewClient(){ @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { WebResourceResponse response = null; if (url.contains("logo")) { try { // 進行圖片替換 InputStream logo = getAssets().open("logo.png"); response = new WebResourceResponse("image/png", "UTF-8", logo); } catch (IOException e) { e.printStackTrace(); } } return response; } });
webView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); // 開始加載 } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // 加載完成 } @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error); // 加載錯誤 } });
webView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); // 接受信任全部網站的證書 // handler.cancel(); // 默認操做 不處理 // handler.handleMessage(null); // 可作其餘處理 } });
默認狀況 HTML 代碼下載到 WebView 後,webkit 開始解析網頁各個節點,發現有外部樣式文件或者外部腳本文件時,會異步發起網絡請求下載文件,但若是在這以前也有解析到 image 節點,也會發起網絡請求下載相應的圖片。在網絡狀況較差的狀況下,過多的網絡請求就會形成帶寬緊張,影響到css或js文件加載完成的時間,形成頁面空白 loading 太久。解決的方法就是告訴 WebView 先不要自動加載圖片,等頁面 finish 後再發起圖片加載。web
// 1.首先在WebView初始化時添加以下代碼 if(Build.VERSION.SDK_INT >= 19) { // 對系統API在19以上的版本做了兼容。由於44以上系統在onPageFinished時再恢復圖片加載時, // 若是存在多張圖片引用的是相同的src時,會只有一個image標籤獲得加載,於是對於這樣的系統咱們就先直接加載。 webView.getSettings().setLoadsImagesAutomatically(true); } else { webView.getSettings().setLoadsImagesAutomatically(false); } // 2.在WebView的WebViewClient子類中重寫onPageFinished()方法添加以下代碼 @Override public void onPageFinished(WebView view, String url) { if(!webView.getSettings().getLoadsImagesAutomatically()) { webView.getSettings().setLoadsImagesAutomatically(true); } }
WebChromeClient 主要用來輔助 WebView 處理 Javascript 的對話框、網站圖標、網站標題以及網頁加載進度等。經過 WebView 的 setWebChromeClient() 方法設置。WebChromeClient 的回調方法以下:chrome
回調方法 | 說明 |
---|---|
onProgressChanged() | 監聽網頁加載進度。 |
onReceivedTitle() | 監聽網頁標題 |
onReceivedIcon() | 監聽網頁圖標 |
在WebChromeClient子類中重寫父類的onProgressChanged函數,progress表示當前頁面加載的進度,爲1至100的整數。數據庫
webView.setWebChromeClient(new WebChromeClient() { @Override public void onProgressChanged(WebView view, int progress) { setTitle("Loading..." + progress + "%"); setProgress(progress * 100); if (progress == 100) { // 加載完成 } } });
一般 WebView 渲染的界面中含有能夠下載文件的連接,點擊該連接後,應該開始執行下載的操做並保存文件到本地中。瀏覽器
建立 DownloadListener:緩存
class MyDownloadListenter implements DownloadListener { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { // 下載任務...,主要有兩種方式:1.自定義下載任務;2.調用系統的download的模塊。 Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } }
而後就能夠給 WebView 設置下載監聽了:
webView.setDownloadListener(new MyDownloadListenter());
WebView webView = (WebView) findViewById(R.id.webview); StringBuilder sb = new StringBuilder(); // 拼接一段HTML代碼 sb.append("<html>"); sb.append("<head>"); sb.append("<title> Test </title>"); sb.append("</head>"); sb.append("<body>"); sb.append("<h2> Test Page </h2>"); sb.append("</body>"); sb.append("</html>"); // 使用簡單的loadData方法會致使亂碼 // webView.loadData(sb.toString(), "text/html", "utf-8"); /** * 加載、並顯示HTML代碼 * baseUrl:做爲網頁輸入的網址(通常null) * data:須要加載的HTML代碼 * mimeType:指定HTML代碼的MIME類型,HTML可指定爲text/html * encoding:指定HTML代碼的字符編碼 * historyUrl:歷史輸入的網址(通常null) */ webView.loadDataWithBaseURL(null, sb.toString(), "text/html", "utf-8", null);
Android 與 H5 互調可讓咱們的實現混合開發,一部分原生的功能改成 Html 5 來開發。使用 H5 實現的功能可以在不升級App的狀況下動態更新,並且能夠在Android或iOS的App上同時運行,節約了成本,提升了開發效率。而原理其實就是 Java代碼 和 JavaScript 之間的調用。Android 能夠經過 WebView 來實現和 Javascript 的交互,在程序中調用 Javascript 代碼,只須要將WebView控件的支持 Javascript 的屬性設置爲 true 便可。下面直接上乾貨:
咱們先定義一段 Javascript 代碼:
function javaCallJs(arg){ document.getElementById("content").innerHTML =("Hello:"+arg); }
而後是在 Java 中調用 Javascript 中的方法:
// Java 中調用 Javascript 是經過 loadUrl() 實現的,格式:mWebView.loadUrl("javascript:showFromHtml()"); // 而且需在UI線程運行 webView.loadUrl("javascript:javaCallJs("+"'"+name+"'"+")");
以上代碼就是調用了 Javascript 中一個叫 javaCallJs(arg) 的方法,並傳入了一個 name 參數。
配置 Javascript 接口:
webSettings.setJavaScriptEnabled(true); // 添加接口函數,jsObj 爲橋連對象 webView.addJavascriptInterface(new JSInterface(), "jsObj");
實現 Javascript 接口類:
class JSInterface { @JavascriptInterface public void showLog(String log){ Log.i("JAVA", log); } }
Javascript 中調用 Java 代碼:
<input type="button" value="點擊Android被調用" onclick="window.Android.showLog('Javascript中傳來的參數')"/>
1.若是想要webView在產生OOM的時候不影響主進程,能夠另開一個進程,在androidmanifest.xml的activity標籤里加上Android:process屬性就能夠了。
3.及時銷燬對象,onDestory() 中應該先從 ViewGroup 中 remove 掉 WebView,再調用webView.removeAllViews(); webview.destory();
2.在Activity被殺死以後,依然保持webView的狀態,方便用戶下次打開的時候能夠回到以前的狀態。webview支持saveState(bundle)和restoreState(bundle)方法。