返回

#yyds干货盘点#H5与原生APP交互

发布时间:2022-12-24 19:37:48 427
# 前端# java# java# 数据

前言

在前端进行开发H5页面的时候,常常会有一些需要内嵌到APP的H5页面。在APP中使用H5页面主要是由于它是运行在浏览器上,所以只需要开发一次便可以在不同的操作系统上显示,并且迭代版本很方便。开发成本比较低。

在开发的过程中,经常需要和APP进行交互。本文已H5的角度整理了双方交互,如有不全面欢迎补充指正,大致将交互分为这两种情况:

  • 单向通信的交互
  • 不满足于单项传参需要return和callback的交互。

单向通信的交互

单向通信又分为不需要传参的通信以及需要传参的通信。

  1. 不需要传参的通信
    只是需要在原生和H5页面之间完成跳转页面的动作,不需要互相进行数据的传递:
  • 原生跳H5:将H5对应的URL给移动端同学即可
  • H5跳原生:location.herf = 要跳转的原生页面对应的协议名
示例:
// H5
window.location = 'app://login';

// 原生(以IOS端为例)
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {

// 一般用作交互的链接都会有一个固定的协议头,这里我们一“app”作为协议头为了,实际项目中可以修改
if ([scheme isEqualToString:@"app"]) { // scheme为“app”说明是做交互的链接
if ([host isEqualToString:@"login"]) { // host为“login”对应的就是登录操作
NSDictionary *paramsDict = [request.URL getURLParams];
UIAlertView *alert = [[UIAlertView alloc]
[alert show];
}
return YES;
}
  1. 需要传参的通信 H5与APP需要进行单向的数据传递,这里介绍两种方式:
  • url传参:

将参数携带在H5url后面或者app协议后面,对需要的参数进行拦截过滤便可在各自的开发环境中使用了

H5向原生传参示例:
// H5
window.location = 'app://login?account=13011112222&password=123456'; //将参数拼接到协议后面

// 原生(以IOS端为例)
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *scheme = request.URL.scheme;
NSString *host = request.URL.host;

// 一般用作交互的链接都会有一个固定的协议头,这里我们一“app”作为协议头为了,实际项目中可以修改
if ([scheme isEqualToString:@"app"]) { // scheme为“app”说明是做交互的链接
if ([host isEqualToString:@"login"]) { // host为“login”对应的就是登录操作
NSDictionary *paramsDict = [request.URL getURLParams];
NSString *account = paramsDict[@"account"];
NSString *password = paramsDict[@"password"];
NSString *msg = [NSString stringWithFormat:@"执行登录操作,账号为:%@,密码为:%@", account, password];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
[alert show];
}
return YES;
}
  • window传参:

因为 app 是宿主,可以直接访问 h5,所以这种调用比较简单,就是在 h5 中曝露一些全局对象(包括方法),然后在原生 app 中调用这些对象。

原生调用H5示例:
// H5
window.sdk= {
double = value => value * 2
trible = value => value * 3
}

// 原生(以IOS端为例)
NSString *func = @"window.sdk.double(10)";
NSString *str = [webview stringByEvaluatingJavaScriptFromString:func];// 20
H5调用原生示例:
// 原生(以IOS端为例)
@interface AppSdk : NSObject
{}
- (int) double:(int)value;
- (int) triple:(int)value;
@end
@implementation AppSdk
- (int) double:(int)value {
return value * 2;
}
- (int) triple:(int)value {
return value * 3;
}
@end
JSContext *context=[webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
AppSdk *appSdk = [AppSdk new];
context[@"appSdk"] = appSdk;

H5:

window.appSdk.double(10); // 20

双向通信的交互

上面说的单向通信的交互通常会使一个简单的方法变得非常割裂,在一些稍微复杂的场景之下,双端的维护成本很高,因此通常我们使用双向通信的时候更多一些。我们可以在回调函数中做很多事情。这里就要提到大家耳熟能详的WebViewJavaScriptBridge:

  • WebViewJavaScriptBridge基本原理
  • WebViewJavaScriptBridge准备工作
  • WebViewJavaScriptBridge原生调用H5
  • WebViewJavaScriptBridgeH5调用原生

基本原理

WebViewJavaScriptBridge的基本原理简单来说就是,建立一个桥梁,然后注册自己,调用他人。

把 OC 的方法注册到桥梁中,让 JS 去调用。

把 JS 的方法注册在桥梁中,让 OC 去调用。

准备工作

  • js初始化:
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback]; // 创建一个 WVJBCallbacks 全局属性数组,并将 callback 插入到数组中。
var WVJBIframe = document.createElement('iframe'); // 创建一个 iframe 元素
WVJBIframe.style.display = 'none'; // 不显示
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'; // 设置 iframe 的 src 属性
document.documentElement.appendChild(WVJBIframe); // 把 iframe 添加到当前文导航上。
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}

// 这里主要是注册 OC 将要调用的 JS 方法。下面具体的交互操作会提到
setupWebViewJavascriptBridge(function(bridge){

});

原生调用H5

原生要调用H5的js需要两个步骤。首先js要注入一个方法a到桥梁中去。第二步当然就是在原生那边去调用桥梁中的方法a了。

  1. 往桥梁中注入js的a方法
  2. 在上面准备工作的时候,我们提到了,js中的第二段函数是要注册OC将要调用的JS方法的,具体的操作如下:
// 往桥梁中注入js方法a
setupWebViewJavascriptBridge(function(bridge){
// 声明 OC 需要调用的 JS 方法。
bridge.registerHanlder('a',function(data,responseCallback){
// data 是 OC 传递过来的数据.
// responseCallback 是调用完毕之后传递给 OC 的数据
alert("JS 被 OC 调用了.");
responseCallback({jsdata: "js 的数据",from : "JS"});
})
});
});
  1. OC调用桥梁中的a方法
[_jsBridge callHandler:@"a" data:@"传递给 JS 的参数" responseCallback:^(id responseData) {
NSLog(@"JS 的返回值: %@",responseData);
}];

H5调用原生

H5调用原生同样是两个步骤。注册自己,调用他人。

  1. 往桥梁中注入OC的b方法
[_jsBridge registerHandler:@"b" handler:^(id data, WVJBResponseCallback responseCallback) {
// data 是 JS 传递过来的数据.
// responseCallback 是调用完毕之后传递给 js 的数据
responseCallback(@"传给js的值");
}];
  1. JS调用桥梁中的b方法
WebViewJavascriptBridge.callHandler('b',{data : "传给 OC 的入参"},function(dataFromOC){
alert("JS 调用了 OC 的方法");
alert('调用结束后OC返回给JS的数据:', dataFromOC);
});

调用方式

  1. OC调JS的三种方式
// 单纯的调用 JSFunction,不往 JS 传递参数,也不需要 JSFunction 的返回值。
[_jsBridge callHandler:@"a"];
// 调用 JSFunction,并向 JS 传递参数,但不需要 JSFunciton 的返回值。
[_jsBridge callHandler:@"a" data:@"传给js的入参"];
// 调用 JSFunction ,并向 JS 传递参数,也需要 JSFunction 的返回值。
[_jsBridge callHandler:@"a" data:@"传递给 JS 的参数" responseCallback:^(id responseData) {
NSLog(@"JS 的返回值: %@",responseData);
}];
  1. JS调OC的三种方式
// JS 单纯的调用 OC 的 block
WebViewJavascriptBridge.callHandler('b');

// JS 调用 OC 的 block,并传递 JS 参数
WebViewJavascriptBridge.callHandler('b',"JS 参数");

// JS 调用 OC 的 block,传递 JS 参数,并接受 OC 的返回值。
WebViewJavascriptBridge.callHandler('b',{data : "这是 JS 传递到 OC 的数据"},function(dataFromOC){
alert("JS 调用了 OC 的方法!");
document.getElementById("returnValue").value = dataFromOC;
});


特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线
下一篇
图谱节点 2022-12-24 19:01:03