阅读 211

Hybrid App 实战之 JSBridge 通信

原生和 H5 混合开发,通过 JSBridge 通信,常用方式为注入 API 和 拦截 URL SCHEME实现。实现拦截 URL SCHEME,获取本地存储权限,客户端头部交互,打开谷歌应用商店等功能

混合(Hybrid App)开发:原生和 H5 混合开发,通过 webview 内嵌 H5 实现

  • 优点:

    • 开发效率高,节约时间,一套代码可适用 Android,IOS,也可在微信或浏览器中访问 H5 链接

    • 代码维护方便,版本更新快,节省成本

    • 更新和部署比较方便,升级版本不需要应用商店审核

  • 缺点:

    • 功能无法自定义,需要客户端支持

    • 加载缓慢,网络要求高

    • 安全性比较低

JSBridge 通信

JSBridge 就是一个 Native 与 JS 的双向通信通道:

  • Native 只通过一个固定的桥对象调用 JS

    • 直接执行拼接好的 JS 代码,JS 的方法必须在全局的 window 上

  • JS 也只通过固定的桥对象调用 Native,推荐注入 API

    • iframe.src:url 长度有限制。iOS 采用 Ajax 发送同域请求的方式,并将参数放到 head 或 body 里,解决长度隐患,但 WKWebView 不支持这种方式

    • location.href:连续调用 Native,很容易丢失一些调用;创建请求,耗时会长



    1. 拦截 URL SCHEME:Web 端通过某种方式(例如 iframe.src)发送 URL Scheme 请求,之后 Native 拦截到请求并根据 URL SCHEME(包括所带的参数)进行相关操作

    1. 注入 API:通过 WebView 提供的接口,向 JS 的 window 中注入对象或方法,让 JS 调用时,直接执行相应的 Native 代码逻辑

项目实战

项目前端基于 NuxtJS 服务端框架,实现方案就是外层是一个 Android 原生壳子,内嵌 H5 页面,当然 H5 页面也可单独运行。其中 JSBridge 为挂载在 window 上的桥接对象

  • 注入 API:在window上挂载对象和方法

    1. window.JSBridge:可判断是否为 app 环境

    2. 挂载了 getAppInfo()函数,用于传递 app 信息,例如版本号、渠道等等

    3. 本地存储权限判断(checkPermission, requestPermission),具体可看 getPermission()

    4. 也可挂载一些其他的方法变量和方法

  • 拦截 URL SCHEME

    1. initTitle:初始化头部的命令,具体可看 setHeaderConfig()

    2. openUrl:跳转链接处理,具体可看 jumpNative()

基础使用

// 与原生交互方法:拦截 URL SCHEME const executeUrlCommand = (url) => {   const frame = document.createElement("iframe");   frame.width = "1px";   frame.height = "1px";   frame.style.display = "none";   frame.src = url;   document.body.appendChild(frame);   setTimeout(() => {     document.body.removeChild(frame);   }, 100); }; // 客户端交互 获取本地存储权限 const getPermission = () => {   // 是否有存储权限,true-有,false-没有   const permission = window.JSBridge.checkPermission(     "android.permission.WRITE_EXTERNAL_STORAGE"   );   if (permission) {     // 没有的话,需要请求开启权限     window.JSBridge.requestPermission(       "android.permission.WRITE_EXTERNAL_STORAGE"     );     return false;   } }; // layouts/default.vue export default {   mounted() {     this.getAppInfo();     this.setHeadConfig();     this.nativeInteract();   },   methods: {     isApp() {       // 判断是否是在app环境       let isApp = false;       if (/JSBridge/i.test(navigator.userAgent) || window.JSBridge) {         isApp = true;       }       return isApp;     },     getAppInfo() {       // 获取app信息       if (this.isApp()) {         const appInfo = JSON.parse(window.JSBridge.getAppInfo());       }     },     // 设置头部信息,与客户端交互     setHeaderConfig() {       // 头部信息主要包含title,左侧按钮(文案,事件),右侧按钮(文案,事件)       const config = {         showBack: true,         title: "首页",         backEvent: "window.history.back()",         leftText: "",         leftClickEvent: "() => {}",         rightText: "",         rightClickEvent: "() => {}",       };       if (this.isApp()) {         executeUrlCommand(           `JSBridge://initTitle?backShow=${config.showBack}&backEvent=${config.backEvent}&leftText=${config.leftText}&leftClickEvent=${config.leftClickEvent}&rightText=${config.rightText}&rightClickEvent=${config.rightClickEvent}`         );       }     },   }, }; 复制代码

跳转处理

// 客户端交互 跳转链接处理 const jumpNative = (jumpType, jumpUrl) => {   let command = "";   if (jumpType === "apkUrl") {     // apk链接     command =       "JSBridge://openUrl?url=" +       encodeURIComponent(jumpUrl) +       "&exitCurrent=false";   } else if (jumpType === "gpUrl") {     // 谷歌应用商店链接     jumpToGooglePlay(jumpUrl);   } else {     executeUrlCommand(command);   } }; // 打开谷歌应用商店 const jumpToGooglePlay = (url) => {   const t = 1000;   let isApp = true;   setTimeout(() => {     if (!isApp) {       window.location.href = url;     }   }, 2000);   const addIframe = (id) => {     const t1 = Date.now();     const iframe = document.createElement("iframe");     iframe.id = "goPlay";     iframe.src = "market://details?" + id;     iframe.style.display = "none";     document.body.appendChild(iframe);     setTimeout(() => {       tryToApp(t1);     }, t);   };   const tryToApp = (t1) => {     const t2 = Date.now();     if (!t1 || t2 - t1 < t + 200) {       isApp = false;     }   };   if (url.indexOf("//play.google.com/store/apps/details?") > 0) {     addIframe(url.split("//play.google.com/store/apps/details?")[1]);   } else {     window.location.href = url;   } }; // h5下载apk const installApk = (src) => {   const form = document.createElement("form");   form.action = src;   document.getElementsByTagName("body")[0].appendChild(form);   form.submit();   return false; };


作者:时光足迹
链接:https://juejin.cn/post/7021451193285083150


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐