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

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

相关推荐
哲科软件4 小时前
跨平台开发的抉择:Flutter vs 原生安卓(Kotlin)的优劣对比与选型建议
android·flutter·kotlin
jyan_敬言10 小时前
【C++】string类(二)相关接口介绍及其使用
android·开发语言·c++·青少年编程·visual studio
程序员老刘11 小时前
Android 16开发者全解读
android·flutter·客户端
福柯柯11 小时前
Android ContentProvider的使用
android·contenprovider
不想迷路的小男孩11 小时前
Android Studio 中Palette跟Component Tree面板消失怎么恢复正常
android·ide·android studio
餐桌上的王子11 小时前
Android 构建可管理生命周期的应用(一)
android
菠萝加点糖12 小时前
Android Camera2 + OpenGL离屏渲染示例
android·opengl·camera
用户20187928316712 小时前
🌟 童话:四大Context徽章诞生记
android
yzpyzp12 小时前
Android studio在点击运行按钮时执行过程中输出的compileDebugKotlin 这个任务是由gradle执行的吗
android·gradle·android studio
aningxiaoxixi12 小时前
安卓之service
android