js&native通信方案

先有问题再有答案

  1. js与native通信有几种方式
  2. 几种方式的本质区别是什么
  3. 如何在合适的场景中选择通信方式

URL Scheme

JavaScript可以通过修改WebView的location(使用特定的URL scheme)来发起请求,Native层通过监听这些请求并解析URL的scheme、host和path,然后执行对应的本地功能。在执行完毕后,可以通过回调URL或执行WebView的JavaScript来将结果传递回JS层。

js侧调用:

ini 复制代码
location.href = "js-to-native://getData";

native拦截:

typescript 复制代码
myWebView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("js-to-native")) {
            // 解析参数 todo
            return true;
        }
        return false;
    }
}

优点

  1. 简单易用:URL Scheme是一种简单直观的通信方式,不需要复杂的设置或额外的库;只需要让Web内容加载特定格式的URL即可触发。
  2. 兼容性强:大多数移动设备上的WebView组件都支持URL Scheme,这意味着即使在旧版本的操作系统上也通常可以使用。
  3. 跨平台:既可以用于Android,也可以用于iOS的WebView,使其成为一个通用的解决方案。

缺点

  1. 单向通信 :这一通信方法通常是单向的从WebView到Native端
  2. 安全性:如果原生应用没有正确验证URL Scheme的输入,可能会遭到恶意代码的攻击。
  3. 数据大小限制:由于URL长度存在限制,如果需要传递大量数据,可能会受到限制。对于数据传输量较大的情况不太适用。
  4. 复杂数据结构处理不方便:基于URL中作为字符串传递,复杂数据结构需要序列化和反序列化,这会增加解析的复杂度和出错的概率。
  5. 用户体验:触发URL操作可能会导致WebView的地址变化,如果没有妥善处理,可能会对用户体验产生负面影响。
  6. 性能问题:频繁的使用URL Scheme进行通信可能会影响应用的性能,特别是在需要大量通信的场景下。

Native注入全局上下文对象

Native侧可以通过 WebView 向 JS 的上下文注入对象和方法,通过WebView的addJavascriptInterface方法将Java对象映射到JavaScript环境中。映射后,JavaScript可以直接调用Java对象的方法作为JavaScript函数。 native注入模块:

typescript 复制代码
public class WebAppInterface {
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.getSettings().setJavaScriptEnabled(true); // 将接口注入到WebView中 
myWebView.addJavascriptInterface(new WebAppInterface(this), "Android"); 
myWebView.loadUrl("file:///android_asset/test.html");

js调用Native注入的全局方法:

scss 复制代码
function showAndroidToast(toast) {
     // 调用Android端的方法
     Android.showToast(toast);
}

优点

  1. 直接调用:通过注入对象,JavaScript 可以直接调用 Native 代码中的方法,实现简单且直观。
  2. 交互设计:允许基于用户交互事件直接触发 Native 功能,为开发者提供了更多控制权。
  3. 适用于不同数据类型:可以处理字符串、数字、布尔值等各种简单数据类型的交互。

缺点

  1. 安全风险:在 Android 4.2 以下版本,addJavascriptInterface 存在安全隐患,恶意网页可能利用反射攻击。在高版本中通过 @JavascriptInterface 注解提高了安全性。
  2. 调用限制:注入的方法必须执行在 UI 线程中,这可能会阻塞 UI 界面,从而影响用户体验。
  3. 无法直接返回值:Native 方法不能直接返回数据给 JavaScript 调用处,必须通过其他方式回传数据,如通过 evaluateJavascript 方法。
  4. 对象保活问题:一旦 Java 对象被注入到 WebView 中,它会持续保活直到 WebView 销毁,需要注意对象生命周期的管理。

Native直接调用js全局方法

Native可以通过evaluateJavascript|| loadUrl(低版本不推荐)直接执行js代码

native代码如下:

typescript 复制代码
// Java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    webView.evaluateJavascript("window.postMessage('Hello H5 OnClick', '*');", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            // 这里可以得到 JavaScript 代码执行的结果
            Log.d("JavaScript Output", value);
        }
    });
} else {
    // 对于低于 API 19 (Android 4.4 KitKat) 的版本,需要使用旧的 loadUrl 方法
    webView.loadUrl("javascript:window.postMessage('Hello H5 OnClick', '*');");
}

js侧可以通过如下方式:

javascript 复制代码
// JavaScript
window.addEventListener('message', (event) => {
    console.log('Received message from App:', event.data);
});

注意native只能调用window上的全局方法
或者 直接写死一个js字符串

例如下面的例子:

typescript 复制代码
mWebView.evaluateJavascript("(function() { return null; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints the string 'null' NOT Java null
    }
});

mWebView.evaluateJavascript("(function() { })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); //s is Java null
    }
});

mWebView.evaluateJavascript("(function() { return ''; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints "" (Two double quotes)
    }
});

优点

  1. 异步处理:evaluateJavascript支持异步执行JavaScript代码,避免了阻塞UI线程,从而提高了应用性能。
  2. 回调结果:你可以获取JavaScript代码执行后的返回值,这为Native与JavaScript双向通信提供了更多的交互可能性。
  3. 安全执行:与直接执行URL Scheme(webView.loadUrl("javascript:...");)相比,evaluateJavascript方法防止了注入风险,更安全。
  4. 无需页面刷新:执行JavaScript代码时页面不会像loadUrl那样进行刷新,用户体验更好。

缺点

  1. 结果处理:结果以String返回,需要处理,特别是对于复杂数据结构的解析可能稍显繁复。
  2. 性能考虑:尽管不会阻塞UI线程,但执行大量的JavaScript或处理复杂的JavaScript任务还是可能对性能产生影响。
  3. 同步问题:由于JavaScript的执行是异步的,因此无法立即获取结果,需要在回调中处理执行结果。

evaluateJavascript 比 loadUrl好在哪里?:

  1. 异步捕获结果:它允许你利用 ValueCallback 接口的实现异步获取执行结果。
  2. 避免转义错误:与loadUrl中的javascript: URI 的字符串相比,evaluateJavascript 让你不必担心转义问题,更加安全。
  3. 更好的性能:evaluateJavascript 可以提供比使用 loadUrl 更好的性能,因为它不会重新加载页面或影响加载状态。

综上所述,推荐使用 evaluateJavascript 方法作为在 Android WebView 中执行 JavaScript 代码的首选方式。这种方法在 API 19 (Android 4.4 KitKat) 及以上版本上可用,并且是Android官方推荐的方式。现在应该不必考虑loadUrl了

bridge

JSBridge作为一个抽象概念,是建立在前面提到的几种通信技术(如URL Scheme、Native主动注入JavaScript对象、evaluateJavascript方法等)之上的。

JSBridge通常是一个更高级别的框架或库,它包装了这些基础通信技术,并提供了一个简单、统一和方便的接口供Web开发者使用。以解决跨平台兼容性、简化API使用、提高安全性、统一消息格式等方面的问题。

总结

对于不同的通信方式各有优缺点,开发者需要根据实际需求和目标平台的能力来选择合适的通信方案。在使用这些通信方式时,需要注意安全性问题,比如防范JavaScript注入攻击,保护应用免受恶意代码侵害。同时,还得考虑数据传输的效率和可靠性,确保良好的用户体验。

相关推荐
前端李易安2 小时前
Web常见的攻击方式及防御方法
前端
PythonFun2 小时前
Python技巧:如何避免数据输入类型错误
前端·python
Neituijunsir2 小时前
2024.09.22 校招 实习 内推 面经
大数据·人工智能·算法·面试·自动驾驶·汽车·求职招聘
hakesashou2 小时前
python交互式命令时如何清除
java·前端·python
天涯学馆2 小时前
Next.js与NextAuth:身份验证实践
前端·javascript·next.js
HEX9CF2 小时前
【CTF Web】Pikachu xss之href输出 Writeup(GET请求+反射型XSS+javascript:伪协议绕过)
开发语言·前端·javascript·安全·网络安全·ecmascript·xss
ConardLi2 小时前
Chrome:新的滚动捕捉事件助你实现更丝滑的动画效果!
前端·javascript·浏览器
ConardLi3 小时前
安全赋值运算符,新的 JavaScript 提案让你告别 trycatch !
前端·javascript
凌云行者3 小时前
使用rust写一个Web服务器——单线程版本
服务器·前端·rust
华农第一蒟蒻3 小时前
Java中JWT(JSON Web Token)的运用
java·前端·spring boot·json·token