WebView是基于webkit引擎展现web页面的控件。不同系统版本使用webkit版本不同,4.4后直接使用了chrome。
作用:显示和渲染web页面加载url;直接使用html文件(网络上或assests中)做布局;可以和javaScript交互调用。
一个特殊的view除了具备一般view的属性,还可以对url请求、页面加载、渲染、Android与html页面交互进行强大处理。
1、WebView三大件
1.1 WebSettings****提供常用的设置WebView的属性和状态的方法
@SuppressLint("SetJavaScriptEnabled")
private void initWebSettings() {
WebSettings settings = webView.getSettings();
//如果要与加载页面中的JavaScript交互,必须设置;
settings.setJavaScriptEnabled(false);
}else {
settings.setJavaScriptEnabled(true);
}
//关闭密码保存提醒
settings.setSavePassword(false);
//设置自适应屏幕,将图片调整到适合webView的大小,缩放至屏幕的大小
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
//特别注意:5.1以上禁止https和http混用,以下是开启,一般是用于测试环境是http生产是https的场景。如果要都支持就得开启
if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.LOLLIPOP){
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
}
*1.2 WebChromeClient辅助WebView处理Javascript对话框,*感觉是浏览器级别的相关方法
onProgressChanged、onJsAlert、onJsPrompt、onJsConfirm
*1.3 WebViewClient处理各种通知,事件请求,是webview组件加载页面的相关回调属于加载请求相关的*
shouldOverrideUrlLoading 用于拦截URL请求,可对制定url进行处理。比如网页内在跳转其他地址这里可以拦截并打印****或者重定向再次请求加载。
onPageStarted 页面开始加载时调用
onPageFinished 页面加载结束时调用
onReceivedError 这个方法有两个,都是页面加载出错时调用;在某些情况下方法二会调用上面一的重载方法,详见源码
onReceivedSslError SSL证书加载出错时调用,应急情况可以选择绕过证书
onScaleChanged 页面缩放
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri url = request.getUrl();
Log.e(TAG, "shouldOverrideUrlLoading: url= "+url+" Method="+request.getMethod()+" Headers="+request.getRequestHeaders() );
// return super.shouldOverrideUrlLoading(view, request);
return false;
}
/**
* 页面开始加载时
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.e(TAG, "onPageStarted: url="+url );
}
/**
* 页面加载结束时调用
* @param view
* @param url
*/
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.e(TAG, "onPageFinished: url="+url );
}
/**
* 页面加载出错-1
*/
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
switch (errorCode){
case 404:
//todo 加载错误页面
break;
}
}
/**
* 页面加载出错时-2
* @param view
* @param request
* @param error
*/
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
Log.e(TAG, "onReceivedError: ErrorCode="+error.getErrorCode()+" Description="+error.getDescription() );
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
Log.e(TAG, "onReceivedSslError: " );
handler.proceed();
// 等待证书响应
// handler.cancel();
// 挂起连接,默认方式
// handler.handleMessage(null);
// 可做其他处理
}
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale);
Log.e(TAG, "onScaleChanged: " );
}
这里说一下上面几个方法的调用顺序:
onPageStarted是在onProgressChanged****执行之后才会执行;
onPageFinished是在onProgressChanged加载到100****之后才执行;
onReceivedError如果执行一定是在onPageFinished****之后
还需要注意Android原生调用js需要在onPageFinished回调之后再调用否则不生效,因为B页面还没加载完你喊破嗓子它也不理你.
以下是加载一个网页的结合了WebChromeClient和WebviewClient的完整****运行日志:
2024-02-20 14:16:05.971 24004-24004/com.example.testdemo3 E/com.example.testdemo3.webview.MyWebChromeClient: onProgressChanged: newProgress=10
* 2024-02-20 14:16:05.991 24004-24004/com.example.testdemo3 E/com.example.testdemo3.webview.MyWebviewClient: onPageStarted: url=https://mp.weixin.qq.com/
* 2024-02-20 14:16:06.093 24004-24004/com.example.testdemo3 E/com.example.testdemo3.webview.MyWebChromeClient: onProgressChanged: newProgress=70
* 2024-02-20 14:16:06.247 24004-24004/com.example.testdemo3 E/com.example.testdemo3.webview.MyWebChromeClient: onProgressChanged: newProgress=100
* 2024-02-20 14:16:06.247 24004-24004/com.example.testdemo3 E/com.example.testdemo3.webview.MyWebChromeClient: onProgressChanged: newProgress=100
* 2024-02-20 14:16:06.247 24004-24004/com.example.testdemo3 E/com.example.testdemo3.webview.MyWebviewClient: onPageFinished: url=https://mp.weixin.qq.com/
* 2024-02-20 14:16:06.300 24004-24004/com.example.testdemo3 E/com.example.testdemo3.webview.MyWebviewClient: onReceivedError: ErrorCode=-10 Description=net::ERR_UNKNOWN_URL_SCHEME
Android使用webView加载网页
//加载网页-加载方式一,加载互联网地址
webView.loadUrl(URL);
//加载网页-加载方式二:apk内本地html文件 我assets文件中放了一个javascriptTest.html文件,文章末尾贴出了代码
webView.loadUrl("file:///android_asset/javascriptTest.html");//加载本地文件
//加载网页-加载方式三:手机内存本地html文件
// webView.loadUrl("content://com.android.htmlfileprovider/sdcard/javascriptTest.html");
//加载网页-加载方式三:加载html中某一段代码
webView.loadData(String data, String mimeType, String encoding);
2、Android原生代码调用html
//调用一:C调用B方法并传值, 如果C调用B还想拿到B的返回值可以再执行一次B调C,因为loadUrl是一个无返回值的方法。success方法内传参也可以是一个json,B端用JSON.stringify(value)接收的
webView.loadUrl("javascript:success('真的成功了')");
//C调B-调用二: 必须这样调用少一个单引号或双引号都不行,有点除了效率高之外还可以onReceiveValue回调方法接收C端的返回值
webView.evaluateJavascript("success('" + "就是成功了" + "')", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//这个value返回的是你调用的方法,中return的返回内容,如果无return默认返回都是null。我这里success方法会的是 "B给C的,接住咯"
Log.e(TAG, "onReceiveValue: value= "+value );
// 2024-05-07 10:38:22.254 28435-28435/com.example.testdemo3 E/com.example.testdemo3.activity.WebViewActivity: onReceiveValue: value= "B给C的,接住咯"
}
});
3、Html调用Android原生代码
*** B调C方法:
*** 第一:addJavascriptInterface,但是4.2有漏洞,4.2以上官方推荐
*** 第二:WebviewClient的shouldOverrideUrlLoading,需要约定号url地址,C端拦截后做出不同操作
*** 第三:WebChromeClient的onJsAlert、onJsConfirm、onJsPrompt,和上一个类似都需要先约定再从C端拦截,4.2之前用的比较多
第一种需要Android设置:
//这里设置多个别名,B页面可以使用任意一个来调用C端方法
webView.addJavascriptInterface(new JsBridge(),"Native");//4.2之前有漏洞,4.2以后官方推荐,引入了**@JavascriptInterface注解
webView.addJavascriptInterface(new JsBridge(),"B2CTest");//4.2之前有漏洞,4.2以后官方推荐,引入了****@JavascriptInterface**注解
**//如果要与加载页面中的JavaScript交互,必须设置;不设置的话加载上面一片空白或者页面报错了,也没法和assets中的html里的script代码交互
**//对于不想加载的地址可以在这里做限制。比如可以根据协议禁止file**协议加载JavaScript,避免域控制不严格的漏洞**
if (URL.startsWith("file://")){
settings.setJavaScriptEnabled(false);
}else {
//如果与html交互这里必须为true
settings.setJavaScriptEnabled(true);
}
4、Android原生使用@JavascriptInterface注解定义的供B断html调用的方法
public class JsBridge {
public static final String TAG = "JsBridge";
@JavascriptInterface
public void allowScreenShot(String value){
Log.e(TAG, "allowScreenShot: value= "+value );
Toast.makeText(BaseApplication.getContext(),"C端接收到"+value,Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public int takePhoto(int key,String status, int value){
Log.e(TAG, "takePhoto: key= "+key+" status="+status+" value= "+value);
return key+value;
}
}
5、assets文件夹中javascriptTest.html文件内容
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson</title>
<script>
function callAndroid(){
// var res = test.clientLog("js调用了android中的hello方法");
//Native.faceDetect("s","fail")
//Native.clientLog(res);
//Native.saveData("test", "123");
//var res = Native.loadData("test");
//var res = Native.notifyLoginStatus(true,1)
//alert(res)
//var res = Native.encrypt("123");
//alert(res);
//Native.previewPdf("http://122.29.205.94:8080/afs/appDownLoad/test.pdf")
//Native.previewWord("http://122.29.205.94:8080/afs/appDownLoad/text.docx")
//B调用C端方法并传值
var value = Native.takePhoto(2,"fail",3)
alert("C端返回"+value);
//Native.downloadPicture("http://122.29.205.94:8080/afs/appDownLoad/pic2.png","s","fail");
//Native.allowScreenShot("0");
}
function fail(){
alert("失败了")
}
function success(args){
alert("成功了-"+JSON.stringify(args))
return "B给C的,接住咯"
}
function callJs(arg){return arg+1}
function callAndroid2(){
//C端设置的两个别名都可以滴调用C端方法
Native.allowScreenShot("1");
B2CTest.allowScreenShot("1-1");
}
</script>
</head>
<body>
<button id="button1" style="font-size:60px;" onclick="callAndroid()" type="button">点击调用</button>
<button class="button" id="button2" onclick="callAndroid2()" type="button">点击调用2</button>
</body>
<style>
.button {
font-size:60px;
margin-left:50px;
}
</style>
</html>
javascriptTest.html文件有很多方法我都是调一个就注释了换下一个方法测试其他场景,辛苦您也动态的去换和调试。
才疏学浅,如有错误,欢迎指正,多谢。
版权归原作者 老梁学Android&HarmonyOS 所有, 如有侵权,请联系我们删除。