JSBridge:混合开发中的双向通信[Android、iOS&JavaScript]

简介: WebView 是移动端中的一个控件,它为 JS 运行提供了一个沙箱环境。WebView 能够加载指定的 url,拦截页面发出的各种请求等各种页面控制功能,JSB 的实现就依赖于 WebView 暴露的各种接口。由于历史原因,IOS以8为分界,Android以4.4为分界,分为高低两个版本。而它们的区别在于 —— 回调。高版本可以通过执行回调拿到 JS 执行完毕的返回值,然后准确进行下一步操作。而低版本无法执行回调!Hybrid App 的核心。

什么是WebView

WebView 是移动端中的一个控件,它为 JS 运行提供了一个沙箱环境。WebView 能够加载指定的 url,拦截页面发出的各种请求等各种页面控制功能,JSB 的实现就依赖于 WebView 暴露的各种接口。

由于历史原因,IOS以8为分界,Android以4.4为分界,分为高低两个版本。而它们的区别在于 —— 回调。高版本可以通过执行回调拿到 JS 执行完毕的返回值,然后准确进行下一步操作。而低版本无法执行回调!

什么是 JSB

Hybrid App 的核心。我们开发的 h5 页面运行在端上的 WebView 容器之中,很多业务场景下 h5 需要依赖端上提供的信息/能力,这时我们需要一个可以连接原生运行环境和 JS 运行环境的桥梁 。 这个桥梁就是 JSB,JSB 让 Web 端和 Native 端得以实现双向通信。

JSB的目的

JSB 的目的就是“让 Native 可以调用 web 端的 JavaScript 代码,让 web 端可以调用 Native 的原生代码”。

native 调用 web 端代码

无论 Android 还是 iOS,在调用 web 端代码的时候,必须是调用的“挂载在window上的函数”。拿一个例子来说:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="调用JS方法"
    android:onClick="onJSFunction1"/>
public void onJSFunction1 (View v) {
    mWebView.evaluateJavascript("javascript:onFunction('android调用JS方法')", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String s) {
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setMessage(s);
            builder.setNegativeButton("确定", null);
            builder.create().show();
        }
    });
}

evaluateJavascript 就是调用 JS 中的方法:onFunction1,并传入参数,在回调中进行处理 —— 这个回调至关重要。

在 js 中:

window.onFunction = function(str) {
    alert(str);
    return "这是 onFunction 方法的返回值";
}

android端调用web端代码

上面的 java 代码中,mWebView 是实例化的 腾讯X5内核组件。在它的配置中首先注意到这样一行代码:

/**
 * 允许加载的网页执行 JavaScript 方法
 */
webSettings.setJavaScriptEnabled(true);

允许网页执行 JS 方法。这个非常重要:它是 Native 是否能够向 web 通信的关键!这一点下面我们会提到。

在进行完一些基础配置后,我们会构建一个 JSBridge 对象:

addJavascriptInterface(
        new MyJaveScriptInterface(mContext, this),
        "AndroidJSBridge");

这个对象就叫做“AndroidJSBridge”,在这个处理中,这个 JSB 对象会被挂载到网页的 window 对象下,从而作为原生端和 web 端通信的桥梁。

web 调用 native 端代码

上面说了在初始化后 window 对象下会有一个 AndroidJSBridge 对象,在网页中也可以直接通过 window.AndroidJSBridge 拿到这个对象,从而调用 Android 端提供给网页端的方法(这里以Android为例):
现在 Android 提供了这样一个方法:

@JavascriptInterface
public void androidTestFunction1 (String str) {
    AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
    builder.setMessage(str);
    builder.setNegativeButton("确定", null);
    builder.create().show();
}

目的是在 APP 弹出一个 Alert 对话框,对话框中的内容为 JavaScript 传入的字符串。

注意:对于 Android 来说,当 js 调用方法并传参时,Android方法接收的参数只能是“基本数据类型”。对于“复杂数据类型”,只能通过 JSON.stringify(Object) 转化成 string 类型。
而 iOS 则不受此限制。

在 web 项目中,拿原生项目来说,我们直接这么写即可:

<input type="button" value="调用androidTestFunction1" @click="toAndroidFunction1()" />

<script>
function toAndroidFunction1() {
    window.AndroidJSBridge.androidTestFunction1('调用 android 下的 function1 方法')
}
</script>

web 端调用 android 方法
当然,在 android 方法中,我们也可以直接 return 数据,到了 web 端就是“回调”了。

function toAndroidFunction2() {
    let result = window.AndroidJSBridge.androidTestFunction1('androidTestFunction2方法的返回值');
    alert(result);
}

web 端调用有返回值的 android方法
诶,为什么调用的 alert 也是弹出的 android 原生弹出框?
因为在初始化的时候,原生对 alert 进行了一次“劫持”:

/**
 * 监听网页中的url加载事件
 */
private void initChromeClient () {
    setWebChromeClient(new WebChromeClient(){

        /**
         * alert()
         * 监听alert弹出框,使用原生弹框代替alert。
         */
        @Override
        public boolean onJsAlert(WebView webView, String s, String s1, JsResult jsResult) {

                AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
                builder.setMessage(s1);
                builder.setNegativeButton("确定", null);
                builder.create().show();
            jsResult.confirm();

            return true;
        }
    });
}

双向通信

Native向web发送消息

其实原理上面已经说了:

Native 向 Web 发送消息基本原理上是在 WebView 容器中动态地执行一段 JS 脚本,通常情况下是调用一个挂载在全局上下文的方法。
具体来说 —— Native 端可以直接调用挂载在 window 上的全局方法并传入相应的函数执行参数,并且在函数执行结束后 Native 端可以直接拿到执行成功的返回值。

场景:页面上是一个web page,在其上有一个原生控件 Button 和 Input。在输入框中输入一段代码更改 web 页面上某个标签的 innerHTML

<EditText
    android:id="@+id/editText_name"
    android:layout_width="edit_parent"
    android:layout_height="edit_content"
    android:hint="请输入你想要执行的js代码" />

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Native向Web发送消息" />
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import cn.sunday.hybridappdemo.views.X5WebView;
 
public class MainActivity extends AppCompatActivity {
    private X5WebView mWebView;
    
    private Button button;
    private EditText editText_name;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.Button_quedin);
        this.editText_name = (EditText) findViewById(R.id.editText_name);
        
        button.setOnClickListener(new View.OnClickListener() {//注册监听
            @Override //监听点击事件
            public void onClick(View v) {
                String name = editText_name.getText().toString();
                mWebView.evaluateJavascript("javascript:onShowPageNative(" + name + ")");
            }
        })
    })
}
window.onShowPageNative = function(str) {
    new Function(str);
}

将文本框输入的字符视为 JS 字符串并调用相关 API 直接执行

你是否想到了著名的用于解决跨域问题的jsonp?其实很多地方都使用了“将代码作为字符串传递再执行”的原理。最出名的就是微信小程序的(双线程)混合架构模型中view层向逻辑层通信。

Web 向 Native 发送消息

Web 向 Native 发送消息本质上就是某段 JS 代码的执行端上是可感知的,目前业界主流的实现方案有两种,分别是拦截式和注入式。

拦截式

和浏览器类似 WebView 中发出的所有请求都是可以被 Native 容器感知到的,因此拦截式具体指的是 Native 拦截 Web 发出的 URL 请求,双方在此之前约定一个 JSB 请求格式,如果该请求是 JSB 则进行相应的处理,若不是则直接转发。

Native 拦截请求的钩子方法:

  • Android:shouldOverrideUrlLoading
  • IOS:

    • 8+:decidePolicyForNavigationAction
    • 8-:shouldStartLoadWithRequest

拦截式的流程存在几个问题:

通过何种方式发出请求?
Web 端发出请求的方式非常多样,例如 <a>iframe.srclocation.href、ajax 等,但 <a> 需要用户手动触发,location.href 可能会导致页面跳转,安卓端拦截 ajax 的能力有所欠缺,因此绝大多数拦截式实现方案均采用 iframe 来发送请求。

如何规定 JSB 的请求格式?
一个标准的 URL 由 <scheme>://<host>:<port><path> 组成,相信大家都有过从微信或手机浏览器点击某个链接意外跳转到其他 App 的经历,如果有仔细留意过这些链接的 URL 你会发现目前主流 App 都有其专属的一个 scheme来作为该应用的标识,例如微信的 URL scheme 就是 weixin://。JSB 的实现借鉴这一思路,定制业务自身专属的一个 URL scheme 来作为 JSB 请求的标识,例如字节内部的 bytedance://

// Web 通过动态创建 iframe,将 src 设置为符合双端规范的 url scheme
const CUSTOM_PROTOCOL_SCHEME = 'vdian'

function web2Native(event) {    
    const messagingIframe = document.createElement('iframe');
    messagingIframe.style.display = 'none';
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + event;
    document.documentElement.appendChild(messagingIframe);

    setTimeout(() => {
        document.documentElement.removeChild(messagingIframe);
    }, 200)
}

拦截式在双端都具有非常好的向下兼容性,曾经是最主流的 JSB 实现方案,但目前在高版本的系统中已经逐渐被淘汰,理由是它有如下几个劣势:

  • 连续发送时可能会造成消息丢失(可以使用消息队列解决该问题)
  • URL 字符串长度有限制
  • 性能一般,URL request 创建请求有一定的耗时(Android 端 200-400ms)

注入式

注入式的原理是通过 WebView 提供的接口向 JS 全局上下文对象(window)中注入对象或者方法,当 JS 调用时,可直接执行相应的 Native 代码逻辑,从而达到 Web 调用 Native 的目的。
—— 也就是上面说的“web 端调用 Native 端代码”。

这种方法简单而直观,并且不存在参数长度限制和性能瓶颈等问题,目前主流的 JSB SDK 都将注入式方案作为优先使用的对象。

相关文章
|
1月前
|
机器学习/深度学习 人工智能 语音技术
探索未来:安卓与iOS的人工智能竞赛
在当今数字化时代,安卓和iOS作为两大主流移动操作系统,已经成为人们日常生活不可或缺的一部分。随着人工智能技术的飞速发展,安卓与iOS之间的竞争日益激烈。本文将深入探讨安卓与iOS在人工智能领域的发展现状和未来趋势,分析两者之间的差异和竞争优势,为读者带来全新的视角和思考。
16 1
|
2月前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
31 1
|
2月前
|
API 开发工具 Android开发
iOS 和 Android 平台的开发有哪些主要区别?
iOS与Android开发区别:iOS用Objective-C/Swift,App Store唯一下载渠道;Android用Java/Kotlin,多商店发布(如Google Play、华为市场)。设计上,iOS简洁一致,Android灵活可定制。开发工具,iOS用Xcode,Android用Android Studio。硬件和系统多样性,iOS统一,Android复杂。权限管理、审核流程及API各有特点,开发者需依据目标平台特性进行选择。
36 3
|
10天前
|
前端开发 Android开发 iOS开发
【Flutter前端技术开发专栏】Flutter在Android与iOS上的性能对比
【4月更文挑战第30天】Flutter 框架实现跨平台移动应用,通过一致的 UI 渲染(Skia 引擎)、热重载功能和响应式框架提高开发效率和用户体验。然而,Android 和 iOS 的系统差异、渲染机制及编译过程影响性能。性能对比显示,iOS 可能因硬件优化提供更流畅体验,而 Android 更具灵活性和广泛硬件支持。开发者可采用代码、资源优化和特定平台优化策略,利用性能分析工具提升应用性能。
【Flutter前端技术开发专栏】Flutter在Android与iOS上的性能对比
|
13天前
|
人工智能 安全 Android开发
【专栏】在人工智能时代,Android和iOS两大移动操作系统巨头正加速融合与创新
【4月更文挑战第27天】在人工智能时代,Android和iOS两大移动操作系统巨头正加速融合与创新。Android以其开放性占据广阔市场,集成AI功能如语音助手;而iOS以其稳定性和生态优势,如Siri,提供卓越体验。两者在AI技术、应用场景上相互借鉴,拓展至医疗、教育等领域,并逐步打通生态系统。然而,技术竞争、数据隐私和标准不一是挑战,新市场需求、技术创新和产业合作则带来机遇。未来,二者将继续推动AI发展,为社会进步贡献力量。
|
11天前
|
缓存 Android开发 iOS开发
打造高效移动应用:Android与iOS性能优化策略
【4月更文挑战第29天】 在移动设备日益成为用户日常互动的主要平台的今天,应用程序的性能已成为决定其成功的关键因素之一。本文将探讨针对Android和iOS平台的性能优化技巧,涵盖内存管理、多线程处理、网络请求优化以及用户界面的流畅性提升等方面。通过分析不同操作系统的架构特点,我们旨在提供一套综合性的策略,帮助开发者构建快速、响应迅捷且用户体验良好的应用。
|
22天前
|
安全 Android开发 iOS开发
探索安卓与iOS操作系统的异同
在当今移动互联网时代,安卓和iOS作为两大主流移动操作系统,扮演着至关重要的角色。本文将深入探讨安卓与iOS操作系统的异同之处,包括界面设计、应用生态、安全性等方面,为读者提供全面的了解和比较。
11 0
|
1月前
|
物联网 vr&ar Android开发
探索安卓与iOS操作系统的发展趋势
本文探讨了安卓与iOS操作系统在不同方面的发展趋势,并分析了它们在技术、用户体验和市场竞争等方面的特点和趋势。通过对比研究,揭示了两大操作系统的发展方向,为读者了解移动操作系统的未来发展提供了参考。
7 0
|
2月前
|
搜索推荐 Android开发 iOS开发
安卓与iOS操作系统的发展与比较
在移动互联网时代,安卓和iOS两大操作系统在智能手机市场竞争激烈。本文将从技术架构、生态系统、用户体验等方面对安卓和iOS进行比较分析,探讨它们各自的特点和发展趋势。
|
2月前
|
Web App开发 前端开发 网络安全
前端分析工具之 Charles 录制 Android/IOS 手机的 https 应用
【2月更文挑战第21天】前端分析工具之 Charles 录制 Android/IOS 手机的 https 应用
60 1
前端分析工具之 Charles 录制 Android/IOS 手机的 https 应用