yunshuipiao / Potato

Read the fucking source code for the Android interview
Apache License 2.0
80 stars 12 forks source link

Interaction between Native and js #35

Open yunshuipiao opened 5 years ago

yunshuipiao commented 5 years ago

Interaction between Native and js

[TOC]

这篇文章介绍 Android 与 js 的交互。由于目前很少有纯 native 的应用,并且电商类 APP 都含有大量的网页,所以这块知识属于必须掌握的。

交互方式概览

Android 和 js 交互实际上是通过 WebView 互相调用方法:

那么对于 Android 调用 js 的方法有两种:

而对于 js 调用 android 代码的方式有 3 种:

Android 调用 JS

通过 weview 的 loadUrl()

比较常用,用于加载给定的 url 或者本地 网页,或者执行 js 代码。

// 注意调用的JS方法名要对应上
// 调用javascript的callJS()方法
mWebView.loadUrl("javascript:callJS()");

这里需要注意,JS 代码调用一定要在 onPageFinished() 回调之后, 表示页面加载结束。

通过 webview 的 evaluateJavascript() 方法

该方法比第一种方法效率更高,使用更简洁。

  1. 因为该方法的执行不会刷新页面,而第一种方法会刷新页面
  2. Android 4.4 以后才支持
// 只需要将第一种方法的loadUrl()换成下面该方法即可
    mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            //此处为 js 返回的结果
        }
    });
}

JS 调用 Android 代码

通过 webview.addJavascriptInterface() 进行对象映射

  1. 定义一个与 JS 对象映射关系的 Android 类,AndroidToJs。

    // 继承自Object类
    public class AndroidtoJs extends Object {
    
       // 定义JS需要调用的方法
       // 被JS调用的方法必须加入@JavascriptInterface注解
       @JavascriptInterface
       public void hello(String msg) {
           System.out.println("JS调用了Android的hello方法");
       }
    }
  2. 需要调用的 js 代码如下:

    function callAndroid(){
           // 由于对象映射,所以调用test对象等于调用Android映射的对象
           test.hello("js调用了android中的hello方法");
    }
  3. 在 Android 里面添加对应的映射关系

  4. // 设置与Js交互的权限
    webSettings.setJavaScriptEnabled(true);
    
           // 通过addJavascriptInterface()将Java对象映射到JS对象
           //参数1:Javascript对象名
           //参数2:Java对象名
    mWebView.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS类对象映射到js的test对象

优点:使用简单,仅需要 Android 和 JS 对象的映射。

缺点:当 JS 拿到 Android 这个对象后,就可以调用这个 Android 对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。

如可以执行命令获取本地设备的SD卡中的文件等信息从而造成信息泄露

解决方案:

  1. Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击
  2. 在Android 4.2版本之前采用拦截prompt()进行漏洞修复。

通过 webviewClient 的 shouoverrideUrlLoading() 方法回调拦截 url

具体原理是:

  1. Android 通过 WebViewClient 的 回调方法 shouoverrideUrlLoading() 拦截 url
  2. 解析该 url 的协议
  3. 如果检测到的是预先约定好的协议,就调用相应方法。

通过 WebChromeClient 的方法,自定义协议

原理:通过 WebViewClient 的 onJsAlert(), onJsConfirm(), onJsPromot() 方法回调分别拦截对应的 JS 对话框。最常见的拦截 promot 对话框。

  1. 常用的拦截是:拦截 JS的输入框(即prompt()方法)
  2. 因为只有prompt()可以返回任意类型的值,操作最全面方便、更加灵活;而alert()对话框没有返回值;confirm()对话框只能返回两种状态(确定 / 取消)两个值

需要返回值时,通过上述 Android 调用 Js 代码将返回值传会 Js 端。