0、常用设置:
常用设置1:WebSettings
常用设置2:WebViewClient
常用设置3:WebChromeClient
1、使用方法
新建Activity:WebViewButtonActivity
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.ClientCertRequest;
import android.webkit.CookieManager;
import android.webkit.GeolocationPermissions;
import android.webkit.HttpAuthHandler;
import android.webkit.JavascriptInterface;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.RenderProcessGoneDetail;
import android.webkit.SafeBrowsingResponse;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
import android.webkit.WebHistoryItem;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import java.util.HashMap;
public class WebViewButtonActivity extends AppCompatActivity {
private WebView mWebView;
public class TestJSEvent{
@JavascriptInterface
public void showToast(String toast){
Toast.makeText(WebViewButtonActivity.this, toast, Toast.LENGTH_LONG).show();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view_button);
mWebView = findViewById(R.id.activity_webview);
// 设置为调试状态 仅支持Android4.4以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ) {
WebView.setWebContentsDebuggingEnabled(true);
}
mWebView.loadUrl("http://www.baidu.com");
// mWebView.loadUrl("file:///android_asset/test.html");//读取本地html文件的标准格式
// HashMap<String, String> map = new HashMap<>();
// map.put("token", "xxxxx");
// map.put("myhead", "header");
// mWebView.loadUrl("http://www.baidu.com",map);//改变header,传递token
mWebView.getSettings().setJavaScriptEnabled(true);//允许和JavaScript交互,可以实现页面内的跳转
mWebView.getSettings().setDomStorageEnabled(true);//允许android调用javascript
// JS调用原生App
mWebView.addJavascriptInterface(new TestJSEvent(), "TestApp");
// 原生App调用JS
mWebView.loadUrl("javascript:javaCallJS('')");
// 常用设置2:WebViewClient
mWebView.setWebViewClient(new TestWebViewClient());
// 常用设置3:WebChromeClient
mWebView.setWebChromeClient(new TestWebChromeClient());
}
// 自定义一个内部类用于设置WebChromeClient
public class TestWebViewClient extends WebViewClient{
public TestWebViewClient(){
super();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 是否重新加载(拦截界面)
// if(url.contains("404")){
// // 可以跳转至登录界面
// }
// view.loadUrl("https://www.zhihu.com/signin?next=%2F");
// return super.shouldOverrideUrlLoading(view, url);//偶尔闪退,因为有js定义的scheme无法被处理
if (url == null) return false;
try{
if(!url.startsWith("http://") && !url.startsWith("https://")){
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}catch (Exception e){//防止crash (如果手机上没有安装处理某个scheme开头的url的APP, 会导致crash)
return true;//没有安装该app时,返回true,表示拦截自定义链接,但不跳转,避免弹出上面的错误页面
}
// TODO Auto-generated method stub
//返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
view.loadUrl(url);
return true;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return super.shouldOverrideUrlLoading(view, request);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// 页面开始,显示loading动画
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
// 页面结束,隐藏loading动画
}
@Override
public void onLoadResource(WebView view, String url) {
// url 替换
if(url.contains("log.img")){
// 替换成更清晰的图片,省流量的方法
}
super.onLoadResource(view, url);
}
@Override
public void onPageCommitVisible(WebView view, String url) {
super.onPageCommitVisible(view, url);
}
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
// 拦截的是url
return super.shouldInterceptRequest(view, url);
}
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
//拦截的是资源,如点赞,其实不会跳转新的界面,但也是对服务器的一种请求,请求点赞数加1
// hybrid 离线网页
return super.shouldInterceptRequest(view, request);
}
@Override
public void onTooManyRedirects(WebView view, Message cancelMsg, Message continueMsg) {
super.onTooManyRedirects(view, cancelMsg, continueMsg);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
}
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
super.onReceivedHttpError(view, request, errorResponse);
}
@Override
public void onFormResubmission(WebView view, Message dontResend, Message resend) {
super.onFormResubmission(view, dontResend, resend);
}
@Override
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
super.doUpdateVisitedHistory(view, url, isReload);
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
}
@Override
public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
super.onReceivedClientCertRequest(view, request);
}
@Override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
super.onReceivedHttpAuthRequest(view, handler, host, realm);
}
@Override
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
return super.shouldOverrideKeyEvent(view, event);
}
@Override
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
super.onUnhandledKeyEvent(view, event);
}
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale);
}
@Override
public void onReceivedLoginRequest(WebView view, String realm, @Nullable String account, String args) {
super.onReceivedLoginRequest(view, realm, account, args);
}
@Override
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
return super.onRenderProcessGone(view, detail);
}
@Override
public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType, SafeBrowsingResponse callback) {
super.onSafeBrowsingHit(view, request, threatType, callback);
}
}
// 自定义一个内部类用于设置WebChromeClient
public class TestWebChromeClient extends WebChromeClient{
public TestWebChromeClient(){
super();
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
// 获取网页加载的进度 可制作进度条
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
// 获取网页标题
super.onReceivedTitle(view, title);
}
@Override
public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) {
super.onReceivedTouchIconUrl(view, url, precomposed);
}
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
super.onShowCustomView(view, callback);
}
@Override
public void onCloseWindow(WebView window) {
super.onCloseWindow(window);
}
@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 boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
return super.onJsBeforeUnload(view, url, message, result);
}
@Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
super.onGeolocationPermissionsShowPrompt(origin, callback);
}
@Override
public void onGeolocationPermissionsHidePrompt() {
super.onGeolocationPermissionsHidePrompt();
}
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
}
}
// 导航---》后退时返回上一页
@Override
public void onBackPressed() {
if(mWebView != null && mWebView.canGoBack()){
// 历史记录
// WebBackForwardList webBackForwardList = mWebView.copyBackForwardList();
// WebHistoryItem historyItem = webBackForwardList.getItemAtIndex(0);
// String historyUrl = historyItem.getUrl();
mWebView.goBack();
// mWebView.goForward();
// mWebView.goBackOrForward(+2);//前进2步
// mWebView.goBackOrForward(-1);//后退1步
// mWebView.reload();
}else{
super.onBackPressed();
}
}
}
相应的xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".WebViewButtonActivity">
<WebView
android:id="@+id/activity_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
用到的test.html是放在main文件夹下,与java和res平级的assets文件夹中:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=gb2312">
<script type="text/javascript">
function javaCallJS(){
document.getElementById("content").innerHTML +=
"<br\>java调用了js函数";
}
function javaCallJSWithArgs(arg){
document.getElementById("content").innerHTML +=
("<br\>"+arg);
console.log(arg);
HJApp.ui_toast('{"message": "123124", "duration": "2000"}')
}
</script>
</head>
<body>
JS-SDK测试页面 <br/>
<a onClick="TestApp.showToast('我是toast')">Toast</a><br/>
<br/>
<div id="content">内容显示</div>
<script>
function test(){
console.log(8);
}
</script>
</body>
</html>
注意要申请网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
2、JS与App的交互
JS调用App
注意点1:想给JS调用的函数头上要加@JavascriptInterface
public class TestJSEvent{
@JavascriptInterface
public void showToast(String toast){
Toast.makeText(WebViewButtonActivity.this, toast, Toast.LENGTH_LONG).show();
}
}
注意点2:要允许与JS的交互:
mWebView.getSettings().setJavaScriptEnabled(true);//允许和JavaScript交互
mWebView.getSettings().setDomStorageEnabled(true);//允许android调用javascript
注意点3:为真正开始调用而进行的准备,TestApp是JS会用的方法名
mWebView.addJavascriptInterface(new TestJSEvent(), "TestApp");// JS调用原生App
注意点4:真的在JS中调用TestApp方法:
<a onClick="TestApp.showToast('我是toast')">Toast</a><br/>
App调用JS
mWebView.loadUrl("javascript:javaCallJS('')"); // 原生App调用JS
3、页面导航:回退与前进
点击手机的back按钮后,不管你浏览了多少层页面,也会直接回退到最初的页面,为了能点击back回退到上一层,重写onBackPressed
方法:
// 导航---》后退时返回上一页
@Override
public void onBackPressed() {
if(mWebView != null && mWebView.canGoBack()){
mWebView.goBack();
}else{
super.onBackPressed();
}
}
其他方法:
onBackPressed中:
mWebView.goBack();
mWebView.goForward();
mWebView.goBackOrForward(+2);//前进2步
mWebView.goBackOrForward(-1);//后退1步
mWebView.reload();
获取浏览的历史记录:
onBackPressed中:
WebBackForwardList webBackForwardList = mWebView.copyBackForwardList();
WebHistoryItem historyItem = webBackForwardList.getItemAtIndex(0);
String historyUrl = historyItem.getUrl();
4、cookie与token
与前端的交互相关
// 写cookie
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.setCookie("domain", "cookie");
将token传递给服务器:
HashMap<String, String> map = new HashMap<>();
map.put("token", "xxxxx");
map.put("myhead", "header");
mWebView.loadUrl("http://www.baidu.com",map);//改变header,传递token
5、在Chrome调试WebView内页面(Android环境)
(1)在WebView页面打开可以debug的设置:
// 设置为调试状态 仅支持Android4.4以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ) {
WebView.setWebContentsDebuggingEnabled(true);
}
(2)手机打开usb调试,插上电脑;
(3)打开谷歌浏览器,输入:chrome://inspect,并回车;
(4)需要fq,可以在电脑端调试网页界面,手机上会有相应的反映。
6、如何拦截网页请求
1、可以确定拦截或不拦截,true表示拦截,false表示不拦截
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 是否重新加载(拦截界面)
if(url.contains("404")){
// 可以跳转至登录界面或其他界面
view.loadUrl("https://www.zhihu.com/signin?next=%2F");
}
return super.shouldOverrideUrlLoading(view, url);//偶尔闪退,因为有js定义的scheme无法被处理
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return super.shouldOverrideUrlLoading(view, request);
}
2、如果返回为空表示不拦截,如果想拦截必须要返回网页流,把之前的网页替换掉
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
// 拦截的是url
return super.shouldInterceptRequest(view, url);
}
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
//拦截的是资源,如点赞,其实不会跳转新的界面,但也是对服务器的一种请求,请求点赞数加1
// hybrid 离线网页
return super.shouldInterceptRequest(view, request);
}
7、显示页面加载进度
WebChromeClient中:
@Override
public void onProgressChanged(WebView view, int newProgress) {
// 获取网页加载的进度 可制作进度条
super.onProgressChanged(view, newProgress);
}
踩坑
报错1:
Android 10.0 WebView无法加载页面报错net:ERR_CLEARTEXT_NOT_PERMITTED
原因:
从Android 9.0(API级别28)开始,默认情况下禁用明文支持。因此http的url均无法在webview中加载。
解决办法:
在AndroidManifest.xml文件中的Application标签添加:
android:usesCleartextTraffic="true"
报错2:
加载自定义scheme报错net::ERR_UNKNOWN_URL_SCHEME
原因:
网页出来后才出现这个错误页面,原因是加载的这个url内部有JavaScript代码里面包含自定义scheme代码导致加载这个JavaScript代码里面的自定义scheme的时候就报错无法加载。就是你想加载某个https网址,但是该https网址里面携带有自定义的scheme属性JavaScript文件,android中默认加载http和https协议,其他自定义协议规定为不安全默认不让加载。android6.0以上加载自定义scheme会报错如下,6.0以下貌似不会。
解决办法:
mWebView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url == null) return false;
try{
if(!url.startsWith("http://") && !url.startsWith("https://")){
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}catch (Exception e){//防止crash (如果手机上没有安装处理某个scheme开头的url的APP, 会导致crash)
return true;//没有安装该app时,返回true,表示拦截自定义链接,但不跳转,避免弹出上面的错误页面
}
// TODO Auto-generated method stub
//返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
view.loadUrl(url);
return true;
}
};