前言
提到混合式开发的童鞋, 是不是想到是指 React Native , Weex, 或者流行的 Flutter, 有意观赏上类分享, 那友情提醒误入文章的你, 打开控制台输入 history.go(). 本篇总结的混合式开发, 指 原生 App 与 H5 的互相传递参数或交互逻辑, 场景就是 H5 嵌套于原生中, 通过原生访问 H5 页面.
至于原生 App 为什么采用 H5, 想必大家可能想到一个原因是, 原生 App 审核需要一定时间, 营销推广活动比较频繁的公司, 所以会选择 H5 开发.
本篇主要带领大家, 熟悉了解 H5 与原生交互 需求中, H5 部门与原生部门这块相关协调工作, 让大家有彼此工作大致了解. 欢迎提补充建议或是错误指正.
安卓, H5 之间传参交互
安卓 与 H5 前端 协调工作较为简单, 只需要安卓将交互方法注册到 Windows 的对象名, 提供给 H5. 然后就是两边的自我代码开发.
安卓方面工作
搭建安卓与 H5 交互通道, 定义提供原生通道名, 并项目启动时调用.
书写安卓, H5, 相互传参的方法
与 H5 联调方法, 是否正常实现传参或功能逻辑
下面代码片段, 通道名设置为 Flight, 交互方法在 Flight 类实现
- @SuppressLint("JavascriptInterface")
- private void initwebViewCallBack() {
- Log.e(TAG, "initWebViewCallBack:");
- if (flightWv != null) {
- flightWv.addJavascriptInterface(new Flight(), "Flight");
- }
- }
- public class Flight {
- @JavascriptInterface
- public String getVersion() {
- H5Besn bean = new H5Besn();
- bean.setCity(Cfg.localityCityName);
- bean.setLat(Cfg.myLatitude);
- bean.setLon(Cfg.myLongitude);
- bean.setVersionName(AppUtils.getVersionName(FlightWebAcitvity.this));
- String JSON = GsonUtils.createJsonStr(bean);
- return JSON;
- }
- }
H5 前端工作
在项目入口文件引入, 与原生交互代码. 当然考虑到与原生操作过多, 避免入口文件臃肿, 可在入口文件引入专门原生操作的分流的文件命名为 bride.JS.
在 bridge.JS 区分 iOS, 安卓环境, 或者其他环境等, 便于安卓方法只注册在安卓设备下, 避免冗余无用代码
安卓方法调用
下面前端代码相关片段:
- import store from '@/store'
- import { setupWebViewJavascriptBridge } from '@/utils'
- // ...
- // 判断环境, 以下添加了 && u.indexOf('matafyApp') 此类并唯一标识哪个 App 标识 (找原生开发同学寻要)
- const urlParams = urlParamsObj()
- const u = navigator.userAgent
- const isAndroid = (u.indexOf('Android')> -1 || u.indexOf('Adr')> -1) && u.indexOf('matafyApp')> -1 // Android 终端且在原生 App 内
- const isiOS = u.indexOf('iPhone')> -1 && u.indexOf('matafyApp')> -1 // iOS 终端且在原生 App 内 !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
- const isMobile = !!u.match(/AppleWebKit.*Mobile.*/) // 是否为移动终端
- const isMiniprogram = urlParams['weixinId'] // 微信 7.0.0 开始, 允许 userAgent 来判断小程序环境, 兼容为携带 weixinid 的路径
- // ......
- // 判断处于哪个环境下, 并存于 vuex 的 store 中, 不过如果你项目未使用 vuex, 大可不必使用.
- if (isMobile) {
- if (isAndroid) {
- store.dispatch('app/setDevice', 'android')
- } else if (isiOS) {
- store.dispatch('app/setDevice', 'ios')
- } else if (isMiniprogram || Windows.__wxjs_environment === 'miniprogram') {
- if (isMiniprogram) sessionStorage.setItem('weixinId', isMiniprogram)
- store.dispatch('app/setDevice', 'wx_program')
- } else {
- store.dispatch('app/setDevice', 'mobile')
- }
- } else {
- store.dispatch('app/setDevice', 'mobile')
- }
- // 原生交互方法
- switch (store.getters.device) {
- case 'android':
- // 获取 token
- const setToken = (token) => {
- store.dispatch('app/setToken', token)
- }
- // 获取版本号信息
- const setVersion = (version) => {
- localStorage.setItem('sendVersion', version)
- }
- Windows.setVersion = setVersion // 注意: 调用方法, 定要注册在 Windows 对象下
- Windows.setToken = setToken
- break;
- // ...
- }
iOS,H5 之间传参交互
iOS 与 H5, 不需要像安卓提前告知暴露的对象名.
iOS 方面工作
安装与 H5 交互的通道
pod 'WebViewJavascriptBridge', '~> 6.0'
以下 iOS 代码用法:
也可参考 GitHub 代码, 点击链接
- Import the header file and declare an ivar property:
- #import "WebViewJavascriptBridge.h"
- Instantiate WebViewJavascriptBridge with a WKWebView, UIWebView (iOS) or WebView (OS X):
- self.bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
- Register a handler in ObjC, and call a JS handler:
- [self.bridge registerHandler:@"ObjC Echo" handler:^(id data, WVJBResponseCallback responseCallback) {
- NSLog(@"ObjC Echo called with: %@", data);
- responseCallback(data);
- }];
- [self.bridge callHandler:@"JS Echo" data:nil responseCallback:^(id responseData) {
- NSLog(@"ObjC received response: %@", responseData);
- }];
H5 前端工作
前端工作也只有短短两步, 第一步, 就是将连接 webview 通道的方法, 可存放于函数的文件, 便于多么模块调用. 第二步, 调用通道方法, 进行注册原生需要调用方法或主动调用原生方法.
函数库文件 src/utils/index.JS 文件部分代码如下:
- /**
- * @export iOS 与 JS 建立连接基础方法
- * @param {Function} 调用方法的回调
- */
- export function setupWebViewJavascriptBridge(callback) {
- if (Windows.WebViewJavascriptBridge) {
- return callback(Windows.WebViewJavascriptBridge)
- }
- if (Windows.WVJBCallbacks) {
- return Windows.WVJBCallbacks.push(callback)
- }
- Windows.WVJBCallbacks = [callback]
- var WVJBIframe = document.createElement('iframe')
- WVJBIframe.style.display = 'none'
- WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'
- document.documentElement.appendChild(WVJBIframe)
- setTimeout(() => {
- document.documentElement.removeChild(WVJBIframe)
- }, 0)
- }
项目处理原生文件 src/utils/bridge.JS, 需项目入口文件引入, 部分代码如下:
- import store from '@/store'
- import { setupWebViewJavascriptBridge } from '@/utils'
- // ...
- switch (store.getters.device) {
- // ...
- case 'ios':
- setupWebViewJavascriptBridge((App) => {
- App.registerHandler('sendToken', (token, responseCallback) => { // 获取 iOS 原生 App 传递过来的 token, 存于前端数据池 (仓库)
- store.dispatch('app/setToken', token)
- })
- }
- break;
- // ...
- }
常见的 H5 与原生交互的应用场景
原生与 H5 交互传参的应用场景, 情况分为两类. 第一, 原生传递 H5 参数或回调相关操作; 第二, H5 主动获取原生参数或进行回调相关操作.
各个公司各个项目需求不一致, 例如, token 传递, 经纬度传递, App 版本信息传递, App 设备 Id 的传递, 设置原生系统栏的背景色字号色的传递, 调用原生方法打开第三方浏览器或者内置浏览器的方法, h5 控制原生返回键的逻辑等等
春雨如酒. jpg
来源: http://www.jianshu.com/p/7d71eaec85e1