Hybrid App(混合模式移动应用)开发是指介于Web-AppNative-App这两者之间的一种开发模式,兼具Native App良好用户交互体验优势和Web App跨平台开发优势,技术上主要以webview和javascript的相互调用实现。

移动应用开发的四种方式

  • Native App: 原生应用(原生App)
  • React Native:React原生应用
  • Hybrid App:混合应用(混合App)
  • Web App:网页应用(移动Web)
Native App React Native Hybrid App Web App
开发成本
开发语言Native语言Web语言Web语言Web语言
安装更新复杂简单简单简单
跨平台
设备访问
高级图形
体验

Native与H5交互原理

移动端网页运行在手机应用内嵌的浏览器引擎中,这个没有UI的内核容器统称WebView,即iPhone的UIWebView(iOS 2.0–12.0)、WKWebView(iOS 8.0+,macOS 10.10+)和Android的WebView。也就是说,WebView就是在手机应用中运行和展示网页的界面和接口。

iOS与JS交互的解决方案

JavaScriptCore iOS 7.0+ macOS 10.5+ Mac Catalyst 13.0+Beta tvOS 9.0+

JavaScriptCore是苹果Safari浏览器的JavaScript引擎,就跟Google的V8引擎一样用来解析 JavaScript 代码,苹果在iOS7引入了JavaScriptCore框架,使得Objective-C 和 JavaScript 代码直接的交互变得更加的简单方便。

oc与js类型对照:
Objective-C typeJavaScript type
nilundefined
NSNullnull
NSStringstring
NSNumbernumber, boolean
NSDictionaryObject
NSArrayArray
NSDateDate
NSBlockFunction
idWrapper
ClassConstructor
Objective-C代码:
// 创建一个JSContext上下文对象,类似于JS中的Window对象
JSContext *ctx = [[JSContext alloc] init];

//定义js异常处理器
ctx.exceptionHandler = ^(JSContext *context, JSValue *exception){
  // 当执行js出现异常时会在控制台上打印
  NSLog(@"js异常:%@",exception);
};

// 执行JS代码
[ctx evaluateScript:@"var num = 10"];
[ctx evaluateScript:@"var arr = ['a', 'b', 'c']"];
[ctx evaluateScript:@"var foo = function(num) {return Math.pow(num, 2);}"];

// 获取js变量num
JSValue *num = ctx [@"num"];

// 获取数组中第一个参数
JSValue *arr = ctx[@"arr"];
JSValue *arr1= arr[0];

// 获取num值的平方
JSValue *square = [ctx evaluateScript:@"foo(num)"];

// oc调用js nativeInvokeJS被定义在H5页面中
JSValue * nativeInvokeJS = ctx[@"nativeInvokeJS"];
// 调用了js中方法"nativeInvokeJS",并且传参数@"hello word"
[nativeInvokeJS callWithArguments:@[@"hello word"]];

// 生成本地js方法,供h5调用
ctx[@"jsInvokeNative"] = ^(NSString *paramer){
  JSValue *currentThis = [JSContext currentThis];\
  JSValue *currentCallee = [JSContext currentCallee];
  NSArray *currentParamers = [JSContext currentArguments];
  dispatch_async(dispatch_get_main_queue(), ^{
    // js调起OC代码,代码在子线程,更新OC中的UI,需要回到主线程
    NSLog(@"js参数:%@",paramer);
});
HTML5代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>H5与Native交互</title>
</head>
<body>
    <script>
        var nativeInvokeJS = function(parameter) {
            alert (parameter);
        };
    </script>
    <button onclick="jsInvokeNative('js')">调用OC方法</button>
</body>
</html>

上面两段代码就是通过JavaScriptCore实现的Objective-C 和 JavaScript的简单交互示例。

WebViewJavascriptBridge

WebViewJavaScriptBridge用于WKWebView和UIWebView 中 OC 和 JS 交互的一个 iOS/OSX 桥接层。

Objective-C代码:
#import "WebViewJavascriptBridge.h"

@property WebViewJavascriptBridge* bridge;

// 使用wkwebview、uiwebview(ios)或webview(osx)实例化一个webviewjavascriptbridge对象
self.bridge = [WebViewJavascriptBridge bridgeForWebView:webView];

[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);
}];
HTML5代码:
function setupWebViewJavascriptBridge(callback) {
  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 = 'https://__bridge_loaded__';
  document.documentElement.appendChild(WVJBIframe);
  setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}

// 注册 OC handlers & 调用OC handlers
setupWebViewJavascriptBridge(function(bridge) {
	
  /* Initialize your app here */

  bridge.registerHandler('JS Echo', function(data, responseCallback) {
    console.log("JS Echo called with:", data)
    responseCallback(data)
  })
  bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {
    console.log("JS received response:", responseData)
  })
})

WKWebView iOS 8.0+ macOS 10.10+

从iOS 8.0和OS X 10.10开始,使用WKWebView将Web内容添加到应用程序中。不再使用UIWebview或webview。

UIWebView iOS 2.0–12.0 Deprecated

Android与JS交互的解决方案

Android调用H5

在 Kitkat(4.4)之前并没有提供 iOS 类似的调用方式,只能用 loadUrl 一段 JavaScript 代码,来实现:

// 调用js中的JSBridge.trigger方法
webView.loadUrl("javascript:JSBridge.trigger('nativeInvokeJs')");

而在 Kitkat 之后的版本,也可以用 evaluateJavascript 方法实现:

webView.evaluateJavascript("javascript:nativeInvokeJs()", new ValueCallback<String>() {
    @Override
    publicvoidonReceiveValue(String value){

    }
});

H5调用Android

  • 拦截 URL SCHEME
  • webview中注入API
// 注入API
class JSInterface {
    @JavascriptInterface
    public String jsInvokeNative(params) {
        return params;
    }
}
webView.addJavascriptInterface(new JSInterface(), "JSBridge");
HTML5代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>H5与Native交互</title>
</head>
<body>
    <script>
        var nativeInvokeJS = function(parameter) {
            alert (parameter);
        };
    </script>
    <button onclick="JSBridge.jsInvokeNative('js')">调用Native方法</button>
</body>
</html>

参考