WebView開發詳解
目錄
一.前言
現在很多app並不是純原生開發,而是會巢狀網頁,比如一些經常會變動的頁面往往會採用巢狀h5網頁的形式展現。Android中就有一個專門用來載入html網頁的元件,這個元件就是Webview。
二.概述
Webview是Android開發中常見的控制元件,內部實現是採用渲染引擎來展示內容,Android4.4以前採用Webkit渲染引擎,4.4版本及以後開始採用chromium渲染引擎來渲染。
三.使用介紹
3.1 基本使用
(1)xml佈局
<WebView
android:id="@+id/webview"
android:layout_width ="match_parent"
android:layout_height="match_parent" />
(2)activity或fragment中載入webview控制元件
WebView webView =(WebView) findViewById(R.id.webview);
//或者
WebView webView =new WebView(this);
(3)新增網路許可權
<uses-permission android:name="android.permission.INTERNET"/>
(4)訪問網頁
webview.loadurl ("https://feibendexiaoma.github.io");
Tip : 如果為了避免記憶體洩漏可以在xml中定義一個viewgroup,如linearLayout,然後在程式碼中動態建立webView
3.2 常用方法詳解
3.2.1 載入url,這個url可以是網路的url,可以是手機裡的html網頁url,也可以是assets資料夾下的網頁url
webview.loadurl("https://feibendexiaoma.github.io");
webview.loadurl("content://com.android.htmlfileprovider/sdcard/index.html" );
webview.loadurl("file:///android_asset/index.html");
//載入url,並新增HTTP headers
webview.loadUrl(String url, Map<String, String> additionalHttpHeaders);
3.2.2 載入網頁中的一部分程式碼
/**
* data:要載入的內容 e.g. 'text/html'. If null,defaults to 'text/html'.
mimeType:data資料型別的mime型別
encoding:data的編碼格式 如果data是base64編碼,則encoding必須為"base64"
*/
webview.loadData(String data, String mimeType, String encoding)
/**
* baseUrl 載入的base url的網頁內容
historyUrl 歷史記錄條目的url 如果為空預設跳轉到“about :blank”,如果非null,則必須是有效的url
*/
webview.loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl)
3.2.3 前進後退
webview.canGoBack()//是否能後退
webview.goBack();//後退
webview.canGoForward();//是否能前進
webview.goForward();//前進
webview.goBackOrForward(int steps);//以當前的index為起始點前進或後退到歷史記錄中指定的steps,steps為正則為前進,steps為負則為後退
Tip : 一般常用的是後退的方法
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
clickBtnReback();
return true;
}
return super.onKeyDown(keyCode, event);
}
public void clickBtnReback() {
if (mCustomWebView.canGoBack()) {//能回退時回退
mCustomWebView.goBack();
}else {
ActivityStack.getInstance().closeActivity(mActivity);
}
}
3.2.4 清除快取
//清除資源快取,由於核心快取是全域性的 ,因此這裡針對的是整個應用程式
public void clearCache(boolean includeDiskFiles)
//清除自動完成填充的表單資料,不會清除WebView儲存到本地的資料
public void clearFormData()
//清除webview訪問的歷史記錄,清除其前進後退列表裡的快取,除了當前訪問記錄
public void clearHistory ()
3.2.5 WebView的狀態
//啟用webview為活躍狀態,能正常執行網頁的響應 在前一個頁面執行完onPause()後會執行這個方法
public void onResume ()
//當前頁面失去焦點被切換為不可見狀態,會執行onPause,會嘗試暫停所有可以安全暫停的動作,如DOM的解析、plugin的執行,注意不會暫停javascript。要想暫停javascript可以用pauseTimers
public void onPause()
//當應用程式被切換到後臺時,我們使用了webview,這個方法不僅針對當前webview,而是全域性應用程式的Webview,會暫停所有webview的layout,解析和JavaScript
public void pauseTimers ()
//恢復所有Webview的所有佈局,解析和JavaScript計時器
public void resumeTimers()
//呼叫destroy時,必須保證Webview已經從view tree中被刪除了,可以在之前呼叫根佈局的removeView方法將webview移除
public void destroy ()
3.3 相關設定介紹
3.3.1.WebSettings設定
WebSettings mSettings = this.getSettings();
mSettings.setJavaScriptEnabled(true);// 開啟javascript 如果訪問的頁面中有與js互動,則必須開啟
mSettings.setJavaScriptCanOpenWindowsAutomatically(true);////支援通過JS開啟新視窗
mSettings.setDomStorageEnabled(true);// 開啟DOM
mSettings.setDatabaseEnabled(true);//開啟database
mSettings.setDefaultTextEncodingName("utf-8");// 設定字元編碼
mSettings.setAllowFileAccess(true);// 設定支援檔案流
//LOAD_DEFAULT預設快取模式 根據cache-control決定是否從網路上取資料
//LOAD_CACHE_ONLY: 不使用網路,只讀取本地快取資料
//LOAD_NO_CACHE: 不使用快取,只從網路獲取資料.
////LOAD_CACHE_ELSE_NETWORK,只要本地有,無論是否過期,或者no-cache,都使用快取中的資料。
mSettings.setCacheMode(WebSettings.LOAD_DEFAULT);// 設定快取模式
mSettings.setSupportZoom(false);// 支援縮放
mSettings.setBuiltInZoomControls(false);// 設定內建的縮放控制元件
//以下兩個結合使用
mSettings.setUseWideViewPort(true);// 調整圖片適合webview大小
mSettings.setLoadWithOverviewMode(true);// 調整到螢幕大小
mSettings.setDefaultZoom(ZoomDensity.FAR);// 螢幕自適應網頁,如果沒有這個,在低解析度的 手機上顯示可能會異常
mSettings.setRenderPriority(RenderPriority.HIGH);
// 提高網頁載入速度,暫時阻塞圖片載入,然後網頁載入好了,在進行載入圖片
mSettings.setBlockNetworkImage(true);
mSettings.setAppCacheEnabled(true);// 開啟快取機制
3.3.2 WebViewClient
用於處理請求事件,載入設定。
常用方法:
webview.setWebViewClient(new WebViewClient(){
/**
* 重寫此方法表明點選網頁裡面的連結還是在當前的webview裡跳轉,不跳到瀏覽器那邊
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.endsWith(".apk")) {//新增下載
WebActivity.this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
} else {
view.loadUrl(url);
}
/**
* 頁面開始載入呼叫的方法
*
*/
@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 onLoadResource(WebView view, String url) {
super.onLoadResource(view, url);
}
/**
* 頁面加載出錯的時候呼叫 如404
在這裡可以自定義錯誤頁面
*/
@Override
public void onReceivedError(WebView view, WebResourceRequest request,WebResourceError error) {
super.onReceivedError(view, request, error);
}
/**
*載入網頁發生證書認證錯誤時
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.cancel(); //取消載入
// handler.proceed(); //如果要上傳到google play ,回撥中使用此方法是無法通過稽核的
// handler.handleMessage(null); //可做其他處理
}
}
});
3.3.3 WebChromeClient
用於處理載入條,對話方塊,網站標題等
常見方法:
webview.setWebChromeClient(new WebChromeClient(){
/**
* 處理進度條
*/
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress >= 100) {
myProgressBar.setVisibility(View.GONE);
} else {
if (View.GONE == myProgressBar.getVisibility()) {
myProgressBar.setVisibility(View.VISIBLE);
}
myProgressBar.setProgress(newProgress);
}
super.onProgressChanged(view, newProgress);
}
/**
* 獲取網站的標題
*/
@Override
public void onReceivedTitle(WebView view, String title) {
titleview.setText(title);
}
/**
* 支援js彈alert窗處理
*/
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
new AlertDialog.Builder(MainActivity.this)
.setTitle("JsAlert")
.setMessage(message)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.setCancelable(false)
.show();
return true;
}
/**
*支援js 確認框處理
*/
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
});
3.4 相關問題
3.4.1 記憶體洩漏
Webview為什麼會發生記憶體洩漏?
Webview會關聯一個activity,webview內部執行的操作是在新的執行緒中,這個執行緒的生命週期和activity的生命週期是不一樣的,導致webview一直持有activity的引用不能回收。和匿名內部類持有外部類的引用導致外部類無法回收原理一樣。
首先應該做的就是不能在xml中定義webview節點,而是在需要的時候動態生成。即:可以在使用WebView的地方放置一個LinearLayout類似ViewGroup的節點,然後在要使用WebView的時候,動態生成. new WebView(getApplicationContext()) ;必須傳入ApplicationContext如果傳入Activity的Context的話,對記憶體的引用會一直被保持著。
LinearLayout.LayoutParams params =new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
WebView mWebView = new WebView(getApplicationContext());
mWebView.setLayoutParams(params);
mLayout.addView(mWebView);
在onDestroy()中將webview移除
@Override
protected void onDestroy() {
if(mWebView!=null){
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
3.4.2 微信支付問題
我在開發時遇到這樣的問題:當網頁中有支付時,android自帶的webview不能響應支付的相關協議,而ios的就可以,搜尋網上的方法及開發者文件,解釋是ios內建瀏覽器中只要輸入相關協議都可以調起相關app,比如輸入weixin://就可以調起微信,輸入alipay://可以調起支付寶。
檢視微信支付的開發者文件(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1)知道:
H5支付是指商戶在微信客戶端外的移動端網頁展示商品或服務,使用者在前述頁面確認使用微信支付時,商戶發起本服務呼起微信客戶端進行支付。
主要用於觸屏版的手機瀏覽器請求微信支付的場景。可以方便的從外部瀏覽器喚起微信支付。
微信支付的案例介紹給出了一個體驗連結:http://wxpay.wxutil.com/mch/pay/h5.v2.php,在微信外瀏覽器開啟,是可以調起微信支付的,看來微信是支援瀏覽器調起微信支付的。
解決方案
方案一
由於微信的H5支付協議可以在瀏覽器中調起微信,那麼如果我們的app開啟第三方網頁的是手機瀏覽器的話,就不用我們做什麼,就可以直接調起微信支付了。但是我們一般是在webview中調起微信支付,而直接在瀏覽器中開啟的非常少,所以這種方法作為參考
方案二
由於以前的微信支付的協議為weixin://wap/pay?,後來改版之後url中就沒有這個了,替換為https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?
這個開頭的url了,其實方案二也是參考的方案一,可以通過判斷支付前一步的網頁地址,在支付的前一步就跳到手機瀏覽器內,通過瀏覽器調起微信支付。
public boolean shouldOverrideUrlLoading(WebView view, String url) {
try {
if (url.startsWith("http://dh.xxx.cn/index/order/pay")) {//在支付的前一個網頁就跳轉出去用瀏覽器調支付
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
return true;
}
因涉及到公司網址,就以xxx代表了,親測這樣是可以通過手機瀏覽器調起微信支付的。
3.5 與Js互動
3.5.1 Android呼叫js
(1)首先要想呼叫網頁上js指令碼,webview必須支援java script
mWebView.getSettings().setJavaScriptEnabled(true);//支援javascript
// 設定允許JS彈窗 測試使用
mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
(2)js程式碼 test.html 放到src\main\assets下
<!DOCTYPE html>
<html>
<script type="text/javascript">
//android 要呼叫的方法
function helloworld(){
alert("helloworld")
}
function alertMsg(msg){
alert(msg)
}
function toastMsg(msg){
window.web2app.toastMsg(msg)
}
function add(num1,num2){
window.web2app.add(num1+num2)
}
</script>
java-js in Android
</html>
(3)java 呼叫js的方法,注意這裡的方法名要和test.html裡的名稱對應
String method ="javascript:helloworld()";//無引數呼叫
method ="javascript:alertMsg(\""+ "content" +"\")";//有引數呼叫 注意對於字串作為引數值需要進行轉義雙引號
mWebView.loadUrl(method);
(4)呼叫setWebChromeClient處理彈窗,這一步只為測試時用
mWebView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder ad = new AlertDialog.Builder(WebTestActivity.this);
ad.setTitle("Java 呼叫了JS方法");
ad.setMessage(message);
ad.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
ad.setCancelable(false);
ad.create().show();
return true;
}
});
java整體程式碼
private void initView() {
mWebView = (WebView)findViewById(R.id.webview);
btnJava = (Button)findViewById(R.id.btn_java_to_js);
WebSettings mWebSettings = mWebView.getSettings();
mWebSettings.setJavaScriptEnabled(true);//開啟支援js
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);//支援彈窗
mWebView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
//處理彈窗後的樣式
AlertDialog.Builder ad = new AlertDialog.Builder(WebTestActivity.this);
ad.setTitle("Java 呼叫了JS方法");
ad.setMessage(message);
ad.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
ad.setCancelable(false);
ad.create().show();
return true;
}
});
btnJava.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//呼叫js方法
javaToJsMethod();
}
});
mWebView.loadUrl("file:///android_asset/test.html");
}
private void javaToJsMethod() {
String method ="javascript:helloworld()";
method ="javascript:alertMsg(\""+ "content" +"\")";
mWebView.loadUrl(method);
}
效果:
Android4.4以後處理
另外在Android4.4之後引入了一個方法evaluateJavascript,因為該方法的執行不會使頁面重新整理,比loadurl更高效,但是隻能用於4.4以後版本。
private void testEvaluateJavascript(WebView webView) {
webView.evaluateJavascript("helloworld()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//處理js 返回的結果
}});
}
在使用時可以通過判斷版本號,根據版本號是4.4以下的話使用loadurl,4.4以上版本可以使用evaluateJavascript
3.5.2 Js 呼叫Android的方法
(1)新增webview的addJavascriptInterface方法,並使用web2app作為注入介面名稱,這個名稱可以自定義,但是要和html中的名稱一致
(2)定義一個與js物件對映關係的android 類 jsToJavaInteration,裡面定義js要呼叫的方法
public class jsToJavaInteration{
@JavascriptInterface
public void toastMsg(String msg){
Toast.makeText(getApplicationContext(),msg,Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void add(String result){
Toast.makeText(getApplicationContext(),result,Toast.LENGTH_SHORT).show();
}
}
(3)載入js方法 ,這裡的方法要和test.html中的方法名一致
private void javaToJsMethod() {
String method ="javascript:helloworld()";
// method ="javascript:alertMsg(\""+ "content" +"\")";
method = "javascript:toastMsg(\"" + "content" + "\")";//有參呼叫
method = "javascript:add(2,3)";//
mWebView.loadUrl(method);
}
(4)同上第四步
整體java類
private void initView() {
mWebView = (WebView)findViewById(R.id.webview);
btnJava = (Button)findViewById(R.id.btn_java_to_js);
WebSettings mWebSettings = mWebView.getSettings();
mWebSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new jsToJavaInteration(),"web2app");
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder ad = new AlertDialog.Builder(WebTestActivity.this);
ad.setTitle("Java 呼叫了JS方法");
ad.setMessage(message);
ad.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
ad.setCancelable(false);
ad.create().show();
return true;
}
});
btnJava.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
javaToJsMethod();
}
});
mWebView.loadUrl("file:///android_asset/test.html");
}
private void javaToJsMethod() {
String method="";
// method ="javascript:helloworld()";
// method ="javascript:alertMsg(\""+ "content" +"\")";
method = "javascript:toastMsg(\"" + "content" + "\")";
method = "javascript:add(2,3)";
mWebView.loadUrl(method);
}
//js 呼叫java 類方法
public class jsToJavaInteration{
@JavascriptInterface
public void toastMsg(String msg){
Toast.makeText(getApplicationContext(),msg,Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void add(String result){
Toast.makeText(getApplicationContext(),result,Toast.LENGTH_SHORT).show();
}
}
效果:
四.使用示例參照上面程式碼
五.總結
本節寫了一些關於webview的知識,包括webview的概述,基本使用,常用方法介紹,相關設定,記憶體洩漏,webview遇到的問題,java與js的互動,並用一些程式碼做了說明,希望對你對我都有幫助,有不完善的後續補充哦。