h5页面与原生APP的交互

前阵子有个需求, 需要在移动端实现图表的呈现, 问题就在于, android和ios,以及h5能够统一的图表很少,echarts又不太符合我们的需求, 并且ios需要提审, 图表可能会更换样式. 经过探讨, 统一由h5提供图表. app内嵌h5页面呈现图表.

那么问题来了, app端需要加载html,js等文件, 这个速度真的不敢恭维, 近40多秒的加载速度, 用户体验特别差,

那么怎么办?

最终决定由h5提供图表模板, 上传到管理后台之后, 由客户端(ios / android) 自行将模板下载并匹配. 那么这样的话, 就由客户端提前加载并渲染好对应图表, 而用户进入界面时,呈现的图表界面早已经加载过了, 速度大大的提升了.

图表数据比较多时, js异步加载数据的速度就变慢了, 那么怎么处理?

我们都知道原生的加载速度是很顺畅的, 这也是很多人喜欢用app的原因, 那么我们可以将数据交给app端处理, 再返回给h5页面渲染数据.

这个时候就需要进行h5和app的交互操作了.

实际上, 只要弄懂h5和app的交互原理, 处理起来就不那么棘手了.

 

h5 与原生 app 的交互,本质上说,就是两种调用:

  1. app 调用 h5 的代码
  2. h5 调用 app 的代码

1. app 调用 h5 的代码

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

图片描述

javascript

window.sdk = {double = value => value * 2,triple = value => value * 3,
};

android

webview.evaluateJavascript('window.sdk.double(10)', new ValueCallback() {@Overridepublic void onReceiveValue(String s) {// 20}
});

ios

NSString *func = @"window.sdk.double(10)";
NSString *str = [webview stringByEvaluatingJavaScriptFromString:func]; // 20

2. h5 调用 app 的代码

因为 h5 不能直接访问宿主 app,所以这种调用就相对复杂一点。

这种调用常用有两种方式:

  1. 由 app 向 h5 注入一个全局 js 对象,然后在 h5 直接访问这个对象
  2. 由 h5 发起一个自定义协议请求,app 拦截这个请求后,再由 app 调用 h5 中的回调函数

2.1 由 app 向 h5 注入一个全局 js 对象

这种方式沟通机制简单,比较好理解,并且对于 h5 来说,没有新的东西,所以是比较推荐的一种方式。但这种方式可能存在安全隐患,详细查看 你不知道的 Android WebView 使用漏洞。

图片描述

android

webview.addJavascriptInterface(new Object() {@JavascriptInterfacepublic int double(value) {return value * 2;}@JavascriptInterfacepublic int triple(value) {return value * 3;}
}, "appSdk");

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;
}
@endJSContext *context=[webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];AppSdk *appSdk = [AppSdk new];context[@"appSdk"] = appSdk;

javascript

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

 

2.2 由 h5 发起一个自定义协议请求

这种方式要稍复杂一点,因为需要自定义协议,这对很多前端开发者来说是比较新的东西。所以一般不推荐这种方式,可以作为第一种方式的补充。

大致需要以下几个步骤:

  1. 由 app 自定义协议,比如 sdk://action?params
  2. 在 h5 定义好回调函数,比如 window.bridge = {getDouble: value => {}, getTriple: value => {}}
  3. 由 h5 发起一个自定义协议请求,比如 location.href = 'sdk://double?value=10'
  4. app 拦截这个请求后,进行相应的操作,获取返回值
  5. 由 app 调用 h5 中的回调函数,比如 window.bridge.getDouble(20);

图片描述

javascript

window.bridge = {getDouble: value => {// 20}, getTriple: value => {// more  }
};location.href = 'sdk://double?value=10';

android

webview.setWebViewClient(new WebViewClient() {@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {// 判断如果 url 是 sdk:// 打头的就拦截掉// 然后从 url sdk://action?params 中取出 action 与params Uri uri = Uri.parse(url);                                 if ( uri.getScheme().equals("sdk")) {// 比如 action = double, params = value=10webview.evaluateJavascript('window.bridge.getDouble(20)');return true;}return super.shouldOverrideUrlLoading(view, url);}
});

ios

- (BOOL)webview:(UIWebView *)webview shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {// 判断如果 url 是 sdk:// 打头的就拦截掉// 然后从 url sdk://action?params 中取出 action 与paramsNSString *urlStr = request.URL.absoluteString;if ([urlStr hasPrefix:@"sdk://"]) {// 比如 action = double, params = value=10NSString *func = @"window.bridge.getDouble(20)";[webview stringByEvaluatingJavaScriptFromString:func];return NO;}return YES;
}

到这里已经完美处理上面的需求.

 

关于ios的处理,可以参考:

iOS开发 WKWebView使用第三方库WebViewJavascriptBridge 以及js端处理

关于android的处理,可以参考

js 与 android 的交互(方法互调用) 和android向 js传递值, js向android传参数 事件拦截看上一篇

使用WebView ,参考  https://blog.csdn.net/qiuqi12/article/details/79073672

var u = navigator.userAgent;var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端// alert('是否是Android:'+isAndroid);// alert('是否是iOS:'+isiOS);function setupWebViewJavascriptBridge(callback) {if (window.WebViewJavascriptBridge) {callback(WebViewJavascriptBridge)} else {document.addEventListener('WebViewJavascriptBridgeReady', function() {callback(WebViewJavascriptBridge)},false);}if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }window.WVJBCallbacks = [callback];var WVJBIframe = document.createElement('iframe');WVJBIframe.style.display = 'none';WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';document.documentElement.appendChild(WVJBIframe);setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)}setupWebViewJavascriptBridge(function(bridge) {if(isAndroid){bridge.init(function(message, responseCallback) {responseCallback("");});}})// 调用 native
window.WebViewJavascriptBridge && window.WebViewJavascriptBridge.callHandler('nativeHandler', data, res => {})

参考博客:

https://github.com/senntyou/blogs

 

 

 

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部