基本設置
WebView可以實現將網頁嵌入到爲應用程序專門設計的環境中。大多數情況還是建議使用標準的網絡瀏覽器將內容交互給用戶。WebView缺少一些完全開發的瀏覽器功能。
private Context mContext;
private WebView mWebView;
/**
* WebView父親佈局
*/
private LinearLayout llContainerWeb;
//可以使用addView的方式,也可以使用xml方式,只要記得必要的時候移除view、清除緩存和置空即可
mWebView = new WebView(mContext);
ViewGroup.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
//把webView加入llContainerWeb
llContainerWeb.addView(mWebView, layoutParams);
//不顯示橫向滑動條
mWebView.setHorizontalScrollBarEnabled(false);
//不顯示縱向滑動條
mWebView.setVerticalScrollBarEnabled(false);
WebSettings webSettings = mWebView.getSettings();
//設置是否啓用javascript
webSettings.setJavaScriptEnabled(true);
//是否支持縮放(這個是前提)
webSettings.setSupportZoom(false);
//是否顯示縮放工具(當上門那個成立,再設置這個,必要條件)
webSettings.setBuiltInZoomControls(false);
//設置此屬性,可任意比例縮放
webSettings.setUseWideViewPort(false);
//設置充滿全屏
webSettings.setLoadWithOverviewMode(true);
//把所有內容放到WebView組件等寬的一列中
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
//設置字體默認大小
/*webSettings.setDefaultFontSize(15);*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//設置不允許安全來源從不安全的位置加載內容起源,非常建議使用這個。
//webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
//不建議使用這個,從任何來源加載內容。5.1以上默認禁止了https和http混用 這是開啓
//webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
//如下設置是解決Cross origin requests are only supported for protocol schemes: http, data, chrome, https.這種問題的。
//webSettings.setAllowFileAccess(true);
//webSettings.setAllowFileAccessFromFileURLs(true);
//能否訪問來自於任何源的文件標識的URL
//webSettings.setAllowUniversalAccessFromFileURLs(true);
//設置WebViewClient,可以攔截url,做一些處理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
//攔截url:根據協議的參數,判斷是否是所需要的url
//比如約定好的字段爲"https://www.haizhuo.com/user?name=hz&id=15"
Uri uri = request.getUrl();
if (uri.getScheme().equals("https")) {
if (uri.getHost().equals("www.haizhuo.com")) {
//todo
}
}
return super.shouldOverrideUrlLoading(view, request);
}
});
} else {
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (uri.getScheme().equals("https")) {
if (uri.getHost().equals("www.haizhuo.com")) {
//todo
}
}
return super.shouldOverrideUrlLoading(view, url);
}
});
}
//設置WebChromeClient,可以獲得js回調、進度、標題、圖標等的回調。
mWebView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
}
});
mWebView.loadUrl("https://www.baidu.com/");
//直接加載assets目錄下的html網頁,並且與網頁有關的css,js,圖片等文件會加載。
//mWebView.loadUrl("file:///android_asset/test/test.html");
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getKeyCode()==KeyEvent.KEYCODE_BACK&&mWebView.canGoBack()){
//獲取webView的瀏覽記錄
WebBackForwardList webBackForwardList=mWebView.copyBackForwardList();
//有上一個界面,就跳轉上一個界面
if (webBackForwardList.getCurrentIndex()>0){
mWebView.goBack();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onDestroy() {
super.onDestroy();
//使用完畢,清除webview緩存和歷史,避免內存泄露
mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebView.clearHistory();
// mWebView.clearCache(true);
// mWebView.clearSslPreferences();
//不要在android.graphics.Canvas#draw、dispatchDraw、onDraw方法裏調用此方法
// webView.removeAllViews();
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy();
mWebView = null;
}
Android調用JS代碼
注意事項:JS代碼調用要在 onPageFinished() 回調之後。
通過WebView的loadUrl()
一般樣式
String url = "javascript:methodName(params……)";
webView.loadUrl(url);
例1:調用網頁端寫好的方法
<script type="text/javascript">
function testPop(){
alert("17張牌你能秒我?");
}
</script>
mWebView.loadUrl("javascript:testPop()");
例2:調用在客戶端寫的方法(網頁端沒有)
//設置網頁中所有標籤名字爲‘img’的對象高寬
mWebView.loadUrl("javascript:(function(){" +
"var objs = document.getElementsByTagName('img'); " +
"for(var i=0;i<objs.length;i++) " +
"{"
+ "var img = objs[i]; " +
" img.style.maxWidth = '100%'; img.style.height = 'auto'; " +
"}" +
"})()");
通過WebView的evaluateJavascript()
mWebView.evaluateJavascript("javascript:test()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此處爲返回的結果
}
});
2個方法對比
JS通過WebView調用 Android 代碼
不管通過什麼方式,雙方都需要做一個約定。比如通過WebChromeClient攔截JS對話框消息,當消息是某約定的字段,Android客戶端做某個操作;再如通過WebViewClient攔截url,當網頁跳轉到某個約定的地址時,Android客戶端做某個操作;還有WebView方法addJavascriptInterface,也要約定對象映射的名字,這樣,js端才能通過這個“名字”的對象調用方法。
1. 通過對象映射
WebView的addJavascriptInterface()。addJavascriptInterface是WebKit的原生API,屬於WebView對象的公共方法,用於暴露一個java對象給js,使得js可以直接調用方法。當然,java與js的相互調用也離不開loadUrl()方法的配合使用。
例子
先寫一個映射關係的類
class MyJsInterfaceTest{
//API 17以後一定要添加註解,否則addJavascriptInterface方法會失效。
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String getAppNameFromAndroid(){
return MyApplication.getInstance().getAppName();
}
}
然後設置WebView
webView.getSettings().setJavaScriptEnabled(true);
//...省略部分代碼
webView.addJavascriptInterface(new MyJsInterfaceTest(), "MyJsInterfaceTest");
在html裏通過映射關係類調用方法
<script type="text/javascript">
//調用Android端代碼獲取名字,並彈框顯示
function getName(){
alert(myJsTest.getName());
}
</script>
//...省略部分代碼
<button class="bt_todo_java" id="button_one" onclick="getName()">js調用Android代碼</button>
2. 攔截url
WebViewClient 的shouldOverrideUrlLoading ()方法
當即將在當前WebView中加載URL時,使主機應用程序有機會控制。 如果未提供WebViewClient,則默認情況下,WebView將要求“活動管理器”爲URL選擇適當的處理程序。 如果提供了WebViewClient,則返回true會導致當前WebView放棄加載URL,而返回false會導致WebView照常繼續加載URL。
WebView在加載一個頁面開始的時候會回調onPageStarted方法,在該頁面加載完成之後會回調onPageFinished方法。而如果該鏈接發生了重定向,回調shouldOverrideUrlLoading會在回調onPageFinished之前。
示例
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mWebview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
//攔截url:根據協議的參數,判斷是否是所需要的url
//比如約定好的字段爲"https://www.haizhuo.com/user?name=hz&id=15"
Uri uri = request.getUrl();
if (uri.getScheme().equals("https")) {
if (uri.getHost().equals("www.haizhuo.com")) {
//todo
}
}
return super.shouldOverrideUrlLoading(view, request);
}
});
} else {
mWebview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (uri.getScheme().equals("https")) {
if (uri.getHost().equals("www.haizhuo.com")) {
//todo
}
}
return super.shouldOverrideUrlLoading(view, url);
}
});
}
shouldOverrideUrlLoading不執行的情況
通常情況,直接與用戶交互,跳轉的地址,shouldOverrideUrlLoading是能攔截到的。
而延時跳轉的話(比如在某個回調裏跳轉),之後的跳轉shouldOverrideUrlLoading都攔截不到了,但會將請求的頁面加載到webview中。
以example.html爲例
//...省略其他代碼
function goToResultPage(){
//本頁跳轉
window.location.href='http://www.myresult.com.cn';
}
//延時跳轉
setTimeout(goToResultPage, 3000);
分析:正常操作到example.html,url都有攔截到,但是跳轉到http://www.myresult.com.cn及他以後的地址shouldOverrideUrlLoading都不會有回調了。
3. 攔截JS對話框
WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調攔截JS對話框alert()、confirm()、prompt() 消息
除此之外,還有onProgressChanged()、onReceivedTitle()、onReceivedTouchIconUrl()等一些列方法。
上述3方法對比
補充知識點
WebViewClient
WebViewClient這個組件主要用來輔助webview處理一些簡單的html頁面內容,比如各種通知,請求事件等等,也就是說在用這個組件你可以監聽網頁加載是否完成,是否開始等等,然後還有就是可以設置是否在外部瀏覽器中瀏覽。
onLoadResource、onPageStart、onPageFinish、onReceiveError、onReceivedHttpAuthRequest、shouldOverrideUrlLoading
WebChromeClient
WebChromeClient可以設置標題啥的。
關於在js中打印日誌,java中獲取打印信息以及提示、打印回調
- 在 Javascript 代碼中調用console.log(‘xxx’)方法,中Native代碼會回調WebChromeClient.consoleMessage()
- JS中調用window.prompt(message, value),WebChromeClient.onJsPrompt()就會收到回調信息
- JS中調用 alert(“信息”),WebChromeClient.onJsAlert()就會收到回調信息
除上面幾個外,再記錄一些(不是全部):onReceivedTitle、onReceivedIcon、onReceivedTouchIconUrl、onHideCustomView、onRequestFocus、onCloseWindow、onJsBeforeUnload、onGeolocationPermissionsShowPrompt、onGeolocationPermissionsHidePrompt、onPermissionRequest、onPermissionRequestCanceled、onConsoleMessage、getDefaultVideoPoster、getVisitedHistory等
跨域
什麼是跨域?什麼是Cross-origin_resource_sharing?
跨域請求存在的原因:由於瀏覽器的同源策略,即屬於不同域的頁面之間不能相互訪問各自的頁面內容。
跨域使用的場景?
- 域名不同
www.jiuyescm.com和www.jiuye.com即爲不同的域名 - 二級域名相同,子域名不同
a.jiuyescm.com和b.jiuyescm.com爲子域不同 - 端口不同,協議不同
http://www.jiuyescm.com 和 https://www.jiuyescm.com
www.jiuyescm.com:8888和www.jiuyescm.com:8080
解決跨域的方式?
- jsonp
安全性差,已經不推薦 - CORS(W3C標準,跨域資源共享 - Cross-origin resource sharing)
服務端設置,安全性高,推薦使用 - websocke
特殊場景時使用,不屬於常規跨域操作 - 代理服務(nginx)
可作爲服務端cors配置的一種方式,推薦使用