展开说说:Android之WebView详解

WebView是基于 webkit 引擎展现 web 页面的控件。不同系统版本使用 webkit 版本不同, 4.4 后直接使用了 chrome
作用:显示和渲染 web 页面加载 url ;直接使用 html 文件(网络上或 assests 中)做布局;可以和 javaScript 交互调用。
一个 特殊的view 除了具备一般 view 的属性,还可以对 url 请求、页面加载、渲染、Android与html页面交互进行强大处理。

1、 WebView 三大件

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

java 复制代码
@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 页面缩放

java 复制代码
   @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 的完整 运行日志:

java 复制代码
 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加载网页

java 复制代码
//加载网页-加载方式一,加载互联网地址
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

java 复制代码
//调用一: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调用的方法

java 复制代码
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 文件内容

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文件有很多方法我都是调一个就注释了换下一个方法测试其他场景,辛苦您也动态的去换和调试。

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

相关推荐
松仔log11 小时前
Java中级——组合和继承
android·java·开发语言
我命由我1234514 小时前
Jetpack Compose - MaterialExpressiveTheme 与 MaterialTheme、ColorScheme
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime
lilian23315 小时前
Harmony os 技术实战|拼豆制图06:收藏 ID、生成记录与重启恢复怎么不打架
android·java·数据库·harmonyos
2601_9557596215 小时前
ClaudeAPI成本中心与业务标签设计指南
android·java·数据库
余衫马15 小时前
2026最新版!Android Studio 极速安装与配置指南
android·ide·android studio
AFinalStone15 小时前
Android 7系统异常问题排查(五)Framework层(下)—System Server崩溃
android·tombstone·系统异常
AFinalStone15 小时前
Android 7系统异常问题排查(八)系统追踪—Trace机制与性能诊断
android·系统异常
极客猴子19 小时前
Android录音转写频繁卡顿?多款APP长时间会议场景稳定性实测
android·人工智能·智能手机·飞书
Kapaseker19 小时前
Boolean 变量到底该怎么命名?
android·kotlin
AFinalStone19 小时前
Android 7系统异常问题排查(六)应用异常(上)—ANR机制全解
android·系统异常