WebView的使用之Android与JS通过WebView互调方法

一、概述:
Android与JS通过WebView实现交互,实际上是:

  • Android调用JS的代码;
  • JS调用Android的代码;

二者互调的纽带就是WebView。
Android调用JS代码的方法有以下几种:

  • 通过WebView的loadUrl();
  • 通过WebView的evaluateJavascript();

JS调用Android代码的方法要多点,有以下3种:

  • 通过WebView的 addJavascriptInterface() 进行对象映射;
  • 通过WebViewClient的 shouldOverrideUrlLoading () 方法回调拦截url;
  • 通过WebChromeClient的 onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt()消息.

二、Android通过WebView调用 JS 方法

1、通过WebView的loadUrl()使用详情:
Android中激发点击事件,即调用WebView JS(文本名为webjs)中的方法;

——为了示例方便及响应速度,这里采用Andorid调用本地JS代码进行解释。实际开发中,Andorid极可能调用本地Html文件中的JS代码,也可能调用网络加载的Html页面中的JS代码。

步骤:
A、将需要调用的JS代码的HTML文件放到src/main/assets文件夹里:
这里写图片描述
webjs.html文件中代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <h4>JS与Android(Java)的交互</h4>
    <input type="button" value="js调Android" onclick="ok()">
    <script type="text/javascript">
    function ok() {
        android.webMessage("JS与Android(Java)的交互");
    }
    function javaCallJavascriptNoParam() {
         alert("Android中的Java通过WebView调用了javaScript的无参构造");
         document.getElementById("javacalljs").innerHTML = "Android中的Java通过WebView调用了javaScript的无参构造<br\>";
    }
    function javaCallJavascript(param) {
         alert(param);
         document.getElementById("javacalljs").innerHTML = param;
    }
    </script>
</head>
<body>
<div id="javacalljs"></div>
    <br/><br/>
</body>
</html>

B、在Activity中设置WebView的常用属性:

1)、让网页显示在WebView中而不是用浏览器打开

mWeb.setWebViewClient(new WebViewClient());

2)、设置启动概述模式浏览界面、使用宽屏的视图窗口。

WebSettings setting = mWeb.getSettings();
setting.setUseWideViewPort(true);
setting.setLoadWithOverviewMode(true);

3)、允许调用Js的权限:

setting.setJavaScriptEnabled(true);
setting.setJavaScriptCanOpenWindowsAutomatically(true);

B、在Activity中加载App内置的html文件:

String URL = "file:///android_asset/webjs.html";
mWeb.loadUrl(URL);

C、在Activity的Java代码中调用html文件中的javascript代码:

@OnClick(R.id.tvLoadWeb)
public void onViewClicked() {
	mWeb.loadUrl("javascript:javaCallJavascript('Android中的Java通过WebView调用了javaScript的有参构造方法')");
}

点击按钮,弹出的弹框效果如下:
这里写图片描述
这里要注意了!注意了!!注意了!!!重要的事情说三遍:

1、javascript中没有Java里面的多态和重载的概念,也就是说不能出现相同名字的方法,哪怕是后面的参数不一样(最鲜明的就是有参和无参)也不行。就如上面html文件里面的方法function javaCallJavascriptNoParam() {}和function javaCallJavascript(param) {},如果我们都用同一个方法名,有时候调用就会报错:Undefined;

2、在WebView调用javascript方法的loadUrl方法里面:

mWeb.loadUrl("javascript:javaCallJavascript('Android中的Java通过WebView调用了javaScript的有参构造方法')");  

中,要以"javascript"开头,有部分博客上面写的是以html的文件名开头,如本例中html的文件名为:“webjs”,他们就写成:

mWeb.loadUrl("webjs:javaCallJavascript(Android中的Java通过WebView调用了javaScript的有参构造方法')");

博主表示这样写也调用成功,但是我在学习的时候没有一次是调用成功的,不知道是我没学到家还是怎么的,总之调用起来各种不顺利。但可以确定的一点是:用前一种方法是肯定可以成功调用的。
3、有参无参其实都一样,就传不传参数而已,这个不多说。

三、JS通过WebView调用Java的方法
调用步骤:

1、创建html文件并放在src/main/assets文件夹下:
这里写图片描述
webjs.html文件代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <h4>JS与Android(Java)的交互</h4>
    <input type="button" value="js调Android" onclick="ok()">
    <script type="text/javascript">
        function javaCallJsNoArgs() {
            document.getElementById("content1").innerHTML += "js调用java,Java再回调js的无参方法<br\>"
        }
        function javaCallJsExistArgs(args) {
            document.getElementById("content2").innerHTML += "js调用java,Java再回调js的无参方法,参数是:" + args + "<br\>";
        }
    </script>
</head>
<body>
<div id="javacalljs"></div>
<br/><br/>
        JS与Android(Java)的交互<br/><br/><br/>
    <input type="button" value="JS调Java无参方法方法然后再回调"
           onclick="js_call_java.javaCallJsMethod1()"/><br/>
        <div id="content1"></div>
    <br/><br/>
    <input type="button" value="JS调Java有参方法方法然后再回调"
           onclick="js_call_java.javaCallJsMethod2()"/><br/>
        <div id="content2"></div>
</body>
</html>

2、在Activity的xml文件中布局WebView控件,并设置对应的Java和Js互调的属性:

WebSettings setting = mWvJsCallJava.getSettings();
setting.setJavaScriptEnabled(true);
/下面单个属性的作用前面已经介绍过了,不再重复。
setting.setJavaScriptCanOpenWindowsAutomatically(true);
setting.setUseWideViewPort(true);
setting.setLoadWithOverviewMode(true);
//设置编码格式
setting.setDefaultTextEncodingName("utf-8");
WebViewUtils.webViewSetting(setting, true, false, getApplicationContext());
WebViewUtils.setViewClient(mWvJsCallJava);
WebViewUtils.setChromeClien(mWvJsCallJava, getApplicationContext());
//添加一个对象,让js对象可以访问该对象的方法
mWvJsCallJava.addJavascriptInterface(new Javascript(), "js_call_java");

上面的Javascript.class文件代码如下:

final class Javascript{
        public Javascript(){

        }
        @JavascriptInterface
        public void javaCallJsMethod1() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mWvJsCallJava.loadUrl("javascript:javaCallJsNoArgs()");
                }
            });
        }
        @JavascriptInterface
        public void javaCallJsMethod2() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mWvJsCallJava.loadUrl("javascript:javaCallJsExistArgs('我是皓月')");
                }
            });
        }
    }

addJavascriptInterface()方法里面参数"js_call_java"的作用:
我的理解是:
他是WebView与JS交互时的一个标识符,点击WebView中的按钮,按钮就会通过"js_call_java"标识符调用Java代码中标识符后面的方法。比如:

<input type="button" value="JS调Java有参方法方法然后再回调"
           onclick="js_call_java.javaCallJsMethod2()"/>

点击这个button后,系统就会通过WebView根据标识符"js_call_java"去调用他后面对应的方法。这里,标识符"js_call_java"后面的方法是:javaCallJsMethod2(),那么他就会调用Javascript对象里面的javaCallJsMethod2()方法。
上面WebViewUtils.class文件的代码在文末贴出。

3、加载html文件

String URL = "file:///android_asset/webjs.html";
mWvJsCallJava.loadUrl(URL);

这样就完成了JS对Java代码的调用。我们还可以在Java代码里面继续调用js方法,就像上面那样:

mWvJsCallJava.loadUrl("javascript:javaCallJsExistArgs('我是皓月')");

JS调用Java代码的效果如下:
这里写图片描述

这里Java代码调用了js代码里面的javaCallJsExistArgs()有参方法。另一个也是如此。
至此,JS与Java的交互告一段落。

WebViewUtils.class文件的代码如下:

public class WebViewUtils {
    public static void webViewSetting(WebSettings setting, boolean setTure, boolean setFalse, Context context) {
        /
        /**是否允许数据库存储,需要读写权限,默认false**/
        /**查看setDatabasePath API 如何正确设置数据库存储。
         * 该设置拥有全局特性,同一进程所有WebView实例共用同一配置。注意:保证在同一进程的任一WebView
         * 加载页面之前修改该属性,因为在这之后设置WebView可能会忽略该配置 **/
        setting.setDatabaseEnabled(setTure);
        /
        /**是否设置缓存,必需设置有效的缓存路径才能生效,默认值 false**/
        setting.setAppCacheEnabled(true);
        setting.setAppCachePath(context.getCacheDir().getAbsolutePath());
        /
        /**是否允许定位,默认true。
         * 注意:为了保证定位可以使用,要保证以下几点:
         * 1、Application需要有android.Manifest.permission#ACCESS_COARSE_LOCATION定位权限
         * 2、Application需要实现WebChromeClient#onGeolocationPermissionsShowPrompt的监听回调,
         *    接收Js定位请求访问地理位置的通知 **/
        setting.setGeolocationEnabled(setTure);
        /
        /**是否保存表单数据,默认false**/
        setting.setSaveFormData(setTure);
        /
        /**是否启动概述模式浏览界面,当页面宽度超过WebView显示宽度时,缩小页面适应WebView。默认false
         * 需要和setUseWideViewPort()配合使用才生效**/
        setting.setLoadWithOverviewMode(setTure);
        /**是否支持ViewPort的meta tag属性,如果页面有ViewPort meta tag 指定的宽度,
         * 则使用meta tag指定的值,否则默认使用宽屏的视图窗口(横竖屏时的宽度)**/
        setting.setUseWideViewPort(setTure);
        /
        /**通知WebView是否需要设置一个节点获取焦点,当WebView#requestFocus(int,android.graphics.Rect)
         * 被调用的时候,默认true **/
        setting.setNeedInitialFocus(setTure);
        /
        /**布局算法**/
        setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
        /
        /**设置是否(支持)允许执行JS方法**/
        setting.setJavaScriptEnabled(setTure);
        /
        /**是否支持多窗口,如果设置为true ,WebChromeClient的onCreateWindow方法必须被主程序实现,默认false**/
        setting.setSupportMultipleWindows(setTure);
        /
        /**是否允许JS自动打开窗口。默认false **/
        setting.setJavaScriptCanOpenWindowsAutomatically(setFalse);
        /
        /**是否允许获取WebView的内容URL,可以让WebView访问ContentPrivider存储的内容.默认true**/
        setting.setAllowContentAccess(setTure);
        /**是否允许访问WebView内部文件,默认true, 不知道什么意思**/
        setting.setAllowFileAccess(setTure);
        /
        /**是否允许通过file url加载的Javascript读取本地文件,默认值 false**/
        setting.setAllowFileAccessFromFileURLs(setTure);
        /
        /**是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false**/
        setting.setAllowUniversalAccessFromFileURLs(setTure);
        /
        资源加载
        /**是否自动加载图片,包括从网络获取的图片、app内置的图片以及从Sdcard获取的图片**/
        setting.setLoadsImagesAutomatically(setTure);
        /**是否自动加载网络图片**/
        setting.setBlockNetworkImage(setTure);
        /**是否加载网络资源**/
        setting.setBlockNetworkLoads(setTure);
        /
        缩放(zoom)
        /**是否支持缩放,配合setBuiltInZoomControls使用,默认true **/
        setting.setSupportZoom(setTure);
        /**是否使用WebView内置的缩放组件,由浮动在窗口上的缩放控制和手势缩放控制组成,默认false**/
        setting.setBuiltInZoomControls(setFalse);
        /**是是否显示内置缩放控件(有点像进度条,左边是“-”,右边是“+”)**/
        setting.setDisplayZoomControls(setFalse);
        /
        默认文本编码,默认值 "UTF-8"
        /**设置页面的编码格式,默认UTF-8 **/
        setting.setDefaultTextEncodingName("utf-8");
        /
        /**设置默认字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。
         * 另外WebView各种字体的大小的设置有一系列的方法:setXxxxFontSize(int size)**/
        setting.setDefaultFontSize(16);
        /**默认等宽字体尺寸,默认值16**/
        setting.setDefaultFixedFontSize(16);
        /**最小文字尺寸,默认值 8**/
        setting.setMinimumFontSize(8);
        /**最小文字逻辑尺寸,默认值 8**/
        setting.setMinimumLogicalFontSize(8);
        /**文字缩放百分比,默认值 100**/
        setting.setTextZoom(100);
        /
        设置字体
        /**是否支持多窗口如果设置为true,WebChromeClient的onCreateWindow方法必须被主程序实现,默认false
         * 另外WebView各种字体的设置有一系列的方法:setXxxxFontFamily(String font)**/
        setting.setStandardFontFamily("sans-serif");
        /**衬线字体,默认值 "serif"**/
        setting.setSerifFontFamily("serif");
        /**无衬线字体,默认值 "sans-serif"**/
        setting.setSansSerifFontFamily("sans-serif");
        /**等宽字体,默认值 "monospace"**/
        setting.setFixedFontFamily("monospace");
        /**手写体(草书),默认值 "cursive"**/
        setting.setCursiveFontFamily("cursive");
        /**幻想体,默认值 "fantasy"**/
        setting.setFantasyFontFamily("fantasy");
        /
        /**存储(storage),启用HTML5 DOM storage API,默认值 false*/
        setting.setDomStorageEnabled(true);
        /
        /**是否需要用户手势来播放Media,默认true**/
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            setting.setMediaPlaybackRequiresUserGesture(setTure);
        }
        /
        /**设置加载不安全资源的WebView加载行为。KITKAT版本以及以下默认为MIXED_CONTENT_ALWAYS_ALLOW
         * 方式,LOLLIPOP默认MIXED_CONTENT_NEVER_ALLOW。强烈建议:使用MIXED_CONTENT_NEVER_ALLOW **/
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        }
        /
        /**是否在离开屏幕时光栅化(会增加内存消耗),默认值 false**/
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            setting.setOffscreenPreRaster(false);
        }
        /
        /**根据cache-control决定是否从网络上取数据
         * LOAD_DEFAULT 默认加载方式
         * LOAD_CACHE_ELSE_NETWORK 按网络情况使用缓存
         * LOAD_NO_CACHE 不使用缓存
         * LOAD_CACHE_ONLY 只使用缓存 **/
        if (isNetworkAvailable(context)) {
            //有网络,从网络获取。
            setting.setCacheMode(WebSettings.LOAD_DEFAULT);
        } else {
            // 没网,离线加载缓存(即使已经过期)
            setting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        }
    }

    public static void setViewClient(WebView mWeb) {
        /*****作用:让HTML网页显示在显示在WebView中而不是用浏览器打开**/
        mWeb.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                //开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。
                super.onPageStarted(view, url, favicon);
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                //在页面加载结束时调用。我们可以关闭loading 条,切换程序动作。
                super.onPageFinished(view, url);
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                //使得打开网页时不调用系统浏览器, 而是在本WebView中显示
                view.loadUrl(url);
                return super.shouldOverrideUrlLoading(view, url);
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                return super.shouldOverrideUrlLoading(view, request);
            }

            @Override
            public void onLoadResource(WebView view, String url) {
                //在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
                super.onLoadResource(view, url);
            }

            @Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                //加载页面的服务器出现错误时(如404)调用。
                switch (errorCode) {
                    //HttpStatus.SC_NOT_FOUND
                    case 404:
                        break;
                    default:
                        break;
                }
                super.onReceivedError(view, errorCode, description, failingUrl);
            }

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                //专用于https请求
                super.onReceivedSslError(view, handler, error);
            }
        });
    }

    public static void setChromeClien(WebView mWeb, final Context context) {
        mWeb.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                //网页的加载进度:newProgress即是加载进度百分比:0<=newProgress<=100;
                super.onProgressChanged(view, newProgress);
            }

            @Override
            public void onReceivedTitle(WebView view, String title) {
                //要加载的网页的标题,比如http://www.baidu.com的标题:百度;http://www.ifeng.com的标题:凤凰网。
                super.onReceivedTitle(view, title);
            }

            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                //允许弹出javascript的警告框,message就是警告框的内容。
                return super.onJsAlert(view, url, message, result);
            }

            @Override
            public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
                //允许弹出javascript的确认框,message就是确认信息。
                new AlertDialog.Builder(context)
                        .setTitle("信息确认")
                        .setMessage(message)
                        .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                result.confirm();
                            }
                        })
                        .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                result.cancel();
                            }
                        })
                        .setCancelable(false)
                        .show();
                // 返回布尔值:判断点击时确认还是取消
                // true表示点击了确认;false表示点击了取消;
                return super.onJsConfirm(view, url, message, result);
            }

            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                //允许弹出javascript输入框
                EditText et = new EditText(context);
                AlertDialog.Builder dialog = new AlertDialog.Builder(context);
                dialog.setTitle("输入信息").setView(et)
                        .setPositiveButton("Sure", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //返回输入框中的值,
                            }
                        })
                        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //返回null。
                            }
                        }).setCancelable(false).show();
                return super.onJsPrompt(view, url, message, defaultValue, result);
            }
        });
    }

    public static void clearCache(WebView mWeb) {
        //清除网页访问留下的缓存,这个方法是针对整个应用程序.
        mWeb.clearCache(true);
        //清除当前webview访问的历史所有记录
        mWeb.clearHistory();
        //这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
        mWeb.clearFormData();
    }

    public static boolean isNetworkAvailable(Context context) {
        // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理)
        try {
            ConnectivityManager connectivity = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            if (connectivity != null) {
                // 获取网络连接管理的对象
                NetworkInfo info = connectivity.getActiveNetworkInfo();
                if (info != null && info.isConnected()) {
                    // 判断当前网络是否已经连接
                    if (info.getState() == NetworkInfo.State.CONNECTED) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static Bitmap getLocalBitmap(String URL) {
        try {
            FileInputStream input = new FileInputStream(URL);
            Log.i("TAG", "onViewClicked: input is Empty?:" + (input == null));
            return BitmapFactory.decodeStream(input);
        } catch (Exception e) {
            Log.i("TAG", "onViewClicked: 抛异常:" + e);
            return null;
        }
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值