三端易用的现代跨平台 Javascript bridge, 通过它, 你可以在 Javascript 和原生之间同步或异步的调用彼此的函数.
注意
DSBridge v3.0 是一个里程碑版本, 和 v2.0 相比, 有许多变化, 需要注意的是 v3.0 不兼容之前版本, 但是我们也会继续维护 v2.0 分支, 所以, 如果你是 v2.0 的使用者, 请放心继续使用 v2.0, 如果你是新用户, 请使用>=v3.0.
DSBridge v3.0.0 更新列表
DSBridge-Android
特性
AndroidIOSJavascript 三端易用, 轻量且强大安全且健壮
同时支持同步调用和异步调用
支持以类的方式集中统一管理 API
支持 API 命名空间
支持调试模式
支持 API 存在性检测
支持进度回调: 一次调用, 多次返回
支持 Javascript 关闭页面事件回调
支持 Javascript 模态 / 非模态对话框
Android 端支持腾讯 X5 内核
安装
pod "dsBridge"
示例
请参考工程目录下的 dsbridgedemo/ 文件夹. 运行并查看示例交互.
如果要在你自己的项目中使用 dsBridge :
使用
新建一个类, 实现 API
- @implementation JsApiTest
- // 同步 API
- - (NSString *) testSyn:(NSString *) msg
- {
- return [msg stringByAppendingString:@"[ syn call]"];
- }
- // 异步 API
- - (void) testAsyn:(NSString *) msg :(void (^)(NSString * _Nullable result,BOOL complete))completionHandler
- {
- completionHandler([msg stringByAppendingString:@"[ asyn call]"],YES);
- }
- @end
可以看到, DSBridge 正式通过 API 类的方式集中统一地管理 API
添加 API 类实例到 DWKwebView
- DWKWebView * dwebview = [[DWKWebView alloc] initWithFrame: bounds];
- // register api object without namespace
- [dwebview addJavascriptObject: [[JsApiTest alloc] init] namespace: nil];
在 Javascript 中调用原生 (Java/Object-c/swift) API , 并注册一个 javascript API 供原生调用.
初始化 dsBridge
- //cdn 方式引入初始化代码(中国地区慢, 建议下载到本地工程)
- //<script src="https://unpkg.com/dsbridge@3.0.6/dist/dsbridge.js"> </script>
- //npm 方式安装初始化代码
- //npm install dsbridge@3.0.6
- var dsBridge = require("dsbridge")
调用原生 API , 并注册一个 javascript API 供原生调用.
- // 同步调用
- var str=dsBridge.call("testSyn","testSyn");
- // 异步调用
- dsBridge.call("testAsyn","testAsyn", function (v) {
- alert(v);
- })
- // 注册 javascript API
- dsBridge.register('addValue',function(l,r){
- return l+r;
- })
在 Object-c 中调用 Javascript API
- [dwebview callHandler: @"addValue"arguments: @ [@3, @4] completionHandler: ^(NSNumber * value) {
- NSLog(@"%@", value);
- }];
- Object - C API signature
为了兼容 Android, 我们约定 OC API 签名如下:
同步 API.
(id) handler: (id) msg
参数可以是任何类型, 但是返回值类型不能为 void.
异步 API.
(void) handler(id arg, (void( ^ )(id result, BOOL complete)) completionHandler)
命名空间
命名空间可以帮助你更好的管理 API, 这在 API 数量多的时候非常实用, 比如在混合应用中 DSBridge (>= v3.0.0) 支持你通过命名空间将 API 分类管理, 并且命名空间支持多级的, 不同级之间只需用'.' 分隔即可
调试模式
在调试模式时, 发生一些错误时, 将会以弹窗形式提示, 并且原生 API 如果触发异常将不会被自动捕获, 因为在调试阶段应该将问题暴露出来如果调试模式关闭, 错误将不会弹窗, 并且会自动捕获 API 触发的异常, 防止 crash 强烈建议在开发阶段开启调试模式, 可以通过如下代码开启调试模式:
- // open debug mode
- [dwebview setDebugMode: true];
进度回调
通常情况下, 调用一个方法结束后会返回一个结果, 是一一对应的但是有时会遇到一次调用需要多次返回的场景, 比如在 javascript 钟调用端上的一个下载文件功能, 端上在下载过程中会多次通知 javascript 进度, 然后 javascript 将进度信息展示在 h5 页面上, 这是一个典型的一次调用, 多次返回的场景, 如果使用其它 Javascript bridge, 你将会发现要实现这个功能会比较麻烦, 而 DSBridge 本省支持进度回调, 你可以非常简单方便的实现一次调用需要多次返回的场景, 下面我们实现一个倒计时的例子:
- In Object-c
- - ( void )callProgress:(NSDictionary *) args :(void (^)(NSNumber * _Nullable result,BOOL complete))completionHandler
- {
- value=10;
- hanlder=completionHandler;
- timer = [NSTimer scheduledTimerWithTimeInterval:1.0
- target:self
- selector:@selector(onTimer:)
- userInfo:nil
- repeats:YES];
- }
- -(void)onTimer:t{
- if(value!=-1){
- hanlder([NSNumber numberWithInt:value--],NO);
- }else{
- hanlder(@"",YES);
- [timer invalidate];
- }
- }
- In javascript
- dsBridge.call("callProgress", function (value) {
- document.getElementById("progress").innerText = value
- })
完整的示例代码请参考 demo 工程
Javascript 弹出框
DSBridge 已经实现了 Javascript 的弹出框函数(alert/confirm/prompt), 这些对话框按钮标签文字默认都是中文的, 如果你想自定义这些文本可以参考
customJavascriptDialogLabelTitles
API, 如果你不想使用 DSBridge 实现的对话框, 你可以通过设置 DSUIDelegate 属性 (是 WKUIDelegate 的代理属性) 完全自定义
另外注意, DSBridge 实现的弹出框都是模态的, 这会阻塞 UI 线程, 如果你需要非模态的对话框, 请参考
- disableJavascriptDialogBlock
- API.
API 列表
Object-C API
在 Object-c 中我们把实现了供 javascript 调用的 API 类的实例 成为 Object-c API object.
addJavascriptObject: (id) object namespace: (NSString * ) namespace
添加一个 Object-c API object 到 DWKWebView, 并为它指定一个命名空间. 然后, 在 javascript 中就可以通过
bridge.call("namespace.api", ...)
来调用 Object-c API object 中的原生 API 了
如果命名空间是空(nil 或空字符串), 那么这个添加的 Object-c API object 就没有命名空间在 javascript 通过
bridge.call("api", ...)
调用
示例:
- In Object - c@implementation JsEchoApi - (id) syn: (id) arg {
- return arg;
- } - (void) asyn: (id) arg: (void( ^ )(id _Nullable result, BOOL complete)) completionHandler {
- completionHandler(arg, YES);
- }@end
- // register api object with namespace "echo"
- [dwebview addJavascriptObject: [[JsEchoApi alloc] init] namespace: @"echo"];
- In Javascript
- // call echo.syn
- var ret = dsBridge.call("echo.syn", {
- msg: "I am echoSyn call",
- tag: 1
- }) alert(JSON.stringify(ret))
- // call echo.asyn
- dsBridge.call("echo.asyn", {
- msg: "I am echoAsyn call",
- tag: 2
- },
- function(ret) {
- alert(JSON.stringify(ret));
- }) removeJavascriptObject: (NSString * ) namespace
通过命名空间名称移除相应的 Object-c API object.
- callHandler: (NSString * ) methodName arguments: (NSArray * ) args
- callHandler: (NSString * ) methodName completionHandler: (void( ^ )(id value)) completionHandler
- callHandler: (NSString * ) methodName arguments: (NSArray * ) args completionHandler: (void( ^ )(id value)) completionHandler
调用 javascript API.methodName 为 javascript API 的名称, 可以包含命名空间; 参数以数组传递, argumentss 数组中的元素依次对应 javascript API 的形参; completionHandler 用于接收 javascript API 的返回值, 注意: completionHandler 将在主线程中被执行
示例:
- [dwebview callHandler:@"append" arguments:@[@"I",@"love",@"you"]
- completionHandler:^(NSString * _Nullable value) {
- NSLog(@"call succeed, append string is: %@",value);
- }];
- // call with namespace 'syn', More details to see the Demo project
- [dwebview callHandler:@"syn.getInfo" completionHandler:^(NSDictionary * _Nullable value) {
- NSLog(@"Namespace syn.getInfo: %@",value);
- }];
- disableJavascriptDialogBlock: (bool) disable
小心使用. 如果你再 javascript 中调用弹窗函数(alert,confirm, 或 prompt), 那么 APP 将会挂起, 因为这些弹窗都是模态的, 会阻塞 APP 主线程, 此时 javascript 执行流也会阻塞如果你想避免阻塞, 可以通过此 API 禁止, 禁止后, 一旦 javascript 中调用了这些弹窗函数, APP 将弹出非模态对话框, 并立即返回,( confirm 会返回 true, prompt 返回空字符串)
如:
[dwebview disableJavascriptDialogBlock: true]
如果你想恢复模态对话框, 传 false 调用即可.
setJavascriptCloseWindowListener: (void( ^ _Nullable)(void)) callback
当 Javascript 中调用 window.close 时, DWKWebView 会触发此监听器:
- Example:
- [dwebview setJavascriptCloseWindowListener: ^{
- NSLog(@"window.close called");
- }];
- hasJavascriptMethod: (NSString * ) handlerName methodExistCallback: (void( ^ )(bool exist)) callback
检测是否存在指定的 javascript API,handlerName 可以包含命名空间.
- Example:
- // test if javascript method exists.
- [dwebview hasJavascriptMethod: @"addValue"methodExistCallback: ^(bool exist) {
- NSLog(@"method'addValue'exist : %d", exist);
- }];
- setDebugMode: (bool) debug
设置调试模式在调试模式时, 发生一些错误时, 将会以弹窗形式提示, 并且原生 API 如果触发异常将不会被自动捕获, 因为在调试阶段应该将问题暴露出来如果调试模式关闭, 错误将不会弹窗, 并且会自动捕获 API 触发的异常, 防止 crash 强烈建议在开发阶段开启调试模式
- customJavascriptDialogLabelTitles: (NSDictionary * ) dic
- custom the label text of javascript dialog that includes alert/confirm/prompt, the default text language is Chinese.
自定义 javascript 对话框上按钮标签的文本, 默认的文本语言是中文, 你可以自定义英文, 如:
- [dwebview customJavascriptDialogLabelTitles:@{
- @"alertTitle":@"Notification",
- @"alertBtn":@"OK",
- @"confirmTitle":@"",
- @"confirmCancelBtn":@"CANCEL",
- @"confirmOkBtn":@"OK",
- @"promptCancelBtn":@"CANCEL",
- @"promptOkBtn":@"OK"
- }];
- Javascript API
- dsBridge
"dsBridge" 在初始化之后可用 .
dsBridge.call(method, [arg, callback])
同步或异步的调用 Java API
method: Java API 名称, 可以包含命名空间
arg: 传递给 Java API 的参数只能传一个, 如果需要多个参数时, 可以合并成一个 json 对象参数
callback(String returnValue)
: 处理 Java API 的返回结果. 可选参数, 只有异步调用时才需要提供.
- dsBridge.register(methodName|namespace,function|synApiObject)
- dsBridge.registerAsyn(methodName|namespace,function|asyApiObject)
注册同步 / 异步的 Javascript API. 这两个方法都有两种调用形式:
注册一个普通的方法, 如:
- In Javascript
- dsBridge.register('addValue',function(l,r){
- return l+r;
- })
- dsBridge.registerAsyn('append',function(arg1,arg2,arg3,responseCallback){
- responseCallback(arg1+""+arg2+" "+arg3);
- })
- In Object-c
- // call javascript method
- [dwebview callHandler:@"addValue" arguments:@[@3,@4] completionHandler:^(NSNumber * value){
- NSLog(@"%@",value);
- }];
- [dwebview callHandler:@"append" arguments:@[@"I",@"love",@"you"] completionHandler:^(NSString * _Nullable value) {
- NSLog(@"call succeed, append string is: %@",value);
- }];
注册一个对象, 指定一个命名空间:
- In Javascript
- //namespace test for synchronous
- dsBridge.register("test", {
- tag: "test",
- test1: function() {
- return this.tag + "1"
- },
- test2: function() {
- return this.tag + "2"
- }
- })
- //namespace test1 for asynchronous calls
- dsBridge.registerAsyn("test1", {
- tag: "test1",
- test1: function(responseCallback) {
- return responseCallback(this.tag + "1")
- },
- test2: function(responseCallback) {
- return responseCallback(this.tag + "2")
- }
- })
因为 Javascript 并不支持函数重载, 所以不能在同一个 Javascript 对象中定义同名的同步函数和异步函数
- In Object-c
- [dwebview callHandler:@"test.test1" completionHandler:^(NSString * _Nullable value) {
- NSLog(@"Namespace test.test1: %@",value);
- }];
- [dwebview callHandler:@"test1.test1" completionHandler:^(NSString * _Nullable value) {
- NSLog(@"Namespace test1.test1: %@",value);
- }];
- dsBridge.hasNativeMethod(handlerName, [type])
检测 Java 中是否存在名为 handlerName 的 API, handlerName 可以包含命名空间.
type: 可选参数,
["all" | "syn" | "asyn"]
, 默认是 "all".
- // 检测是否存在一个名为'testAsyn'的 API(无论同步还是异步)
- dsBridge.hasNativeMethod('testAsyn')
- // 检测 test 命名空间下是否存在一个 testAsyn 的 API
- dsBridge.hasNativeMethod('test.testAsyn')
- // 检测是否存在一个名为 "testSyn" 的异步 API
- dsBridge.hasNativeMethod('testSyn', 'asyn') //false
- dsBridge.disableJavascriptDialogBlock(disable)
调用
dsBridge.disableJavascriptDialogBlock(...)
和在 Java 中调用
dwebview.disableJavascriptDialogBlock(...)
作用一样.
示例:
- //disable
- dsBridge.disableJavascriptDialogBlock()
- //enable
- dsBridge.disableJavascriptDialogBlock(false)
和 fly.js 一起使用
当 dsBridge 遇见 Fly.js 时, 将会打开一个新的世界 fly.js 传送门
正如我们所知, 在浏览器中, ajax 请求受同源策略限制, 不能跨域请求资源然而, Fly.js 有一个强大的功能就是支持请求重定向: 将 ajax 请求通过任何 Javascript bridge 重定向到端上, 并且 Fly.js 官方已经提供的 dsBridge 的 adapter, 可以非常方便的协同 dsBridge 一起使用由于端上没有同源策略的限制, 所以 fly.js 可以请求任何域的资源
另一个典型的使用场景是在混合 APP 中, 由于 Fly.js 可以将所有 ajax 请求转发到端上, 所以, 开发者就可以在端上进行统一的请求管理证书校验 cookie 管理访问控制等
详情请参考 github.com/wendux/fly. (DSBridge Android 版 demo 中包含 fly.js 的示例)
最后
如果你瞎换 DSBridge, 欢迎 star, 以便更多的人知道它, 谢谢 !
来源: https://juejin.im/post/5a7aaa9f6fb9a0634b4d6128