展开说说:Android之WebView详解

老梁学Android&HarmonyOS 2024-08-25 11:03:02 阅读 68

WebView是基于webkit引擎展现web页面的控件。不同系统版本使用webkit版本不同,4.4后直接使用了chrome

作用:显示和渲染web页面加载url;直接使用html文件(网络上或assests中)做布局;可以和javaScript交互调用。

一个特殊的view除了具备一般view的属性,还可以对url请求、页面加载、渲染、Android与html页面交互进行强大处理。

1、WebView三大件

1.1 WebSettings提供常用的设置WebView的属性和状态的方法

<code>@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: " );code>

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页面还没加载完你喊破嗓子也不理你.

以下是加载一个网页的结合了WebChromeClientWebviewClient的完整运行日志:

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原生代码

* BC方法:

* 第一:addJavascriptInterface,但是4.2有漏洞,4.2以上官方推荐

* 第二:WebviewClientshouldOverrideUrlLoading,需要约定号url地址,C端拦截后做出不同操作

* 第三:WebChromeClientonJsAlertonJsConfirmonJsPrompt,和上一个类似都需要先约定再从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);code>

return key+value;

}

}

5、assets文件夹中javascriptTest.html文件内容

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">code>

<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>code>

<button class="button" id="button2" onclick="callAndroid2()" type="button">点击调用2</button>code>

</body>

<style>

.button {

font-size:60px;

margin-left:50px;

}

</style>

</html>

javascriptTest.html文件有很多方法我都是调一个就注释了换下一个方法测试其他场景,辛苦您也动态的去换和调试。

才疏学浅,如有错误,欢迎指正,多谢。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。