前言

上一篇文章写到了WebView的常规设置,今天具体来介绍一下WebView中java与js代码的交互问题。
本次参考了两篇文章的内容:

1.WebView详解

2.Android:你要的WebView与 JS 交互方式 都在这里了


Android通过WebView调用 JS 代码

首先来说说通过WebView调用js代码的问题。在WebView中,提供了两个方法来调用js中的代码分别是

1. 通过WebView的loadUrl() 
2. 通过WebView的evaluateJavascript()

这里说明一下,第二个方法是在Android4.4之后加入的,其具有可以获取js方法返回值的功能。下面来说说具体的使用。
首先我们可以定义一段js代码,代码截取上述的“WebView详解”。这里分别定义了两个函数,一个有返回值,一个无返回值。

<script>
 // 有参无返回值
 function funcParam(param){
   alert(param+"");
 }

 // 有参有返回值
 function newFunc(param1,param2,param3){
   alert((param1 + param2) + " " + param3);
   return param3;
 }
</script>

然后我们就可以在java代码中通过webView.loadUrl("Javascript:js方法名()");或webView.evaluateJavascript("Javascript:js方法名()",ValueCallback<T> callback);这两种形式去调用js中的方法。
这里因为设计到两种版本的方法调用,在参考文章中也提到了一种封装方式,这里也顺带提一下:

public static final String JS_FUNC_PREFIX = "javascript:";
public void invokeJs(String jsFunc, final ValueCallback<String> callback) {
  if (!jsFunc.startsWith(JS_FUNC_PREFIX)) {
     jsFunc = JS_FUNC_PREFIX + jsFunc;
  }
 // api 19
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    mWebView.evaluateJavascript(jsFunc, callback);
 } else {
    mWebView.loadUrl(jsFunc);
 }
}

其实现在市场上的Android手机普遍都是5.0版本起步,个人认为这里的适配可以省略。当然也需要根据实际需求为准。

然后在说一下ValueCallback<T>这个接口,它是一个Android Webkit中用于js代码返回值回调的接口,这里的泛型T就是js代码的返回值类型,需要按照实际需求来设置。

然后就是js函数的问题了,这里因为我们的参数是String类型的,在传参方面其实就是直接把它的参数也拼接成字符串传递。所以我们可以使用如下方法进行字符串的拼接。

public static String generateJsFunc(String funcName, Object... params) {
StringBuilder sb = new StringBuilder(funcName).append("(");
for (int i = 0; i < params.length; i++) {
  if (params[i] != null) {
     if (params[i] instanceof String) {
        sb.append("'").append(params[i]).append("'");
     } else {
        sb.append(params[i]);
     }
     if (i != params.length - 1) {
        sb.append(",");
     }
  }
}
sb.append(")");
return sb.toString();}

在调用loadUrl或evaluateJavascript方法之前就可使用上述的方法进行字符串拼接参数。


通过Js调用Android代码

这里可以通过3种方式进行调用

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

    • addJavascriptInterface()进行对象映射
public class JsBridge {

    @JavascriptInterface 
    public void toast(String msg) {
      ToastUtils.show(msg);
    }

    @JavascriptInterface
    public void log(String msg) {
      Log.e(TAG, msg);
    }

  }

如上述代码所示,我们可以通过创建一个自定义类型,在公共方法上加入@JavascriptInterface注解声明其为一个可以被js调用的方法。
然后我们可以通过webView.addJavascriptInterface(obj, name); 这个方法来与js建立连接。这里解释一下,obj就是刚刚所创建的类的对象,name是用于提供给js调用的key值。
当然前提是需要设置webView.getSettings().setJavaScriptEnabled(true);

webView.addJavascriptInterface(new JsBridge(), "android");

在 js 中调用 java 方法时,只需要使用 window 对象:

window.android.log('test js invoke java method');

android就是我们刚刚传递的name参数值,而log就是JsBridge类中的其中一个公共方法。

  • shouldOverrideUrlLoading ()方法回调拦截 url
    在上一篇文章中我们提到了WebViewClient中的shouldOverrideUrlLoading方法,这个其实也是一个用于js和Android交互的工具。下面介绍的是如何通过uri的scheme来调起其他应用,例如支付宝。

如果web中需要通过链接的方式去调起手机上某应用的话就可以使用这个方法。
首先定义一个检测scheme的方法:

 public static final String ALIPAY_SCHEME = "alipays";
 public static boolean shouldOverrideIntentUrl(Context context, String url) {
 Uri uri = Uri.parse(url);
 if (uri != null
     && uri.getScheme() != null
     && uri.getScheme().equals(ALIPAY_SCHEME)) {
  try {
     Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
     intent.addCategory(Intent.CATEGORY_BROWSABLE);
     intent.setComponent(null);
     intent.setSelector(null);
     context.startActivity(intent);
  } catch (Exception e) {
     e.printStackTrace();
     if (e instanceof ActivityNotFoundException) {
        ToastUtil.show("未检测到支付宝客户端");
     }
     return true;
  }
  return true;
}
 return false;
}

该方法先将url转成uri然后获取其scheme判断其是否为支付宝支付标志,若是则使用intent调起支付宝应用。
然后在shouldOverrideUrlLoading进行判断

webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
   // 如果不使用 intent 覆盖加载这个链接,就走原来
   if (!shouldOverrideIntentUrl(getContext(), url)) {
      view.loadUrl(url, Api.makeHttpHeaders(getContext()));
   }
   return true;
 }
});

还有一种情况是点击连接之后下载文件,例如下载一个apk,这种链接的url一般会是http://xxx.apk这种形式的。这时候我们就需要在shouldOverrideUrlLoading判断是否含有这种格式,然后作一个拦截。拦截后可以通过调用webView的DownloadListener做下载处理。这里使用的是使用直接打开浏览器的方式。

webView.setDownloadListener(new DownloadListener() {
  @Override
  public void onDownloadStart(String url, String userAgent,
                       String contentDisposition,
                       String mimetype,
                       long contentLength) {
     //去下载
     Intent intent = new Intent(Intent.ACTION_VIEW);
     String downLoadUrl = url;
     if (!downLoadUrl.contains("http://")) {
        downLoadUrl = "http://" + downLoadUrl;
     }
     intent.setData(Uri.parse(downLoadUrl));
     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     webView.getContext().startActivity(intent);
  }
});
  • WebChromeClient
    然后就到了第3种方式了。这里内置了三种方法分别是onJsAlert()、onJsConfirm()、onJsPrompt()方法。分别对应的是js中的alert()、confirm()、prompt() 弹窗方法。下图是对应这三个方法在js的作用。

944365-1385f748618af886.png

这里首先我们需要设置WebChromeClient,这里用onJsPrompt为例,然后就是重写onJsPrompt方法

webView.setWebChromeClient(new WebChromeClient(){
        @Override
        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, 
        JsPromptResult result) {
             Uri uri = Uri.parse(url);
             if ( uri.getScheme().equals("js")) {
                 if (uri.getAuthority().equals("webview")) {
                     Set<String> collection = uri.getQueryParameterNames();
                     result.confirm("js调用了Android的方法成功啦");
                 }
                 return true;
             }
             return super.onJsPrompt(view, url, message, defaultValue, result);
        }
    });
  1. 参数message:代表promt()的内容,而result代表输入框的返回值。
  2. 这里我们假定url = "js://webview?arg1=111&arg2=222",顺便提一下,scheme(协议格式), authority(协议名)。
  3. 然后通过url转换成uri与上面第2种方式是一样的,分析其scheme、authority的值进行拦截。然后如果都符合的话就进行拦截后的处理。
  4. 还有一点需要注意的是我们可以通过url拼接的方式如上述url所示,将一些参数传递给Android。在java中可以通过uri.getQueryParameterNames();获取这些参数的key值,uri.getQueryParameter(String key)通过key值获取对应的value值。
  5. 最后我们可以调用JsPromptResult的confirm将结果回调到弹窗上显示。

注:

  • WebView 默认是无法弹出 alert() 的,需要设置 WebChromeClient。
  • 需要设置webSettings.setJavaScriptCanOpenWindowsAutomatically(true);来允许js弹窗

标签: Android WebView

添加新评论