(async (resolve, reject) => {
38 | let sum = this.add()
39 | resolve(sum)
40 | })
41 | }
42 |
43 | async add(){
44 | return this.a + this.b
45 | }
46 |
47 |
48 | }
49 |
50 | export class JsBridge {
51 | private cHandler?: CompleteHandler
52 |
53 | /**
54 | * 同步模版
55 | * @param p
56 | * @returns
57 | */
58 | @JavaScriptInterface(false)
59 | testSync(p: string): string {
60 | LogUtils.d("testSync: " + JSON.stringify(p))
61 | return "原生同步testSync方法返回的数据"
62 | }
63 |
64 | /**
65 | * 同步方法中执行异步并发任务
66 | * @param args
67 | * @returns
68 | */
69 | @JavaScriptInterface(false)
70 | testTaskWait(args: string): number {
71 | let p = new Param(100, 200)
72 | let p1 = new Param(100, 300)
73 | taskWait(p)
74 | p1.tag = "Param"
75 | taskWait(p1)
76 | LogUtils.d(`testTaskWait sum: ${p.sum} ${p1.sum}`)
77 | return p.sum + p1.sum
78 | }
79 |
80 | @JavaScriptInterface(false)
81 | testNoArg(): boolean {
82 | LogUtils.d('testNoArg')
83 | return true
84 | }
85 |
86 | @JavaScriptInterface()
87 | testNoArgAsync() {
88 | LogUtils.d('testNoArgAsync')
89 | ToastUtils.show("testNoArgAsync")
90 | }
91 |
92 | /**
93 | * 异步模版
94 | * @param args
95 | * @param handler
96 | */
97 | @JavaScriptInterface()
98 | testAsync(args: string, handler: CompleteHandler) {
99 | LogUtils.d("testAsync: " + JSON.stringify(args))
100 | this.cHandler = handler
101 | this.countdown(5, (time:number) => {
102 | if (time === 0) {
103 | handler.complete("原生异步testAsync方法返回的数据--结束")
104 | handler.complete("原生异步testAsync方法返回的数据--结束2") // 不会被调用 会报错 Uncaught ReferenceError: xxx is not defined
105 | } else {
106 | handler.setProgressData("原生异步testAsync方法返回的数据--" + time)
107 | }
108 | })
109 |
110 |
111 | }
112 |
113 | countdown(seconds: number, callback: (counter: number) => void) {
114 | let count = seconds;
115 | const interval = setInterval(() => {
116 | if (count === 0) {
117 | clearInterval(interval);
118 | LogUtils.d("Finished");
119 | return;
120 | }
121 | count--
122 | callback(count)
123 | }, 1000);
124 | }
125 | }
126 |
127 |
--------------------------------------------------------------------------------
/entry/src/main/ets/bridge/JsBridge2.ets:
--------------------------------------------------------------------------------
1 | import { LogUtils } from '../utils/LogUtils';
2 | import { BaseSendable, CompleteHandler, JavaScriptInterface, taskWait } from '@hzw/ohos-dsbridge';
3 | import { JsParam } from '../pages/UseInJs2Page';
4 |
5 | /**
6 | * @author: HZWei
7 | * @date: 2024/7/6
8 | * @desc: 测试DSBridge2.0脚本
9 | */
10 |
11 | export class JsBridge2 {
12 |
13 | @JavaScriptInterface(false)
14 | testComponentSync(args: string): Object {
15 | const jsParam = JSON.parse(args) as JsParam
16 | LogUtils.d(jsParam.msg ?? '')
17 | // return `组件中的同步方法: ${jsParam.msg}`
18 | return `${jsParam.msg}`
19 | }
20 |
21 | @JavaScriptInterface(false)
22 | testNoArgSync(): number {
23 | return 1 + 2
24 | }
25 |
26 | @JavaScriptInterface()
27 | testComponentAsync(args: string, handler: CompleteHandler) {
28 | // LogUtils.d(args)
29 | const jsParam = JSON.parse(args) as JsParam
30 | handler.complete(`组件中的异步方法: ${jsParam.msg}`)
31 | }
32 |
33 | @JavaScriptInterface()
34 | testCallNoArgAsync(handler: CompleteHandler) {
35 | handler.complete(`无参异步方法返回值: 100`)
36 | }
37 |
38 | @JavaScriptInterface()
39 | testProgressAsync(handler: CompleteHandler) {
40 | this.countdown(5, (time: number) => {
41 | if (time === 0) {
42 | handler.complete("原生异步方法返回的数据--结束")
43 | handler.complete("原生异步方法返回的数据--结束2") // 不会被调用 会报错 Uncaught ReferenceError: xxx is not defined
44 | } else {
45 | handler.setProgressData("原生异步方法返回的数据--" + time)
46 | }
47 | })
48 | }
49 |
50 | countdown(seconds: number, callback: (counter: number) => void) {
51 | let count = seconds;
52 | const interval = setInterval(() => {
53 | if (count === 0) {
54 | clearInterval(interval);
55 | LogUtils.d("Finished");
56 | return;
57 | }
58 | count--
59 | callback(count)
60 | }, 1000);
61 | }
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/entry/src/main/ets/bridge/JsBridgeNamespace.ets:
--------------------------------------------------------------------------------
1 | import { LogUtils } from '../utils/LogUtils';
2 | import { CompleteHandler, JavaScriptInterface } from '@hzw/ohos-dsbridge';
3 |
4 | export class JsBridgeNamespace {
5 | private cHandler?: CompleteHandler
6 |
7 | /**
8 | * 同步模版
9 | * @param p
10 | * @returns
11 | */
12 | @JavaScriptInterface(false)
13 | testSync(p: string): string {
14 | LogUtils.d("namespace testSync: " + JSON.stringify(p))
15 | return "namespace: 原生同步testSync方法返回的数据"
16 | }
17 | /**
18 | * 异步模版
19 | * @param p
20 | * @param handler
21 | */
22 | @JavaScriptInterface()
23 | testAsync(p: string, handler: CompleteHandler) {
24 | LogUtils.d("namespace testAsync: " + JSON.stringify(p))
25 | this.cHandler = handler
26 | this.countdown(5, (time:number) => {
27 | if (time === 0) {
28 | handler.complete("namespace: 原生异步testAsync方法返回的数据--结束")
29 | handler.complete("namespace: 原生异步testAsync方法返回的数据--结束2") // 不会被调用 会报错 Uncaught ReferenceError: xxx is not defined
30 | } else {
31 | handler.setProgressData("namespace: 原生异步testAsync方法返回的数据--" + time)
32 | }
33 | })
34 |
35 |
36 | }
37 |
38 | countdown(seconds: number, callback: (counter: number) => void) {
39 | let count = seconds;
40 | const interval = setInterval(() => {
41 | if (count === 0) {
42 | clearInterval(interval);
43 | LogUtils.d("Finished");
44 | return;
45 | }
46 | count--
47 | callback(count)
48 | }, 1000);
49 | }
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/entry/src/main/ets/entryability/EntryAbility.ets:
--------------------------------------------------------------------------------
1 | import UIAbility from '@ohos.app.ability.UIAbility';
2 | import hilog from '@ohos.hilog';
3 | import window from '@ohos.window';
4 | import { AbilityConstant, Want } from '@kit.AbilityKit';
5 |
6 | export default class EntryAbility extends UIAbility {
7 | onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
8 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
9 | }
10 |
11 | onDestroy() {
12 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
13 | }
14 |
15 | onWindowStageCreate(windowStage: window.WindowStage) {
16 | // Main window is created, set main page for this ability
17 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
18 |
19 | windowStage.loadContent('pages/Index', (err, data) => {
20 | if (err.code) {
21 | hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
22 | return;
23 | }
24 | hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
25 | });
26 | }
27 |
28 | onWindowStageDestroy() {
29 | // Main window is destroyed, release UI related resources
30 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
31 | }
32 |
33 | onForeground() {
34 | // Ability has brought to foreground
35 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
36 | }
37 |
38 | onBackground() {
39 | // Ability has back to background
40 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/entry/src/main/ets/pages/Index.ets:
--------------------------------------------------------------------------------
1 | import router from '@ohos.router'
2 | @Entry
3 | @Component
4 | struct Index {
5 | @State message: string = 'Hello World'
6 |
7 | build() {
8 | Row() {
9 | Column() {
10 | Button('NativeAndJsCalls - DSBridge3.0')
11 | .onClick(() => {
12 | router.pushUrl({ url: "pages/NativeAndJsCallsPage" })
13 | })
14 |
15 | Button('UseInComponent - DSBridge3.0')
16 | .margin('10vp')
17 | .onClick(() => {
18 | router.pushUrl({ url: "pages/UseInComponentsPage" })
19 | })
20 |
21 | Button('UseInJs2 - 适配DSBridge2.0脚本')
22 | .margin('10vp')
23 | .onClick(() => {
24 | router.pushUrl({ url: "pages/UseInJs2Page" })
25 | })
26 |
27 | Button("加载h5").onClick((event: ClickEvent) => {
28 | router.pushUrl({ url: "pages/TestPage" })
29 | })
30 | }
31 | .width('100%')
32 | }
33 | .height('100%')
34 | }
35 | }
--------------------------------------------------------------------------------
/entry/src/main/ets/pages/NativeAndJsCallsPage.ets:
--------------------------------------------------------------------------------
1 | import { LogUtils } from '../utils/LogUtils'
2 |
3 | import { JavaScriptInterface, WebViewControllerProxy } from '@hzw/ohos-dsbridge'
4 | import { JsBridge } from '../bridge/JsBridge'
5 | import { JsBridgeNamespace } from '../bridge/JsBridgeNamespace'
6 | import { promptAction } from '@kit.ArkUI'
7 |
8 | @Component
9 | @Entry
10 | struct NativeAndJsCallsPage {
11 | private controller: WebViewControllerProxy = WebViewControllerProxy.createController()
12 | private url: string = "https://mp.weixin.qq.com/s/kAtQ98mhdbYheo-SIIaFOQ"
13 | private localPath = $rawfile('index.html')
14 | @State isLoading: boolean = true
15 | @State msg: string = ""
16 |
17 | aboutToAppear() {
18 | this.controller.addJavascriptObject(new JsBridge())
19 | this.controller.addJavascriptObject(new JsBridgeNamespace(), "namespace")
20 | // this.controller.addJavascriptObject(this)
21 | this.controller.setClosePageListener(() => {
22 | return true; // false 会拦截关闭页面
23 | })
24 | this.controller.setGlobalErrorMessageListener((err: string) => {
25 | promptAction.showDialog({title:`err: ${err}`})
26 | })
27 | }
28 |
29 | @JavaScriptInterface(false)
30 | testNoArg(): boolean {
31 | LogUtils.d('testNoArg')
32 | return true
33 | }
34 |
35 |
36 | aboutToDisappear() {
37 | this.controller.destroy()
38 | }
39 |
40 | build() {
41 | // 注意需要请求网络权限
42 | Column() {
43 | Stack() {
44 | Web({ src: this.localPath, controller: this.controller.getWebViewController() })
45 | .javaScriptAccess(true)
46 | .javaScriptProxy(this.controller.javaScriptProxy)
47 | .onAlert((event) => {
48 | // AlertDialog.show({ message: event.message })
49 | return false
50 | })
51 | .onProgressChange((event) => {
52 | if (event?.newProgress == 100) {
53 | this.isLoading = false
54 | }
55 | })
56 | .onConsole((event) => {
57 | LogUtils.d('getMessage:' + event?.message.getMessage());
58 | return false;
59 | })
60 | .width('100%')
61 | .height('100%')
62 | .backgroundColor("#ffeef5ee")
63 |
64 | if (this.isLoading) {
65 | LoadingProgress()
66 | .width(40)
67 | .height(40)
68 | }
69 |
70 | }
71 | .alignContent(Alignment.Center)
72 | .width('100%')
73 | .height('70%')
74 |
75 | Column() {
76 | Text(this.msg ? "js返回的数据: " + this.msg : "")
77 | .padding(20)
78 | Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Center }) {
79 | Button("调用js函数-同步")
80 | .margin({ top: 10 })
81 | .onClick(() => {
82 | this.controller.callJs("showAlert", [1, 2, '666'], (v: string) => {
83 | this.msg = v + ""
84 | })
85 | })
86 |
87 | Button("调用js函数-异步")
88 | .margin({ top: 10 })
89 | .onClick(() => {
90 | this.controller.callJs("showAlertAsync", [1, 2, '666'], (v: string) => {
91 | this.msg = v + ""
92 | })
93 | })
94 |
95 | Button("检测js注册的函数1")
96 | .margin({ top: 10 })
97 | .onClick(async () => {
98 | let has = await this.controller.hasJavascriptMethod("showAlert")
99 | this.msg = `showAlert: ${has}`
100 | })
101 | Button("检测js注册的函数2")
102 | .margin({ top: 10 })
103 | .onClick(async () => {
104 | let has = await this.controller.hasJavascriptMethod("showAlertAy")
105 | this.msg = `showAlertAy: ${has}`
106 | })
107 |
108 | Button("命名空间:调用js函数-同步")
109 | .margin({ top: 10 })
110 | .onClick(async () => {
111 | this.controller.callJs("sync.test", [1, 2], (value: string) => {
112 | this.msg = value
113 | })
114 | })
115 |
116 | Button("命名空间:调用js函数-异步")
117 | .margin({ top: 10 })
118 | .onClick(async () => {
119 | this.controller.callJs("asny.test", [3, 2], (value: string) => {
120 | this.msg = value
121 | })
122 | })
123 |
124 | Button("backward").onClick((event: ClickEvent) => {
125 | this.controller.getWebViewController().backward()
126 | })
127 |
128 | }
129 | .width('100%')
130 | }
131 | .width('100%')
132 | .alignItems(HorizontalAlign.Center)
133 |
134 | }
135 | .width('100%')
136 | .height('100%')
137 | }
138 | }
139 |
140 |
--------------------------------------------------------------------------------
/entry/src/main/ets/pages/TestPage.ets:
--------------------------------------------------------------------------------
1 | import { LogUtils } from '../utils/LogUtils'
2 |
3 | import { CompleteHandler, JavaScriptInterface, WebViewControllerProxy } from '@hzw/ohos-dsbridge'
4 | import { JsBridge } from '../bridge/JsBridge'
5 | import { webview } from '@kit.ArkWeb'
6 | import { promptAction, router } from '@kit.ArkUI'
7 | import { JsBridge2 } from '../bridge/JsBridge2'
8 |
9 | /**
10 | * @author: HZWei
11 | * @date: 2024/11/20
12 | * @desc:
13 | */
14 | @Component
15 | @Entry
16 | struct TestPage {
17 | private controller: WebViewControllerProxy = WebViewControllerProxy.createController()
18 | private url: string = "https://www.harmonyos.com/"
19 | @State isLoading: boolean = true
20 | @State msg: string = ""
21 |
22 | aboutToAppear() {
23 |
24 | // 开启调试模式
25 | webview.WebviewController.setWebDebuggingAccess(true);
26 | }
27 |
28 |
29 |
30 |
31 | aboutToDisappear() {
32 | this.controller.destroy()
33 | }
34 |
35 | build() {
36 | Column({space:20}) {
37 | Stack() {
38 | Web({ src: this.url, controller: this.controller.getWebViewController() })
39 | .javaScriptAccess(true)
40 | .javaScriptProxy(this.controller.javaScriptProxy)
41 | .onAlert((event) => {
42 | return false
43 | })
44 | .onProgressChange((event) => {
45 | if (event?.newProgress == 100) {
46 | this.isLoading = false
47 | }
48 | })
49 | .onConsole((event) => {
50 | LogUtils.d('getMessage:' + event?.message.getMessage());
51 | return false;
52 | })
53 | .width('100%')
54 | .height('100%')
55 | .backgroundColor("#ffeef5ee")
56 |
57 | if (this.isLoading) {
58 | LoadingProgress()
59 | .width(40)
60 | .height(40)
61 | }
62 |
63 | }
64 | .alignContent(Alignment.Center)
65 | .width('100%')
66 | .height('70%')
67 |
68 | Text(this.msg)
69 | .fontSize(18)
70 | .padding(10)
71 |
72 | Button("back").onClick((event: ClickEvent) => {
73 | if (this.controller.getWebViewController().accessBackward()){
74 | this.controller.getWebViewController().backward()
75 | }else {
76 | router.back()
77 | }
78 | })
79 |
80 |
81 |
82 | }
83 | .width('100%')
84 | .height('100%')
85 | }
86 | }
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/entry/src/main/ets/pages/UseInComponentsPage.ets:
--------------------------------------------------------------------------------
1 | import { LogUtils } from '../utils/LogUtils'
2 |
3 | import { CompleteHandler, JavaScriptInterface, WebViewControllerProxy } from '@hzw/ohos-dsbridge'
4 | import { JsBridge } from '../bridge/JsBridge'
5 | import { webview } from '@kit.ArkWeb'
6 | import { promptAction } from '@kit.ArkUI'
7 |
8 | /**
9 | * @author: HZWei
10 | * @date: 2024/6/24
11 | * @desc: 在组件中定义桥接函数示例
12 | */
13 |
14 | @Component
15 | @Entry
16 | struct UseInComponentsPage {
17 | private controller: WebViewControllerProxy = WebViewControllerProxy.createController()
18 | private url: string = "https://www.harmonyos.com/"
19 | private localPath = $rawfile('use-in-components.html')
20 | @State isLoading: boolean = true
21 | @State msg: string = ""
22 |
23 | aboutToAppear() {
24 | // 在一个组件内只能存在一个无命名空间类
25 | this.controller.addJavascriptObject(this)
26 | this.controller.setGlobalErrorMessageListener((err: string) => {
27 | promptAction.showDialog({title:`${err}`})
28 | })
29 |
30 |
31 | // 开启调试模式
32 | webview.WebviewController.setWebDebuggingAccess(true);
33 | }
34 |
35 | @JavaScriptInterface(false)
36 | testComponentSync(args: string): string {
37 | return `组件中的同步方法: ${args}`
38 | }
39 |
40 | @JavaScriptInterface()
41 | testComponentAsync(args: string, handler: CompleteHandler) {
42 | handler.complete(`组件中的异步方法: ${args}`)
43 | }
44 |
45 | aboutToDisappear() {
46 | this.controller.destroy()
47 | }
48 |
49 | build() {
50 | Column() {
51 | Stack() {
52 | Web({ src: this.localPath, controller: this.controller.getWebViewController() })
53 | .javaScriptAccess(true)
54 | .javaScriptProxy(this.controller.javaScriptProxy)
55 | .onAlert((event) => {
56 | return false
57 | })
58 | .onProgressChange((event) => {
59 | if (event?.newProgress == 100) {
60 | this.isLoading = false
61 | }
62 | })
63 | .onConsole((event) => {
64 | LogUtils.d('getMessage:' + event?.message.getMessage());
65 | return false;
66 | })
67 | .width('100%')
68 | .height('100%')
69 | .backgroundColor("#ffeef5ee")
70 |
71 | if (this.isLoading) {
72 | LoadingProgress()
73 | .width(40)
74 | .height(40)
75 | }
76 |
77 | }
78 | .alignContent(Alignment.Center)
79 | .width('100%')
80 | .height('70%')
81 |
82 | }
83 | .width('100%')
84 | .height('100%')
85 | }
86 | }
87 |
88 |
--------------------------------------------------------------------------------
/entry/src/main/ets/pages/UseInJs2Page.ets:
--------------------------------------------------------------------------------
1 | import { LogUtils } from '../utils/LogUtils'
2 |
3 | import { CompleteHandler, JavaScriptInterface, WebViewControllerProxy } from '@hzw/ohos-dsbridge'
4 | import { JsBridge } from '../bridge/JsBridge'
5 | import { webview } from '@kit.ArkWeb'
6 | import { promptAction } from '@kit.ArkUI'
7 | import { JsBridge2 } from '../bridge/JsBridge2'
8 |
9 | /**
10 | * @author: HZWei
11 | * @date: 2024/7/6
12 | * @desc: 测试dsBridge2的js
13 | */
14 |
15 | @Component
16 | @Entry
17 | struct UseInJs2Page {
18 | private controller: WebViewControllerProxy = WebViewControllerProxy.createController()
19 | private url: string = "https://www.harmonyos.com/"
20 | private localPath = $rawfile('use-in-js2.html')
21 | @State isLoading: boolean = true
22 | @State msg: string = ""
23 |
24 | aboutToAppear() {
25 | // 如果是使用DSBridge2.0 ,调用supportDS2方法
26 | this.controller.supportDS2(true)
27 | // 以下两种注册方式都可以,任选其一
28 | this.controller.addJavascriptObject(this)
29 | // this.controller.addJavascriptObject(new JsBridge2())
30 |
31 |
32 | // DS2.0脚本不支持API命令空间 xx
33 | // this.controller.addJavascriptObject(new JsBridge2(),'js2')
34 | // 开启调试模式
35 | webview.WebviewController.setWebDebuggingAccess(true);
36 |
37 | this.controller.setGlobalErrorMessageListener((err: string) => {
38 | promptAction.showDialog({title:`${err}`})
39 | })
40 |
41 | }
42 |
43 | @JavaScriptInterface(false)
44 | testComponentSync(args: string): Object {
45 | const jsParam = JSON.parse(args) as JsParam
46 | LogUtils.d(jsParam.msg ?? '')
47 | // return `组件中的同步方法: ${jsParam.msg}`
48 | return `${jsParam.msg}`
49 | }
50 |
51 | @JavaScriptInterface(false)
52 | testNoArgSync(): number {
53 | return 1 + 2
54 | }
55 |
56 | @JavaScriptInterface()
57 | testComponentAsync(args: string, handler: CompleteHandler) {
58 | // LogUtils.d(args)
59 | const jsParam = JSON.parse(args) as JsParam
60 | handler.complete(`组件中的异步方法: ${jsParam.msg}`)
61 | }
62 |
63 | @JavaScriptInterface()
64 | testCallNoArgAsync(handler: CompleteHandler) {
65 | handler.complete(`无参异步方法返回值: 100`)
66 | }
67 |
68 | @JavaScriptInterface()
69 | testProgressAsync(handler: CompleteHandler) {
70 | this.countdown(5, (time: number) => {
71 | if (time === 0) {
72 | handler.complete("原生异步方法返回的数据--结束")
73 | handler.complete("原生异步方法返回的数据--结束2") // 不会被调用 会报错 Uncaught ReferenceError: xxx is not defined
74 | } else {
75 | handler.setProgressData("原生异步方法返回的数据--" + time)
76 | }
77 | })
78 | }
79 |
80 | countdown(seconds: number, callback: (counter: number) => void) {
81 | let count = seconds;
82 | const interval = setInterval(() => {
83 | if (count === 0) {
84 | clearInterval(interval);
85 | LogUtils.d("Finished");
86 | return;
87 | }
88 | count--
89 | callback(count)
90 | }, 1000);
91 | }
92 |
93 | aboutToDisappear() {
94 | this.controller.destroy()
95 | }
96 |
97 | build() {
98 | Column() {
99 | Stack() {
100 | Web({ src: this.localPath, controller: this.controller.getWebViewController() })
101 | .javaScriptAccess(true)
102 | .javaScriptProxy(this.controller.javaScriptProxy)
103 | .onAlert((event) => {
104 | return false
105 | })
106 | .onProgressChange((event) => {
107 | if (event?.newProgress == 100) {
108 | this.isLoading = false
109 | }
110 | })
111 | .onConsole((event) => {
112 | LogUtils.d('getMessage:' + event?.message.getMessage());
113 | return false;
114 | })
115 | .width('100%')
116 | .height('100%')
117 | .backgroundColor("#ffeef5ee")
118 |
119 | if (this.isLoading) {
120 | LoadingProgress()
121 | .width(40)
122 | .height(40)
123 | }
124 |
125 | }
126 | .alignContent(Alignment.Center)
127 | .width('100%')
128 | .height('70%')
129 |
130 | Text(this.msg)
131 | .fontSize(18)
132 | .padding(10)
133 |
134 | Button("callJs").onClick((event: ClickEvent) => {
135 | this.controller.callHandler('addValue', [1, 2], (r: number) => {
136 | this.msg = r.toString()
137 | })
138 | })
139 |
140 | Button("callJs2").onClick((event: ClickEvent) => {
141 | this.controller.callHandlerNoParam('addValue2', () => {
142 | this.msg = 'addValue2'
143 | })
144 | })
145 |
146 | }
147 | .width('100%')
148 | .height('100%')
149 | }
150 | }
151 |
152 | export class JsParam {
153 | msg?: string
154 | }
155 |
156 |
--------------------------------------------------------------------------------
/entry/src/main/ets/utils/LogUtils.ets:
--------------------------------------------------------------------------------
1 | import hilog from '@ohos.hilog'
2 |
3 | export class LogUtils{
4 | private static readonly LOG_TAG: string = "LogUtils"
5 |
6 | static d(msg: string) {
7 | hilog.debug(0x0001, LogUtils.LOG_TAG, msg)
8 | }
9 |
10 | static e(msg: string) {
11 | hilog.error(0x0001, LogUtils.LOG_TAG, msg)
12 | }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/entry/src/main/module.json5:
--------------------------------------------------------------------------------
1 | {
2 | "module": {
3 | "name": "entry",
4 | "type": "entry",
5 | "description": "$string:module_desc",
6 | "mainElement": "EntryAbility",
7 | "deviceTypes": [
8 | "phone"
9 | ],
10 | "deliveryWithInstall": true,
11 | "installationFree": false,
12 | "pages": "$profile:main_pages",
13 | "requestPermissions": [{
14 | "name": "ohos.permission.INTERNET",
15 | }],
16 | "abilities": [
17 | {
18 | "name": "EntryAbility",
19 | "srcEntry": "./ets/entryability/EntryAbility.ets",
20 | "description": "$string:EntryAbility_desc",
21 | "icon": "$media:icon",
22 | "label": "$string:EntryAbility_label",
23 | "startWindowIcon": "$media:icon",
24 | "startWindowBackground": "$color:start_window_background",
25 | "exported": true,
26 | "skills": [
27 | {
28 | "entities": [
29 | "entity.system.home"
30 | ],
31 | "actions": [
32 | "action.system.home"
33 | ]
34 | }
35 | ]
36 | }
37 | ]
38 | }
39 | }
--------------------------------------------------------------------------------
/entry/src/main/resources/base/element/color.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": [
3 | {
4 | "name": "start_window_background",
5 | "value": "#FFFFFF"
6 | }
7 | ]
8 | }
--------------------------------------------------------------------------------
/entry/src/main/resources/base/element/string.json:
--------------------------------------------------------------------------------
1 | {
2 | "string": [
3 | {
4 | "name": "module_desc",
5 | "value": "module description"
6 | },
7 | {
8 | "name": "EntryAbility_desc",
9 | "value": "description"
10 | },
11 | {
12 | "name": "EntryAbility_label",
13 | "value": "dsbrige-demo"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/entry/src/main/resources/base/media/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-HarmonyOS/7515ee4037c91959c4a7e2244802cf67a3e3a7c3/entry/src/main/resources/base/media/icon.png
--------------------------------------------------------------------------------
/entry/src/main/resources/base/profile/main_pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "src": [
3 | "pages/Index",
4 | "pages/NativeAndJsCallsPage",
5 | "pages/UseInComponentsPage",
6 | "pages/UseInJs2Page",
7 | "pages/TestPage"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/entry/src/main/resources/rawfile/dsBridge3.0.js:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | const bridge = {
3 | call: function (method, args, callback) {
4 | let params = {data: args === undefined ? null : args}
5 | if (callback != null && typeof callback == 'function') {
6 | if (!window.callID) {
7 | window.callID = 0
8 | }
9 | const callName = "dscall" + (window.callID++)
10 | window[callName] = callback
11 | params["_dscbstub"] = callName
12 | }
13 | let paramsStr = JSON.stringify(params)
14 | let res = ""
15 | if (window._dsbridge){
16 | res = window._dsbridge.call(method, paramsStr)
17 | }
18 | return JSON.parse(res).data
19 | },
20 | register: function (method, func, async) {
21 | if (window._dsaf && window._dsf){
22 | let obj = async ? window._dsaf : window._dsf
23 | obj[method] = func
24 | if (typeof func == "object") {
25 | obj._obs[method] = func;
26 | } else {
27 | obj[method] = func;
28 | }
29 | }
30 |
31 | },
32 | registerAsyn: function (method, func) {
33 | this.register(method, func, true)
34 | },
35 | registerAsync: function (method, func) {
36 | this.register(method, func, true)
37 | },
38 | hasNativeMethod: function (method) {
39 | return this.call("_dsb.hasNativeMethod", {name: method})
40 | },
41 | close: function () {
42 | this.call("_dsb.closePage")
43 | },
44 | };
45 |
46 | (function (){
47 | const manager = {
48 | _dsf: {
49 | _obs: {}
50 | },
51 | _dsaf: {
52 | _obs: {}
53 | },
54 | dsBridge: bridge,
55 | close: function () {
56 | bridge.close()
57 | },
58 | _handleMessageFromNative: function (info) {
59 |
60 | let arg = JSON.parse(info.data);
61 | let ret = {
62 | id: info.callbackId, complete: true
63 | }
64 | let f = this._dsf[info.method];
65 | let af = this._dsaf[info.method]
66 | let callSyn = function (f, ob) {
67 | ret.data = f.apply(ob, arg)
68 | bridge.call("_dsb.returnValue", ret)
69 | }
70 | let callAsync = function (f, ob) {
71 | arg.push(function (data, complete) {
72 | ret.data = data;
73 | ret.complete = complete !== false;
74 | bridge.call("_dsb.returnValue", ret)
75 | })
76 | f.apply(ob, arg)
77 | }
78 | if (f) {
79 | callSyn(f, this._dsf);
80 | } else if (af) {
81 | callAsync(af, this._dsaf);
82 | }else {
83 | // namespace
84 | let name = info.method.split('.');
85 | if (name.length<2) return;
86 | let method=name.pop();
87 | let namespace=name.join('.')
88 | let obs = this._dsf._obs;
89 | let ob = obs[namespace] || {};
90 | let m = ob[method];
91 | if (m && typeof m == "function") {
92 | callSyn(m, ob);
93 | return;
94 | }
95 | obs = this._dsaf._obs;
96 | ob = obs[namespace] || {};
97 | m = ob[method];
98 | if (m && typeof m == "function") {
99 | callAsync(m, ob);
100 | return;
101 | }
102 | }
103 | }
104 | }
105 |
106 | for (let attr in manager) {
107 | window[attr] = manager[attr]
108 | console.log(attr)
109 | }
110 |
111 | dsBridge.register("_hasJavascriptMethod", (method) => {
112 | const name = method.split('.')
113 | if (name.length < 2) {
114 | return !!(_dsf[name] || _dsaf[name])
115 | } else {
116 | // namespace
117 | let method = name.pop()
118 | let namespace = name.join('.')
119 | let ob = _dsf._obs[namespace] || _dsaf._obs[namespace]
120 | return ob && !!ob[method]
121 | }
122 | })
123 |
124 | })();
125 |
126 | // module.exports = dsBridge;
127 | // export default dsBridge;
128 |
129 |
--------------------------------------------------------------------------------
/entry/src/main/resources/rawfile/dsbridge2.0.js:
--------------------------------------------------------------------------------
1 | function getJsBridge() {
2 | window._dsf = window._dsf || {};
3 | return {
4 | call: function (method, args, cb) {
5 | var ret = "";
6 | if (typeof args == "function") {
7 | cb = args;
8 | args = {}
9 | }
10 | if (typeof cb == "function") {
11 | window.dscb = window.dscb || 0;
12 | var cbName = "dscb" + window.dscb++;
13 | window[cbName] = cb;
14 | args["_dscbstub"] = cbName
15 | }
16 | console.log('cb: ',JSON.stringify(args) , typeof cb == "function");
17 | args = JSON.stringify(args || {});
18 | if (window._dswk) {
19 |
20 | ret = prompt(window._dswk + method, args)
21 | } else {
22 | if (typeof _dsbridge == "function") {
23 | ret = _dsbridge(method, args)
24 | } else {
25 | ret = _dsbridge.call(method, args)
26 | }
27 | }
28 | return ret
29 | }, register: function (name, fun) {
30 | if (typeof name == "object") {
31 | Object.assign(window._dsf, name)
32 | } else {
33 | window._dsf[name] = fun
34 | }
35 | }
36 | }
37 | }
38 |
39 | window.dsBridge = getJsBridge();
40 |
--------------------------------------------------------------------------------
/entry/src/main/resources/rawfile/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
50 |
51 |
52 |
53 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
214 |
--------------------------------------------------------------------------------
/entry/src/main/resources/rawfile/new-page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
50 |
51 |
52 |
53 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
191 |
--------------------------------------------------------------------------------
/entry/src/main/resources/rawfile/use-in-components.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
50 |
51 |
52 |
53 |
56 |
57 |
58 |
59 |
60 |
61 |
80 |
--------------------------------------------------------------------------------
/entry/src/main/resources/rawfile/use-in-js2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
48 |
49 |
50 |
51 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
119 |
--------------------------------------------------------------------------------
/entry/src/ohosTest/ets/test/Ability.test.ets:
--------------------------------------------------------------------------------
1 | import hilog from '@ohos.hilog';
2 | import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
3 |
4 | export default function abilityTest() {
5 | describe('ActsAbilityTest', function () {
6 | // Defines a test suite. Two parameters are supported: test suite name and test suite function.
7 | beforeAll(function () {
8 | // Presets an action, which is performed only once before all test cases of the test suite start.
9 | // This API supports only one parameter: preset action function.
10 | })
11 | beforeEach(function () {
12 | // Presets an action, which is performed before each unit test case starts.
13 | // The number of execution times is the same as the number of test cases defined by **it**.
14 | // This API supports only one parameter: preset action function.
15 | })
16 | afterEach(function () {
17 | // Presets a clear action, which is performed after each unit test case ends.
18 | // The number of execution times is the same as the number of test cases defined by **it**.
19 | // This API supports only one parameter: clear action function.
20 | })
21 | afterAll(function () {
22 | // Presets a clear action, which is performed after all test cases of the test suite end.
23 | // This API supports only one parameter: clear action function.
24 | })
25 | it('assertContain',0, function () {
26 | // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
27 | hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
28 | let a = 'abc'
29 | let b = 'b'
30 | // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
31 | expect(a).assertContain(b)
32 | expect(a).assertEqual(a)
33 | })
34 | })
35 | }
--------------------------------------------------------------------------------
/entry/src/ohosTest/ets/test/List.test.ets:
--------------------------------------------------------------------------------
1 | import abilityTest from './Ability.test'
2 |
3 | export default function testsuite() {
4 | abilityTest()
5 | }
--------------------------------------------------------------------------------
/entry/src/ohosTest/ets/testability/TestAbility.ets:
--------------------------------------------------------------------------------
1 | import UIAbility from '@ohos.app.ability.UIAbility';
2 | import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
3 | import hilog from '@ohos.hilog';
4 | import { Hypium } from '@ohos/hypium';
5 | import testsuite from '../test/List.test';
6 | import window from '@ohos.window';
7 |
8 | export default class TestAbility extends UIAbility {
9 | onCreate(want, launchParam) {
10 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
11 | hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
12 | hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');
13 | var abilityDelegator: any
14 | abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
15 | var abilityDelegatorArguments: any
16 | abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
17 | hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
18 | Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)
19 | }
20 |
21 | onDestroy() {
22 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
23 | }
24 |
25 | onWindowStageCreate(windowStage: window.WindowStage) {
26 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
27 | windowStage.loadContent('testability/pages/Index', (err, data) => {
28 | if (err.code) {
29 | hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
30 | return;
31 | }
32 | hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',
33 | JSON.stringify(data) ?? '');
34 | });
35 | }
36 |
37 | onWindowStageDestroy() {
38 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
39 | }
40 |
41 | onForeground() {
42 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
43 | }
44 |
45 | onBackground() {
46 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
47 | }
48 | }
--------------------------------------------------------------------------------
/entry/src/ohosTest/ets/testability/pages/Index.ets:
--------------------------------------------------------------------------------
1 | import hilog from '@ohos.hilog';
2 |
3 | @Entry
4 | @Component
5 | struct Index {
6 | aboutToAppear() {
7 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');
8 | }
9 | @State message: string = 'Hello World'
10 | build() {
11 | Row() {
12 | Column() {
13 | Text(this.message)
14 | .fontSize(50)
15 | .fontWeight(FontWeight.Bold)
16 | Button() {
17 | Text('next page')
18 | .fontSize(20)
19 | .fontWeight(FontWeight.Bold)
20 | }.type(ButtonType.Capsule)
21 | .margin({
22 | top: 20
23 | })
24 | .backgroundColor('#0D9FFB')
25 | .width('35%')
26 | .height('5%')
27 | .onClick(()=>{
28 | })
29 | }
30 | .width('100%')
31 | }
32 | .height('100%')
33 | }
34 | }
--------------------------------------------------------------------------------
/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts:
--------------------------------------------------------------------------------
1 | import hilog from '@ohos.hilog';
2 | import TestRunner from '@ohos.application.testRunner';
3 | import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
4 |
5 | var abilityDelegator = undefined
6 | var abilityDelegatorArguments = undefined
7 |
8 | async function onAbilityCreateCallback() {
9 | hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
10 | }
11 |
12 | async function addAbilityMonitorCallback(err: any) {
13 | hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
14 | }
15 |
16 | export default class OpenHarmonyTestRunner implements TestRunner {
17 | constructor() {
18 | }
19 |
20 | onPrepare() {
21 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
22 | }
23 |
24 | async onRun() {
25 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
26 | abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
27 | abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
28 | var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility'
29 | let lMonitor = {
30 | abilityName: testAbilityName,
31 | onAbilityCreate: onAbilityCreateCallback,
32 | };
33 | abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
34 | var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName
35 | var debug = abilityDelegatorArguments.parameters['-D']
36 | if (debug == 'true')
37 | {
38 | cmd += ' -D'
39 | }
40 | hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd);
41 | abilityDelegator.executeShellCommand(cmd,
42 | (err: any, d: any) => {
43 | hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? '');
44 | hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? '');
45 | hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? '');
46 | })
47 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
48 | }
49 | }
--------------------------------------------------------------------------------
/entry/src/ohosTest/module.json5:
--------------------------------------------------------------------------------
1 | {
2 | "module": {
3 | "name": "entry_test",
4 | "type": "feature",
5 | "description": "$string:module_test_desc",
6 | "mainElement": "TestAbility",
7 | "deviceTypes": [
8 | "phone"
9 | ],
10 | "deliveryWithInstall": true,
11 | "installationFree": false,
12 | "pages": "$profile:test_pages",
13 | "abilities": [
14 | {
15 | "name": "TestAbility",
16 | "srcEntry": "./ets/testability/TestAbility.ets",
17 | "description": "$string:TestAbility_desc",
18 | "icon": "$media:icon",
19 | "label": "$string:TestAbility_label",
20 | "exported": true,
21 | "startWindowIcon": "$media:icon",
22 | "startWindowBackground": "$color:start_window_background",
23 | "skills": [
24 | {
25 | "actions": [
26 | "action.system.home"
27 | ],
28 | "entities": [
29 | "entity.system.home"
30 | ]
31 | }
32 | ]
33 | }
34 | ]
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/entry/src/ohosTest/resources/base/element/color.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": [
3 | {
4 | "name": "start_window_background",
5 | "value": "#FFFFFF"
6 | }
7 | ]
8 | }
--------------------------------------------------------------------------------
/entry/src/ohosTest/resources/base/element/string.json:
--------------------------------------------------------------------------------
1 | {
2 | "string": [
3 | {
4 | "name": "module_test_desc",
5 | "value": "test ability description"
6 | },
7 | {
8 | "name": "TestAbility_desc",
9 | "value": "the test ability"
10 | },
11 | {
12 | "name": "TestAbility_label",
13 | "value": "test label"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/entry/src/ohosTest/resources/base/media/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-HarmonyOS/7515ee4037c91959c4a7e2244802cf67a3e3a7c3/entry/src/ohosTest/resources/base/media/icon.png
--------------------------------------------------------------------------------
/entry/src/ohosTest/resources/base/profile/test_pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "src": [
3 | "testability/pages/Index"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/hvigor/hvigor-config.json5:
--------------------------------------------------------------------------------
1 | {
2 | "modelVersion": "5.0.0",
3 | "dependencies": {
4 | }
5 | }
--------------------------------------------------------------------------------
/hvigorfile.ts:
--------------------------------------------------------------------------------
1 | // Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
2 | export { appTasks } from '@ohos/hvigor-ohos-plugin';
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /oh_modules
3 | /.preview
4 | /build
5 | /.cxx
6 | /.test
--------------------------------------------------------------------------------
/library/BuildProfile.ets:
--------------------------------------------------------------------------------
1 | /**
2 | * Use these variables when you tailor your ArkTS code. They must be of the const type.
3 | */
4 | export const HAR_VERSION = '1.7.2';
5 | export const BUILD_MODE_NAME = 'debug';
6 | export const DEBUG = true;
7 | export const TARGET_NAME = 'default';
8 |
9 | /**
10 | * BuildProfile Class is used only for compatibility purposes.
11 | */
12 | export default class BuildProfile {
13 | static readonly HAR_VERSION = HAR_VERSION;
14 | static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
15 | static readonly DEBUG = DEBUG;
16 | static readonly TARGET_NAME = TARGET_NAME;
17 | }
--------------------------------------------------------------------------------
/library/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | ## 版本更新记录
3 |
4 | ### 2024//11/15 - 1.7.2
5 |
6 | - 对h5与原生端方法注册不一致的问题进行安全校验。
7 |
8 | ### 2024//10/8 - 1.7.1
9 |
10 | - 原生异步桥接函数支持无参定义;调整代码结构。
11 |
12 | ### 2024/9/27 - 1.7.0
13 |
14 | - 新增异常信息监听;
15 | - 命名空间注册新增校验检测。
16 |
17 | ### 2024/7/8 - 1.6.0
18 |
19 | - 兼容DSBridge2.0 JS脚本
20 |
21 | ### 2024/6/13 - 1.5.2
22 |
23 | - 取消报错toast,由业务自行处理
24 |
25 | ### 2024/5/27 - 1.5.1
26 |
27 | - 修复#12
28 |
29 | ### 2024/5/21 - 1.5.0
30 |
31 | - 修复#8、#11问题;
32 | - 原生同步方法内支持异步串行并发任务,根据鸿蒙特性设计的需求。
33 |
34 | ### 2024/4/19 - 1.3.0
35 |
36 | - 修复#5 、#7问题
37 |
38 | ### 2024/4/6 - 1.2.0
39 |
40 | - 重构适配鸿蒙NEXT版本;
41 | - 支持命名空间API。
42 |
43 | ### 2024/1/14 - 1.1.0
44 |
45 | - 支持检测API是否存在;
46 | - 支持监听JavaScript关闭页面;
47 | - 修复退出页面因异步任务闪退问题。
48 |
49 | ### 2024/1/7 - 1.0.0
50 |
51 | - 鸿蒙原生与JS的桥接交互,兼容现有Android和iOS的dsBridge库的核心功能。
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/library/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
--------------------------------------------------------------------------------
/library/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## 介绍
3 |
4 | HarmonyOS版的DSBridge,通过本库可以在鸿蒙原生与JavaScript完成交互,可以相互调用彼此的功能。
5 |
6 | 目前兼容Android、iOS第三方DSBridge库的核心功能,基本保持原来的使用方式,后续会持续迭代保持与DSBridge库相同的功能,减少前端和客户端的适配工作,另外也会根据鸿蒙的特性做一些定制需求。
7 |
8 | 特性:
9 |
10 | - **已适配鸿蒙NEXT版本;**
11 | - **支持原生同步方法内执行串行异步并发任务,同步等待异步结果,根据鸿蒙特点设计的需求;**
12 | - **兼容DSBridge2.0与3.0 JS脚本;**
13 | - 支持以类的方式集中统一管理API,也支持原生自定义页面组件直接注册使用;
14 | - 支持同步和异步调用;
15 | - 支持进度回调/回传:一次调用,多次返回;
16 | - 支持API是否存在检测;
17 | - 支持Javascript关闭页面的监听与拦截,
18 | - 支持异常信息监听;
19 | - 支持命名空间API。
20 |
21 |
22 |
23 | DSBridge-HarmonyOS已上架录入到[华为鸿蒙生态伙伴组件专区](https://developer.huawei.com/consumer/cn/market/landing/component)
24 |
25 | 
26 |
27 |
28 |
29 | 源码:
30 |
31 | * github:[DSBridge-HarmonyOS](https://github.com/751496032/DSBridge-HarmonyOS)
32 | * gitee: [DSBridge-HarmonyOS](https://gitee.com/common-apps/dsbrigde-harmony-os)
33 | * [DSBridge-Android](https://github.com/wendux/DSBridge-Android)
34 | * [DSBridge-IOS](https://github.com/wendux/DSBridge-IOS)
35 |
36 |
37 | >由于DSBridge库作者已停止维护,Android端建议使用 https://github.com/751496032/DSBridge-Android ,目前本人在维护。
38 |
39 |
40 | ## 安装
41 |
42 | 安装库:
43 |
44 | ```text
45 | ohpm install @hzw/ohos-dsbridge
46 | ```
47 |
48 |
49 | ## 基本用法
50 |
51 | ### ArkTS原生侧
52 |
53 | 1、在原生新建一个类`JsBridge`,实现业务API
54 | , 通过类来集中统一管理API,方法用`@JavaScriptInterface()`标注,是不是很眼熟呢,加一个`@JavaScriptInterface()`标注主要为了使用规范,是自定义的装饰器,与Android保持一致性。
55 | ```typescript
56 | export class JsBridge{
57 | private cHandler: CompleteHandler = null
58 |
59 | /**
60 | * 同步
61 | * @param p
62 | * @returns
63 | */
64 | @JavaScriptInterface(false)
65 | testSync(p: string): string {
66 | LogUtils.d("testSync: " + JSON.stringify(p))
67 | return "hello native"
68 | }
69 |
70 | /**
71 | * 异步
72 | * @param p
73 | * @param handler
74 | */
75 | @JavaScriptInterface()
76 | testAsync(p: string, handler: CompleteHandler) {
77 | LogUtils.d("testAsync: " + JSON.stringify(p))
78 | this.cHandler = handler
79 | }
80 | }
81 | ```
82 |
83 | 如果你不希望用一个类来管理API接口,可以在自定义页面组件Component中直接注册使用,然后在组件内定义API接口。
84 |
85 | ```typescript
86 | @Component
87 | @Entry
88 | struct UseInComponentsPage{
89 | aboutToAppear()
90 | {
91 | // 在一个组件内只能存在一个无命名空间
92 | this.controller.addJavascriptObject(this)
93 |
94 | }
95 |
96 | @JavaScriptInterface(false)
97 | testComponentSync(args: string): string {
98 | return `组件中的同步方法: ${args}`
99 | }
100 |
101 | @JavaScriptInterface()
102 | testComponentAsync(args: string, handler: CompleteHandler) {
103 | handler.complete(`组件中的异步方法: ${args}`)
104 | }
105 |
106 | }
107 |
108 |
109 | ```
110 |
111 | **API同步方法是不支持用async/await声明,如果需要在同步方法内执行异步任务,可以使用`taskWait()`函数来加持完成,下面会介绍基本用法**;异步方法的形参`CompleteHandler`,可用于结果异步回调。
112 |
113 | 2、在原生Web组件初始化时,通过`WebViewControllerProxy`类来获取`WebviewController`实例来实现JS注入,然后将其关联到Web组件中,接着将API管理类(JsBridge)关联到`WebViewControllerProxy`中。
114 |
115 | ```typescript
116 | private controller: WebViewControllerProxy = WebViewControllerProxy.createController()
117 |
118 | aboutToAppear() {
119 | this.controller.addJavascriptObject(new JsBridge())
120 | }
121 |
122 |
123 | Web({ src: this.localPath, controller: this.controller.getWebViewController() })
124 | .javaScriptAccess(true)
125 | .javaScriptProxy(this.controller.getJavaScriptProxy())
126 | .onAlert((event) => {
127 | // AlertDialog.show({ message: event.message })
128 | return false
129 | })
130 |
131 | )
132 |
133 | ```
134 |
135 | 3、通过`WebViewControllerProxy`调用JavaScript函数。
136 |
137 | ```typescript
138 | Button("调用js函数-同步")
139 | .onClick(() => {
140 | this.controller.callJs("showAlert", [1, 2, '666'], (v) => {
141 | this.msg = v + ""
142 | })
143 | })
144 |
145 | Button("调用js函数-异步")
146 | .onClick(() => {
147 | this.controller.callJs("showAlertAsync", [1, 2, '666'], (v) => {
148 | this.msg = v + ""
149 | })
150 | })
151 | }
152 | ```
153 |
154 | `callJs()`方法有三个形参,第一个是Js注册的函数名称,第二个是Js接收函数的参数,是一个数组类型,第三个是监听Js函数返回结果的函数。
155 | 另外也提供了与Android库一样调用函数`callHandler()`。
156 |
157 |
158 | 如果前端使用的是DSBridge2.0的JS脚本,可以通过supportDS2()方法来兼容,如下:
159 |
160 | ```typescript
161 | aboutToAppear() {
162 | // 如果是使用DSBridge2.0 ,调用supportDS2方法
163 | this.controller.supportDS2(true)
164 | // 以下两种注册方式都可以,任选其一
165 | this.controller.addJavascriptObject(this)
166 | // this.controller.addJavascriptObject(new JsBridge2())
167 | // DS2.0脚本不支持API命令空间
168 | // this.controller.addJavascriptObject(new JsBridge2(),'js2')
169 | // 开启调试模式
170 | webview.WebviewController.setWebDebuggingAccess(true);
171 | }
172 | ```
173 |
174 |
175 |
176 | ### JavaScript侧
177 |
178 | 1、在JavaScript中初始化dsBridge,通过cdn或者npm安装都可以。
179 |
180 | 如果项目没有历史包袱,建议直接用`m-dsbridge`包。
181 |
182 | ```
183 | npm i m-dsbridge
184 | // 或者cdn引入
185 |
186 | ```
187 |
188 | 也支持直接用原Android或iOS的[DSBridge库](https://github.com/wendux/DSBridge-Android)的JS脚本。
189 |
190 | ```typescript
191 | https://cdn.jsdelivr.net/npm/dsbridge/dist/dsbridge.js
192 | ```
193 |
194 | > 如果m-dsbridge 发生异常,建议切换到 https://cdn.jsdelivr.net/npm/dsbridge/dist/dsbridge.js
195 |
196 | 2、通过`dsBridge`对象注册Js函数,供原生调用。
197 |
198 | ```typescript
199 | // 注册同步函数
200 | dsBridge.register('showAlert', function (a, b, c) {
201 | // return "原生调用JS showAlert函数"
202 | alert("原生调用JS showAlert函数" + a + " " + b + " " + c)
203 | return true
204 | })
205 |
206 | // 注册异步函数
207 | dsBridge.registerAsyn('showAlertAsync', function (a, b, c, callback) {
208 | let counter = 0
209 | let id = setInterval(() => {
210 | if (counter < 5) {
211 | callback(counter, false)
212 | alert("原生调用JS showAlertAsync函数" + a + " " + b + " " + c + " " + counter)
213 | counter++
214 | } else {
215 | callback(counter, true)
216 | alert("原生调用JS showAlertAsync函数" + a + " " + b + " " + c + " " + counter)
217 | clearInterval(id)
218 | }
219 | }, 1000)
220 |
221 | })
222 | ```
223 |
224 | 其中异步的`callback`函数,如果最后一个参数返回`true`则完成整个链接的调用,`false`则可以一直回调给原生,这个就是JavaScript端的一次调用,多次返回。比如需要将JavaScript端进度数据不间断同步到原生,这时就可以派上用场了。
225 |
226 |
227 | 3、通过`dsBridge`对象调用原生API,第一个参数是原生方法名称,第二参数是原生方法接收的参数,异步方法有第三个参数是回调函数,会接收`CompleteHandler`异步回调结果。
228 |
229 | ```typescript
230 | // 同步
231 | let msg = dsBridge.call('testSync', JSON.stringify({data: 100}))
232 |
233 | // 异步, 参数可以是对象或者基本类型
234 | dsBridge.call('testAsync', JSON.stringify({data: 200}), (msg) => {
235 | updateMsg(msg)
236 | })
237 |
238 | // 如果你使用的是DSBridge 2.0脚本,异步调用的参数必须是一个对象
239 | dsBridge.call('testAsync', {data: 200}, (msg) => {
240 | updateMsg(msg)
241 | })
242 | ```
243 |
244 |
245 |
246 |
247 | ## 进度回调(一次调用,多次返回)
248 |
249 | 前面提到了JavaScript端的一次调用,多次回调的情况,在原生端也是支持的,还是有应用场景的,比如将原生的下载进度实时同步到js中,可以通过`CompleteHandler#setProgressData()`方法来实现。
250 |
251 | ```typescript
252 | @JavaScriptInterface()
253 | testAsync(p: string, handler: CompleteHandler) {
254 | LogUtils.d("testAsync: " + JSON.stringify(p))
255 | this.cHandler = handler
256 | let counter = 0
257 | setInterval(() => {
258 | if (counter < 5) {
259 | counter++
260 | handler.setProgressData("异步返回的数据--" + counter)
261 | } else {
262 | this.cHandler.complete("异步返回的数据--结束")
263 | this.cHandler.complete("异步返回的数据--结束2")
264 | }
265 | }, 1000)
266 | ```
267 | JavaScript:
268 |
269 | ```typescript
270 | dsBridge.call('testAsync', JSON.stringify({data: 200}), (msg) => {
271 | updateMsg(msg)
272 | })
273 | ```
274 |
275 | ## 监听或拦截Javascript关闭页面
276 |
277 | Js调用`close()`函数可以关闭当前页面,原生可以设置监听观察是否拦截。
278 |
279 | ```typescript
280 | aboutToAppear() {
281 | this.controller.setClosePageListener(() => {
282 | return true; // false 会拦截关闭页面
283 | })
284 | }
285 | ```
286 |
287 | 在回调函数中如果返回`false`,会拦截掉关闭页面的事件。
288 |
289 | ## 销毁结束任务
290 |
291 | 如果异步任务还在执行中,比如`setProgressData`,此时关闭页面返回就会闪退,为了避免这种情形,建议在组件的生命周期函数`aboutToDisappear()`中结束任务。
292 |
293 | ```typescript
294 | aboutToDisappear(){
295 | this.jsBridge.destroy()
296 | }
297 |
298 | ```
299 |
300 | ## 命名空间
301 |
302 | 命名空间可以帮助你更好的管理API,这在API数量多的时候非常实用,支持你通过命名空间将API分类管理,不同级之间只需用'.' 分隔即可。支持同步与异步方式使用。
303 |
304 |
305 | ### ArkTS API命令空间
306 |
307 | 原生用`WebViewControllerProxy#addJavascriptObject` 指定一个命名空间名称:
308 |
309 | ```typescript
310 | this.controller.addJavascriptObject(new JsBridgeNamespace(), "namespace")
311 | ```
312 |
313 |
314 | 在JavaScript中,用命名空间名称`.`对应的原生函数。
315 |
316 | ```javascript
317 | const callNative6 = () => {
318 | let msg = dsBridge.call('namespace.testSync',{msg:'来自js命名空间的数据'})
319 | updateMsg(msg)
320 | }
321 |
322 | const callNative7 = () => {
323 | dsBridge.call('namespace.testAsync', 'test', (msg) => {
324 | updateMsg(msg)
325 | })
326 | }
327 |
328 | ```
329 |
330 | ### JavaScript API命令空间
331 |
332 | 用dsBridge对象注册js函数的命名空间。
333 |
334 | ```javascript
335 | // namespace
336 | dsBridge.register('sync', {
337 | test: function (a, b) {
338 | return "namespace: " + (a + b)
339 | }
340 | })
341 |
342 | dsBridge.registerAsyn("asny",{
343 | test: function (a,b ,callback) {
344 | callback("namespace: " + (a + b))
345 | }
346 | })
347 | ```
348 |
349 | 第一个参数命名空间的名称,比如`sync`,第二个参数是API业务对象实例,支持字面量对象和Class类实例。
350 |
351 | 在原生调用方式:
352 |
353 | ```typescript
354 |
355 | this.controller.callJs("sync.test", [1, 2], (value: string) => {
356 | this.msg = value
357 | })
358 |
359 | this.controller.callJs("asny.test", [3, 2], (value: string) => {
360 | this.msg = value
361 | })
362 | ```
363 |
364 | ## 原生同步方法内执行串行异步并发任务
365 |
366 | 原生同步方法:
367 |
368 | ```typescript
369 | /**
370 | * 同步模版
371 | * @param p
372 | * @returns
373 | */
374 | @JavaScriptInterface(false)
375 | testSync(p: string): string {
376 | LogUtils.d("testSync: " + JSON.stringify(p))
377 | return "原生同步testSync方法返回的数据"
378 | }
379 | ```
380 |
381 | 如果要在同步方法内执行异步任务,并将异步结果立即返回给h5,上面的设计显然是无法满足需求的;在鸿蒙中异步任务基本与Promise和async/await有关联,然而桥接函数是不支持使用async/await声明(主要是受鸿蒙Web脚本注入机制的限制,也是为了考虑兼容Android/iOS项目而因此这样设计的)。
382 |
383 | 对此,设计了一个`taskWait()`函数来满足上述的需求,可以通过`taskWait()`函数在主线程的同步方法内执行串行并发异步任务,主线程会同步等待异步结果。
384 |
385 | ```typescript
386 | /**
387 | * 同步方法中执行异步并发任务
388 | * @param args
389 | * @returns
390 | */
391 | @JavaScriptInterface(false)
392 | testTaskWait(args: string): number {
393 | let p = new Param(100, 200)
394 | let p1 = new Param(100, 300)
395 | taskWait(p)
396 | p1.tag = "Param"
397 | taskWait(p1)
398 | LogUtils.d(`testTaskWait sum: ${p.sum} ${p1.sum}`)
399 | return p.sum + p1.sum
400 | }
401 |
402 | ```
403 |
404 | 其中`Param`类需要继承`BaseSendable`,同时用`@Sendable`装饰器声明,任务放在`run()`方法中执行。
405 |
406 | ```typescript
407 | @Sendable
408 | export class Param extends BaseSendable{
409 | private a : number = 0
410 | private b : number = 0
411 | public sum: number = 0
412 | public enableLog: boolean = true
413 |
414 | constructor(a: number, b: number) {
415 | super();
416 | this.a = a;
417 | this.b = b;
418 | }
419 | // 异步任务执行
420 | async run(): Promise {
421 | this.sum = await this.add()
422 | }
423 |
424 | async add(){
425 | return this.a + this.b
426 | }
427 |
428 |
429 | }
430 | ```
431 |
432 | `taskWait()`函数是一个轻量级的同步等待函数,不建议执行耗时过长的任务,如果在3s内没有完成任务,会自动结束等待将结果返回,可能会存在数据丢失的情况;对于特别耗时的任务建议使用异步桥接函数。
433 |
434 | ## 监听异常
435 |
436 | 可以通过setGlobalErrorMessageListener()方法来监听调用异常,如下:
437 |
438 |
439 | ```typescript
440 | this.controller.setGlobalErrorMessageListener((err: string) => {
441 | promptAction.showDialog({title:`${err}`})
442 | })
443 |
444 | ```
445 |
446 | ## 交流
447 |
448 | 如有疑问,请提issues, 或加v进群交流:751496032,备注鸿蒙
449 |
450 |
451 | ## 最后
452 |
453 | 感谢[@name718](https://github.com/name718)、[@fjc0k](https://github.com/fjc0k)等各位大佬的支持与反馈。欢迎大家多多反馈,一起支持完善鸿蒙生态。
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
--------------------------------------------------------------------------------
/library/build-profile.json5:
--------------------------------------------------------------------------------
1 | {
2 | "apiType": "stageMode",
3 | "buildOption": {
4 | // "artifactType": "original"
5 | },
6 | "targets": [
7 | {
8 | "name": "default",
9 | "output": {
10 | "artifactName": "dsbridge"
11 | }
12 | }
13 | ],
14 | }
--------------------------------------------------------------------------------
/library/example/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## 介绍
3 |
4 | HarmonyOS版的DSBridge,通过本库可以在鸿蒙原生与JavaScript完成交互,可以相互调用彼此的功能。
5 |
6 | 目前兼容Android、iOS第三方DSBridge库的核心功能,基本保持原来的使用方式,后续会持续迭代保持与DSBridge库相同的功能,减少前端和客户端的适配工作,另外也会根据鸿蒙的特性做一些定制需求。
7 |
8 | 特性:
9 |
10 | - **已适配鸿蒙NEXT版本;**
11 | - **支持原生同步方法内执行串行异步并发任务,同步等待异步结果,根据鸿蒙特点设计的需求;**
12 | - **兼容DSBridge2.0与3.0 JS脚本;**
13 | - 支持以类的方式集中统一管理API,也支持原生自定义页面组件直接注册使用;
14 | - 支持同步和异步调用;
15 | - 支持进度回调/回传:一次调用,多次返回;
16 | - 支持API是否存在检测;
17 | - 支持Javascript关闭页面的监听与拦截,
18 | - 支持命名空间API。
19 |
20 |
21 | 源码:
22 |
23 | * [DSBridge-HarmonyOS](https://github.com/751496032/DSBridge-HarmonyOS)
24 | * [DSBridge-Android](https://github.com/wendux/DSBridge-Android)
25 | * [DSBridge-IOS](https://github.com/wendux/DSBridge-IOS)
26 |
27 |
28 | >由于DSBridge库作者已停止维护,Android端建议使用 https://github.com/751496032/DSBridge-Android ,目前本人在维护。
29 |
30 |
31 | ## 安装
32 |
33 | 安装库:
34 |
35 | ```text
36 | ohpm install @hzw/ohos-dsbridge
37 | ```
38 |
39 | 或者安装本地har包
40 |
41 | ```text
42 | ohpm install ../libs/library.har
43 | ```
44 |
45 | ## 基本用法
46 |
47 | ### ArkTS原生侧
48 |
49 | 1、在原生新建一个类`JsBridge`,实现业务API
50 | , 通过类来集中统一管理API,方法用`@JavaScriptInterface()`标注,是不是很眼熟呢,加一个`@JavaScriptInterface()`标注主要为了使用规范,是自定义的装饰器,与Android保持一致性。
51 | ```typescript
52 | export class JsBridge{
53 | private cHandler: CompleteHandler = null
54 |
55 | /**
56 | * 同步
57 | * @param p
58 | * @returns
59 | */
60 | @JavaScriptInterface(false)
61 | testSync(p: string): string {
62 | LogUtils.d("testSync: " + JSON.stringify(p))
63 | return "hello native"
64 | }
65 |
66 | /**
67 | * 异步
68 | * @param p
69 | * @param handler
70 | */
71 | @JavaScriptInterface()
72 | testAsync(p: string, handler: CompleteHandler) {
73 | LogUtils.d("testAsync: " + JSON.stringify(p))
74 | this.cHandler = handler
75 | }
76 | }
77 | ```
78 |
79 | 如果你不希望用一个类来管理API接口,可以在自定义页面组件Component中直接注册使用,然后在组件内定义API接口。
80 |
81 | ```typescript
82 | @Component
83 | @Entry
84 | struct UseInComponentsPage{
85 | aboutToAppear()
86 | {
87 | // 在一个组件内只能存在一个无命名空间
88 | this.controller.addJavascriptObject(this)
89 |
90 | }
91 |
92 | @JavaScriptInterface(false)
93 | testComponentSync(args: string): string {
94 | return `组件中的同步方法: ${args}`
95 | }
96 |
97 | @JavaScriptInterface()
98 | testComponentAsync(args: string, handler: CompleteHandler) {
99 | handler.complete(`组件中的异步方法: ${args}`)
100 | }
101 |
102 | }
103 |
104 |
105 | ```
106 |
107 | **API同步方法是不支持用async/await声明,如果需要在同步方法内执行异步任务,可以使用`taskWait()`函数来加持完成,下面会介绍基本用法**;异步方法的形参`CompleteHandler`,可用于结果异步回调。
108 |
109 | 2、在原生Web组件初始化时,通过`WebViewControllerProxy`类来获取`WebviewController`实例来实现JS注入,然后将其关联到Web组件中,接着将API管理类(JsBridge)关联到`WebViewControllerProxy`中。
110 |
111 | ```typescript
112 | private controller: WebViewControllerProxy = WebViewControllerProxy.createController()
113 |
114 | aboutToAppear() {
115 | this.controller.addJavascriptObject(new JsBridge())
116 | }
117 |
118 |
119 | Web({ src: this.localPath, controller: this.controller.getWebViewController() })
120 | .javaScriptAccess(true)
121 | .javaScriptProxy(this.controller.getJavaScriptProxy())
122 | .onAlert((event) => {
123 | // AlertDialog.show({ message: event.message })
124 | return false
125 | })
126 |
127 | )
128 |
129 | ```
130 |
131 | 3、通过`WebViewControllerProxy`调用JavaScript函数。
132 |
133 | ```typescript
134 | Button("调用js函数-同步")
135 | .onClick(() => {
136 | this.controller.callJs("showAlert", [1, 2, '666'], (v) => {
137 | this.msg = v + ""
138 | })
139 | })
140 |
141 | Button("调用js函数-异步")
142 | .onClick(() => {
143 | this.controller.callJs("showAlertAsync", [1, 2, '666'], (v) => {
144 | this.msg = v + ""
145 | })
146 | })
147 | }
148 | ```
149 |
150 | `callJs()`方法有三个形参,第一个是Js注册的函数名称,第二个是Js接收函数的参数,是一个数组类型,第三个是监听Js函数返回结果的函数。
151 | 另外也提供了与Android库一样调用函数`callHandler()`。
152 |
153 |
154 | 如果前端使用的是DSBridge2.0的JS脚本,可以通过supportDS2()方法来兼容,如下:
155 |
156 | ```typescript
157 | aboutToAppear() {
158 | // 如果是使用DSBridge2.0 ,调用supportDS2方法
159 | this.controller.supportDS2(true)
160 | // 以下两种注册方式都可以,任选其一
161 | this.controller.addJavascriptObject(this)
162 | // this.controller.addJavascriptObject(new JsBridge2())
163 | // DS2.0脚本不支持API命令空间
164 | // this.controller.addJavascriptObject(new JsBridge2(),'js2')
165 | // 开启调试模式
166 | webview.WebviewController.setWebDebuggingAccess(true);
167 | }
168 | ```
169 |
170 |
171 |
172 | ### JavaScript侧
173 |
174 | 1、在JavaScript中初始化dsBridge,通过cdn或者npm安装都可以。
175 |
176 | 如果项目没有历史包袱,建议直接用`m-dsbridge`包。
177 |
178 | ```
179 | npm i m-dsbridge
180 | // 或者cdn引入
181 |
182 | ```
183 |
184 | 也支持直接用原Android或iOS的[DSBridge库](https://github.com/wendux/DSBridge-Android)的JS脚本。
185 |
186 | ```typescript
187 | https://cdn.jsdelivr.net/npm/dsbridge/dist/dsbridge.js
188 | ```
189 |
190 | 2、通过`dsBridge`对象注册Js函数,供原生调用。
191 |
192 | ```typescript
193 | // 注册同步函数
194 | dsBridge.register('showAlert', function (a, b, c) {
195 | // return "原生调用JS showAlert函数"
196 | alert("原生调用JS showAlert函数" + a + " " + b + " " + c)
197 | return true
198 | })
199 |
200 | // 注册异步函数
201 | dsBridge.registerAsyn('showAlertAsync', function (a, b, c, callback) {
202 | let counter = 0
203 | let id = setInterval(() => {
204 | if (counter < 5) {
205 | callback(counter, false)
206 | alert("原生调用JS showAlertAsync函数" + a + " " + b + " " + c + " " + counter)
207 | counter++
208 | } else {
209 | callback(counter, true)
210 | alert("原生调用JS showAlertAsync函数" + a + " " + b + " " + c + " " + counter)
211 | clearInterval(id)
212 | }
213 | }, 1000)
214 |
215 | })
216 | ```
217 |
218 | 其中异步的`callback`函数,如果最后一个参数返回`true`则完成整个链接的调用,`false`则可以一直回调给原生,这个就是JavaScript端的一次调用,多次返回。比如需要将JavaScript端进度数据不间断同步到原生,这时就可以派上用场了。
219 |
220 |
221 | 3、通过`dsBridge`对象调用原生API,第一个参数是原生方法名称,第二参数是原生方法接收的参数,异步方法有第三个参数是回调函数,会接收`CompleteHandler`异步回调结果。
222 |
223 | ```typescript
224 | // 同步
225 | let msg = dsBridge.call('testSync', JSON.stringify({data: 100}))
226 |
227 | // 异步
228 | dsBridge.call('testAsync', JSON.stringify({data: 200}), (msg) => {
229 | updateMsg(msg)
230 | })
231 | ```
232 |
233 |
234 |
235 |
236 | ## 进度回调(一次调用,多次返回)
237 |
238 | 前面提到了JavaScript端的一次调用,多次回调的情况,在原生端也是支持的,还是有应用场景的,比如将原生的下载进度实时同步到js中,可以通过`CompleteHandler#setProgressData()`方法来实现。
239 |
240 | ```typescript
241 | @JavaScriptInterface()
242 | testAsync(p: string, handler: CompleteHandler) {
243 | LogUtils.d("testAsync: " + JSON.stringify(p))
244 | this.cHandler = handler
245 | let counter = 0
246 | setInterval(() => {
247 | if (counter < 5) {
248 | counter++
249 | handler.setProgressData("异步返回的数据--" + counter)
250 | } else {
251 | this.cHandler.complete("异步返回的数据--结束")
252 | this.cHandler.complete("异步返回的数据--结束2")
253 | }
254 | }, 1000)
255 | ```
256 | JavaScript:
257 |
258 | ```typescript
259 | dsBridge.call('testAsync', JSON.stringify({data: 200}), (msg) => {
260 | updateMsg(msg)
261 | })
262 | ```
263 |
264 | ## 监听或拦截Javascript关闭页面
265 |
266 | Js调用`close()`函数可以关闭当前页面,原生可以设置监听观察是否拦截。
267 |
268 | ```typescript
269 | aboutToAppear() {
270 | this.controller.setClosePageListener(() => {
271 | return true; // false 会拦截关闭页面
272 | })
273 | }
274 | ```
275 |
276 | 在回调函数中如果返回`false`,会拦截掉关闭页面的事件。
277 |
278 | ## 销毁结束任务
279 |
280 | 如果异步任务还在执行中,比如`setProgressData`,此时关闭页面返回就会闪退,为了避免这种情形,建议在组件的生命周期函数`aboutToDisappear()`中结束任务。
281 |
282 | ```typescript
283 | aboutToDisappear(){
284 | this.jsBridge.destroy()
285 | }
286 |
287 | ```
288 |
289 | ## 命名空间
290 |
291 | 命名空间可以帮助你更好的管理API,这在API数量多的时候非常实用,支持你通过命名空间将API分类管理,不同级之间只需用'.' 分隔即可。支持同步与异步方式使用。
292 |
293 |
294 | ### ArkTS API命令空间
295 |
296 | 原生用`WebViewControllerProxy#addJavascriptObject` 指定一个命名空间名称:
297 |
298 | ```typescript
299 | this.controller.addJavascriptObject(new JsBridgeNamespace(), "namespace")
300 | ```
301 |
302 |
303 | 在JavaScript中,用命名空间名称`.`对应的原生函数。
304 |
305 | ```javascript
306 | const callNative6 = () => {
307 | let msg = dsBridge.call('namespace.testSync',{msg:'来自js命名空间的数据'})
308 | updateMsg(msg)
309 | }
310 |
311 | const callNative7 = () => {
312 | dsBridge.call('namespace.testAsync', 'test', (msg) => {
313 | updateMsg(msg)
314 | })
315 | }
316 |
317 | ```
318 |
319 | ### JavaScript API命令空间
320 |
321 | 用dsBridge对象注册js函数的命名空间。
322 |
323 | ```javascript
324 | // namespace
325 | dsBridge.register('sync', {
326 | test: function (a, b) {
327 | return "namespace: " + (a + b)
328 | }
329 | })
330 |
331 | dsBridge.registerAsyn("asny",{
332 | test: function (a,b ,callback) {
333 | callback("namespace: " + (a + b))
334 | }
335 | })
336 | ```
337 |
338 | 第一个参数命名空间的名称,比如`sync`,第二个参数是API业务对象实例,支持字面量对象和Class类实例。
339 |
340 | 在原生调用方式:
341 |
342 | ```typescript
343 |
344 | this.controller.callJs("sync.test", [1, 2], (value: string) => {
345 | this.msg = value
346 | })
347 |
348 | this.controller.callJs("asny.test", [3, 2], (value: string) => {
349 | this.msg = value
350 | })
351 | ```
352 |
353 | ## 原生同步方法内执行串行异步并发任务
354 |
355 | 原生同步方法:
356 |
357 | ```typescript
358 | /**
359 | * 同步模版
360 | * @param p
361 | * @returns
362 | */
363 | @JavaScriptInterface(false)
364 | testSync(p: string): string {
365 | LogUtils.d("testSync: " + JSON.stringify(p))
366 | return "原生同步testSync方法返回的数据"
367 | }
368 | ```
369 |
370 | 如果要在同步方法内执行异步任务,并将异步结果立即返回给h5,上面的设计显然是无法满足需求的;在鸿蒙中异步任务基本与Promise和async/await有关联,然而桥接函数是不支持使用async/await声明(主要是受鸿蒙Web脚本注入机制的限制,也是为了考虑兼容Android/iOS项目而因此这样设计的)。
371 |
372 | 对此,设计了一个`taskWait()`函数来满足上述的需求,可以通过`taskWait()`函数在主线程的同步方法内执行串行并发异步任务,主线程会同步等待异步结果。
373 |
374 | ```typescript
375 | /**
376 | * 同步方法中执行异步并发任务
377 | * @param args
378 | * @returns
379 | */
380 | @JavaScriptInterface(false)
381 | testTaskWait(args: string): number {
382 | let p = new Param(100, 200)
383 | let p1 = new Param(100, 300)
384 | taskWait(p)
385 | p1.tag = "Param"
386 | taskWait(p1)
387 | LogUtils.d(`testTaskWait sum: ${p.sum} ${p1.sum}`)
388 | return p.sum + p1.sum
389 | }
390 |
391 | ```
392 |
393 | 其中`Param`类需要继承`BaseSendable`,同时用`@Sendable`装饰器声明,任务放在`run()`方法中执行。
394 |
395 | ```typescript
396 | @Sendable
397 | export class Param extends BaseSendable{
398 | private a : number = 0
399 | private b : number = 0
400 | public sum: number = 0
401 | public enableLog: boolean = true
402 |
403 | constructor(a: number, b: number) {
404 | super();
405 | this.a = a;
406 | this.b = b;
407 | }
408 | // 异步任务执行
409 | async run(): Promise {
410 | this.sum = await this.add()
411 | }
412 |
413 | async add(){
414 | return this.a + this.b
415 | }
416 |
417 |
418 | }
419 | ```
420 |
421 | `taskWait()`函数是一个轻量级的同步等待函数,不建议执行耗时过长的任务,如果在3s内没有完成任务,会自动结束等待将结果返回,可能会存在数据丢失的情况;对于特别耗时的任务建议使用异步桥接函数。
422 |
423 |
424 |
425 |
426 | ## 最后
427 |
428 | 感谢[@name718](https://github.com/name718)、[@fjc0k](https://github.com/fjc0k)等各位大佬的支持与反馈。欢迎大家多多反馈,一起支持完善鸿蒙生态。
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
--------------------------------------------------------------------------------
/library/hvigorfile.ts:
--------------------------------------------------------------------------------
1 | // Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
2 | export { harTasks } from '@ohos/hvigor-ohos-plugin';
--------------------------------------------------------------------------------
/library/index.ets:
--------------------------------------------------------------------------------
1 | export { MainPage } from './src/main/ets/components/mainpage/MainPage'
2 |
3 | export { BaseBridge } from './src/main/ets/core/BaseBridge'
4 |
5 | export { IWebViewControllerProxy as WebViewInterface } from './src/main/ets/core/WebViewInterface'
6 |
7 | export { WebViewControllerProxy } from './src/main/ets/core/WebViewControllerProxy'
8 |
9 | export * from "./src/main/ets/core/Entity"
10 |
11 | export { BaseSendable } from './src/main/ets/wait/BaseSendable'
12 |
13 | export { taskWait } from './src/main/ets/wait/TaskWait'
14 |
15 | export { LogUtils } from './src/main/ets/utils/LogUtils'
16 |
--------------------------------------------------------------------------------
/library/oh-package-lock.json5:
--------------------------------------------------------------------------------
1 | {
2 | "meta": {
3 | "stableOrder": false
4 | },
5 | "lockfileVersion": 3,
6 | "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
7 | "specifiers": {
8 | "reflect-metadata@^0.1.13": "reflect-metadata@0.1.13"
9 | },
10 | "packages": {
11 | "reflect-metadata@0.1.13": {
12 | "name": "reflect-metadata",
13 | "version": "0.1.13",
14 | "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
15 | "resolved": "https://repo.harmonyos.com/ohpm/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
16 | "shasum": "67ae3ca57c972a2aa1642b10fe363fe32d49dc08",
17 | "registryType": "ohpm"
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/library/oh-package.json5:
--------------------------------------------------------------------------------
1 | {
2 | "license": "Apache-2.0",
3 | "devDependencies": {},
4 | "keywords": [
5 | "JavaScript",
6 | "鸿蒙",
7 | "harmonyOS",
8 | "dsBridge",
9 | "android",
10 | "JsBridge"
11 | ],
12 | "author": "HZWei",
13 | "name": "@hzw/ohos-dsbridge",
14 | "description": "HarmonyOS native and JS interaction, calling each other\u0027s functions",
15 | "main": "index.ets",
16 | "repository": "https://github.com/751496032/DSBridge-HarmonyOS.git",
17 | "homepage": "https://gitee.com/common-apps/dsbrigde-harmony-os.git",
18 | "version": "1.7.2",
19 | "dependencies": {
20 | "reflect-metadata": "^0.1.13"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/library/src/main/ets/components/mainpage/MainPage.ets:
--------------------------------------------------------------------------------
1 | @Component
2 | export struct MainPage {
3 | @State message: string = 'Hello World'
4 |
5 | build() {
6 | Row() {
7 | Column() {
8 | Text(this.message)
9 | .fontSize(50)
10 | .fontWeight(FontWeight.Bold)
11 | }
12 | .width('100%')
13 | }
14 | .height('100%')
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/library/src/main/ets/core/BaseBridge.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CallResult,
3 | MetaData,
4 | JsInterface,
5 | JavaScriptProxy,
6 | Parameter,
7 | NativeCallInfo,
8 | OnReturnValue,
9 | CompleteHandler,
10 | JavaScriptInterface,
11 | OnCloseWindowListener,
12 | Args,
13 | OnErrorMessageListener,
14 | NativeMethodParam
15 | } from './Entity'
16 | import "reflect-metadata"
17 | import { LogUtils } from '../utils/LogUtils'
18 | import router from '@ohos.router'
19 | import { IBaseBridge, IWebViewControllerProxy } from './WebViewInterface'
20 | import { ToastUtils } from '../utils/ToastUtils'
21 | import { JSON } from '@kit.ArkTS'
22 |
23 | export class BaseBridge implements JsInterface, IBaseBridge {
24 |
25 | private controller: IWebViewControllerProxy
26 | private name: string = "_dsbridge"
27 | private isInject: boolean = true
28 | private callID: number = 0
29 | private handlerMap = new Map()
30 | private jsClosePageListener?: OnCloseWindowListener
31 | private interrupt = false
32 | private _isSupportDS2: boolean = false
33 |
34 |
35 | private onErrorListener?: OnErrorMessageListener
36 |
37 | supportDS2(enable: boolean): void {
38 | this._isSupportDS2 = enable
39 | }
40 |
41 | public get isSupportDS2(): boolean {
42 | return this._isSupportDS2
43 | }
44 |
45 | setWebViewControllerProxy(controller: IWebViewControllerProxy){
46 | this.controller = controller
47 | }
48 |
49 | setGlobalErrorMessageListener(listener: OnErrorMessageListener): void {
50 | this.onErrorListener = listener
51 | }
52 |
53 |
54 | javaScriptProxy(): JavaScriptProxy {
55 | let list = this._isSupportDS2 ? ['call', 'returnValue'] : ['call']
56 | return {
57 | object: this,
58 | name: this.name,
59 | methodList: list,
60 | controller: this.controller
61 | }
62 | }
63 |
64 | /**
65 | * @deprecated
66 | */
67 | injectJavaScript = (name: string) => {
68 | if (this.isInject) {
69 | this.controller.registerJavaScriptProxy(this, name,
70 | ['call'])
71 | this.controller.refresh()
72 | this.isInject = false
73 | }
74 |
75 | }
76 |
77 | private isObject(val: Object) {
78 | return val !== null && typeof val === 'object';
79 | }
80 |
81 | private isNotEmpty(val: Object): boolean {
82 | return !this.isEmpty(val)
83 | }
84 |
85 | private isEmpty(val: Object): boolean {
86 | let isEmpty = val === undefined || val === null
87 | if (typeof val === 'string') {
88 | return isEmpty || val.trim().length <= 0
89 | }
90 | return isEmpty
91 | }
92 |
93 |
94 | /**
95 | *
96 | * @param methodName 原生方法名
97 | * @param params js携带的参数 对应实体类Parameter
98 | * @returns result#code == 0, 则整个流程正常调用
99 | */
100 | call = (methodName: string, params: string): string => {
101 | const error = "Js bridge called, but can't find a corresponded " +
102 | "JavascriptInterface object , please check your code!"
103 | let result: CallResult = { code: -1 }
104 | if (this.isEmpty(methodName)) {
105 | return this.handlerError(result, error)
106 | }
107 | const m = this.parseNamespace(methodName)
108 | const obj = this.controller.javaScriptNamespaceInterfaces.get(m[0])
109 | methodName = m[1]
110 | LogUtils.d("call methodName: " + methodName + " params: " + params + ' ' + (obj === this))
111 | if (this.isEmpty(obj)) {
112 | return this.handlerError(result, error)
113 | }
114 | const method = Reflect.get(this.isNotEmpty(obj) ? obj : this, methodName);
115 |
116 | if (typeof method !== 'function') {
117 | const err = `call failed: ${methodName} is not a method or is not defined`
118 | return this.handlerError(result, err)
119 | }
120 | let async: boolean = false
121 | if (this.isNotEmpty(method)) {
122 | const decorator = Reflect.getMetadata(MetaData.METHOD_DECORATE, method)
123 | if (!decorator) {
124 | const err = `call failed: please add @JavaScriptInterface decorator in the ${methodName} method`
125 | return this.handlerError(result, err)
126 | }
127 | async = Reflect.getMetadata(MetaData.ASYNC, method) ?? false
128 | } else {
129 | const err = `call failed: 【${methodName}】 method is undefined `
130 | return this.handlerError(result, err)
131 | }
132 |
133 | let jsParam: Parameter = this.safeParse(params)
134 | // async = (this.isNotEmpty(jsParam._dscbstub)) || async
135 | LogUtils.d(`call async: ${async}`)
136 | if (!async && this.isNotEmpty(jsParam._dscbstub)) {
137 | const err = 'call failed: h5 async differs from native registration.'
138 | return this.handlerError(result, err)
139 | }
140 | let data: string = (this.isObject(jsParam.data) ? JSON.stringify(jsParam.data) : jsParam.data) as string
141 | if (this._isSupportDS2) {
142 | if (async) {
143 | let newParam = this.safeParse(JSON.stringify(jsParam))
144 | delete newParam._dscbstub
145 | data = JSON.stringify(newParam)
146 | } else {
147 | data = params
148 | }
149 |
150 | }
151 | if (async) {
152 | const handler = {
153 | complete: (value: Args) => {
154 | result.code = 0
155 | result.data = value
156 | this.callbackToJs(jsParam, result)
157 | },
158 | setProgressData: (value: Args) => {
159 | result.code = 0
160 | result.data = value
161 | this.callbackToJs(jsParam, result, false)
162 | },
163 |
164 | }
165 | try {
166 | let len = method.length
167 |
168 | if (len === 0) {
169 | result.code = 0
170 | method.call(obj)
171 | } else if (len === 1) {
172 | result.code = 0
173 | method.call(obj, handler)
174 | } else if (len === 2) {
175 | result.code = 0
176 | method.call(obj, data, handler)
177 | } else {
178 | const err = `call failed: (${methodName}) method parameter number error`
179 | return this.handlerError(result, err)
180 | }
181 | } catch (e) {
182 | return this.handlerError(result, JSON.stringify(e))
183 | }
184 |
185 | } else {
186 | const r = method.call(obj, data);
187 | result.code = 0
188 | result.data = r
189 | }
190 |
191 | return this._isSupportDS2 ? result.data?.toString() : JSON.stringify(result)
192 |
193 | }
194 |
195 |
196 | private handlerError(result: CallResult, err: string) {
197 | result.errMsg = err
198 | LogUtils.e(err)
199 | // 不再弹出toast,由业务自行处理
200 | // ToastUtils.show(err)
201 | this.onErrorListener?.(err)
202 | return JSON.stringify(result)
203 | }
204 |
205 | private parseNamespace(method: string): string[] {
206 | let pos = method.lastIndexOf('.')
207 | let namespace = ''
208 | if (pos != -1) {
209 | namespace = method.substring(0, pos)
210 | method = method.substring(pos + 1)
211 | }
212 | return [namespace, method]
213 | }
214 |
215 | private safeParse(json: string): Parameter {
216 | let data: Parameter = {}
217 | if (this.isEmpty(json)) {
218 | return data
219 | }
220 |
221 | try {
222 | data = JSON.parse(json)
223 | } catch (e) {
224 | LogUtils.e(e)
225 | const msg = JSON.stringify(e)
226 | this.handlerError({ errMsg: msg } as CallResult, msg)
227 | }
228 | return data
229 |
230 | }
231 |
232 | private callbackToJs = (jsParam: Parameter, result: CallResult, complete: boolean = true) => {
233 | if (this.interrupt) return;
234 | let args = JSON.stringify(result)
235 | let callbackName = jsParam._dscbstub
236 | LogUtils.d(`callbackToJs : ${callbackName}(${args})`)
237 | let script = `${callbackName}(${args}.data);`
238 | if (complete) {
239 | script += "delete window." + callbackName
240 | }
241 | this.controller.runJavaScript(script)
242 | }
243 |
244 | callJs(method: string, args?: any[], jsReturnValueHandler?: OnReturnValue) {
245 | if (this.interrupt) return;
246 | const callInfo = {
247 | method,
248 | callbackId: ++this.callID,
249 | data: args ? JSON.stringify(args) : ''
250 | }
251 | let script = `(window._dsf.${method}||window.${method}).apply(window._dsf||window,${JSON.stringify(args)})`
252 | if (jsReturnValueHandler != null) {
253 | if (this._isSupportDS2) {
254 | script = `${this.name}.returnValue(${callInfo.callbackId},${script})`
255 | }
256 | this.handlerMap.set(callInfo.callbackId, jsReturnValueHandler)
257 | }
258 | if (this._isSupportDS2) {
259 | this.controller.runJavaScript(script)
260 | }else {
261 | const arg = JSON.stringify(callInfo)
262 | this.controller.runJavaScript(`window._handleMessageFromNative(${arg})`)
263 | }
264 |
265 | }
266 |
267 | callJsNoParam(method: string, jsReturnValueHandler?: OnReturnValue) {
268 | this.callJs(method, [], jsReturnValueHandler)
269 | }
270 |
271 | callHandler(method: string, args?: any[], jsReturnValueHandler?: OnReturnValue) {
272 | this.callJs(method, args, jsReturnValueHandler)
273 | }
274 |
275 | callHandlerNoParam(method: string, jsReturnValueHandler?: OnReturnValue) {
276 | this.callJs(method, [], jsReturnValueHandler)
277 | }
278 |
279 | hasJavascriptMethod(method: string): Promise {
280 | if (this.checkIfDS2()) {
281 | return
282 | }
283 | return new Promise((resolve, reject) => {
284 | let handler: OnReturnValue = (has: boolean) => {
285 | resolve(has)
286 | }
287 | this.callHandler("_hasJavascriptMethod", [method], handler)
288 | })
289 | }
290 |
291 |
292 | @JavaScriptInterface(false)
293 | private returnValue(param: string, value?: string) {
294 | if (this._isSupportDS2) {
295 | let id = Number.parseInt(param)
296 | if (id && this.handlerMap.has(id)) {
297 | let handler = this.handlerMap.get(id)
298 | handler(value)
299 | this.handlerMap.delete(id)
300 | }
301 | return
302 | }
303 | let p: {
304 | id?: number,
305 | complete?: boolean,
306 | data?: any
307 | } = JSON.parse(param)
308 | if (p.id && this.handlerMap.has(p.id)) {
309 | let handler = this.handlerMap.get(p.id)
310 | handler(p.data)
311 | if (p.complete) {
312 | this.handlerMap.delete(p.id)
313 | }
314 |
315 | }
316 |
317 | }
318 |
319 | @JavaScriptInterface(false)
320 | private dsinit(param: string) {
321 |
322 | }
323 |
324 | @JavaScriptInterface(false)
325 | private hasNativeMethod(param: string): boolean {
326 | if (this.checkIfDS2()) {
327 | return
328 | }
329 | const result: {
330 | code: number,
331 | msg: string
332 | } = { code: -1, msg: "" };
333 | try {
334 | const p = JSON.parse(param) as NativeMethodParam
335 | const m = this.parseNamespace(p.name)
336 | let methodName = m[1]
337 | const obj = this.controller.javaScriptNamespaceInterfaces.get(m[0])
338 | const method = Reflect.get(this.isNotEmpty(obj) ? obj : this, methodName);
339 | LogUtils.d(this + " " + methodName + " " + param + ' ' + obj)
340 | if (typeof method !== 'function') {
341 | const err = `call failed: ${methodName} is not a method or is not defined`
342 | result.msg = err
343 | } else {
344 | if (this.isNotEmpty(method)) {
345 | const decorator = Reflect.getMetadata(MetaData.METHOD_DECORATE, method)
346 | if (!decorator) {
347 | const err = `call failed: please add @JavaScriptInterface decorator in the ${methodName} method`
348 | result.msg = err
349 | } else {
350 | const async = Reflect.getMetadata(MetaData.ASYNC, method) ?? false
351 | result.code = (async && p.type === 'syn') || (!async && p.type === 'asyn') ? 1 : 0
352 | }
353 | } else {
354 | const err = `call failed: ${methodName} method does not exist`
355 | result.msg = err
356 | }
357 | }
358 |
359 | } catch (e) {
360 | result.msg = e
361 | }
362 | if (this.isNotEmpty(result.msg)) {
363 | LogUtils.e(result.msg)
364 | this.handlerError({ errMsg: result.msg } as CallResult, result.msg)
365 | }
366 | return result.code === 0
367 | }
368 |
369 | setClosePageListener(listener: OnCloseWindowListener) {
370 | this.jsClosePageListener = listener
371 | }
372 |
373 | @JavaScriptInterface(false)
374 | private closePage(param: any) {
375 | if (this.checkIfDS2()) {
376 | return
377 | }
378 | if (this.jsClosePageListener == null || this.jsClosePageListener() === true) {
379 | router.back()
380 | this.destroy()
381 | }
382 | }
383 |
384 | private checkIfDS2(): boolean {
385 | if (this._isSupportDS2) {
386 | ToastUtils.show('DS2.0脚本不支持该功能,请前端引入DS3.0脚本')
387 | return true
388 | }
389 | return false
390 | }
391 |
392 | destroy(){
393 | this.interrupt = true
394 | this.jsClosePageListener = null
395 | this.handlerMap.clear()
396 | this._isSupportDS2 = false
397 | this.onErrorListener = null
398 | }
399 |
400 | injectDS2Js(){
401 | if (this._isSupportDS2) {
402 | let script =
403 | "function getJsBridge(){window._dsf=window._dsf||{};return{call:function(b,a,c){\"function\"==typeof a&&(c=a,a={});if(\"function\"==typeof c){window.dscb=window.dscb||0;var d=\"dscb\"+window.dscb++;window[d]=c;a._dscbstub=d}a=JSON.stringify(a||{});return window._dswk?prompt(window._dswk+b,a):\"function\"==typeof _dsbridge?_dsbridge(b,a):_dsbridge.call(b,a)},register:function(b,a){\"object\"==typeof b?Object.assign(window._dsf,b):window._dsf[b]=a}}}dsBridge=getJsBridge();"
404 | this.controller.runJavaScript(script)
405 | }
406 |
407 | }
408 |
409 | }
--------------------------------------------------------------------------------
/library/src/main/ets/core/Entity.ts:
--------------------------------------------------------------------------------
1 | import "reflect-metadata"
2 | import { IWebViewControllerProxy } from './WebViewInterface'
3 |
4 | export interface JavaScriptProxy{
5 | object: JsInterface,
6 | name: string,
7 | methodList: Array,
8 | controller: IWebViewControllerProxy
9 | }
10 |
11 | /**
12 | * 在js中统一用JavaScriptProxy#name来调用原生的call(x,x)方法
13 | */
14 | export interface JsInterface {
15 | /**
16 | * @param methodName JS实际调用原生的方法
17 | * @param params JS携带过来的参数,有一个callbackID参数
18 | * @returns
19 | */
20 | call(methodName: string, params: string): string
21 | }
22 |
23 |
24 | export interface CallResult {
25 | code: number,
26 | data?: Args,
27 | errMsg?: string,
28 | async?: boolean,
29 | }
30 |
31 | export interface Parameter {
32 | data?: string | Object,
33 | _dscbstub?: string, // JS callback函数名称 ,挂载到window对象中
34 | // _dscbstub
35 | }
36 |
37 | export interface CompleteHandler {
38 | /**
39 | * 只能使用一次
40 | * @param value
41 | */
42 | complete(value: Args)
43 | /**
44 | * 可以使用多次
45 | * @param value
46 | */
47 | setProgressData(value: Args)
48 | }
49 |
50 | export enum MetaData {
51 | METHOD_DECORATE = "bridge:JavaScriptInterface",
52 | ASYNC = "bridge:Async"
53 | }
54 |
55 | /**
56 | * @param asyncCall
57 | * @returns
58 | */
59 | export function JavaScriptInterface(asyncCall: boolean = true): MethodDecorator {
60 | return (target, propertyKey, descriptor) => {
61 | Reflect.defineMetadata(MetaData.METHOD_DECORATE, propertyKey, descriptor.value !)
62 | Reflect.defineMetadata(MetaData.ASYNC, asyncCall, descriptor.value !)
63 | }
64 | }
65 |
66 |
67 | export interface NativeCallInfo {
68 | data: string,
69 | callbackId: number,
70 | method: string
71 | }
72 |
73 | export interface NativeMethodParam {
74 | name: string,
75 | type: 'syn' | 'asyn' | 'all'
76 | }
77 |
78 | export type OnReturnValue = (any) => void
79 |
80 | export type OnCloseWindowListener = () => boolean
81 |
82 | export type Args = number | string | boolean | Object
83 |
84 | export type OnErrorMessageListener = (string) => void
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/library/src/main/ets/core/WebViewControllerProxy.ets:
--------------------------------------------------------------------------------
1 | import webview from '@ohos.web.webview';
2 | import { ToastUtils } from '../utils/ToastUtils';
3 | import { BaseBridge } from './BaseBridge';
4 | import { Args, JsInterface, OnCloseWindowListener, OnErrorMessageListener, OnReturnValue } from './Entity';
5 | import { IWebViewControllerProxy, IBaseBridge } from './WebViewInterface';
6 |
7 |
8 | export class WebViewControllerProxy implements IWebViewControllerProxy, IOhosWebviewController, IBaseBridge {
9 | private controller: webview.WebviewController
10 | private bridge: BaseBridge = new BaseBridge()
11 | javaScriptNamespaceInterfaces: Map = new Map();
12 |
13 | constructor(controller: webview.WebviewController, obj?: object) {
14 | this.controller = controller
15 | this.bridge.setWebViewControllerProxy(this)
16 | this.addJavascriptObject(this.bridge, "_dsb")
17 | if (obj != null) {
18 | this.addJavascriptObject(obj)
19 | }
20 | }
21 |
22 | setGlobalErrorMessageListener(listener: OnErrorMessageListener): void {
23 | this.bridge.setGlobalErrorMessageListener(listener)
24 | }
25 |
26 | supportDS2(enable: boolean): void {
27 | this.bridge.supportDS2(enable)
28 | }
29 |
30 | injectDS2Js(): void {
31 | this.bridge.injectDS2Js()
32 | }
33 |
34 |
35 | addJavascriptObject = (obj: object, namespace: string = '') => {
36 | if (obj != null) {
37 | if (this.javaScriptNamespaceInterfaces.has(namespace)) {
38 | ToastUtils.show(namespace + "注入失败:一个组件内不能同时存在两个相同的命名空间名称")
39 | return
40 | }
41 | if (this.bridge.isSupportDS2 && namespace && namespace.length > 0) {
42 | ToastUtils.show(namespace + "注入失败:DSBridge2.0不支持命名空间")
43 | return
44 | }
45 | this.javaScriptNamespaceInterfaces.set(namespace, obj)
46 | }
47 |
48 | }
49 |
50 | hasJavascriptMethod(method: string): Promise {
51 | return this.bridge.hasJavascriptMethod(method)
52 | }
53 |
54 |
55 |
56 | callJs(method: string, args?: Args[], jsReturnValueHandler?: OnReturnValue) {
57 | this.bridge.callJs(method, args, jsReturnValueHandler)
58 | }
59 |
60 |
61 | callJsNoParam(method: string, jsReturnValueHandler?: OnReturnValue): void {
62 | this.bridge.callJsNoParam(method, jsReturnValueHandler)
63 | }
64 |
65 | callHandler(method: string, args?: Args[], jsReturnValueHandler?: OnReturnValue): void {
66 | this.bridge.callHandler(method, args, jsReturnValueHandler)
67 |
68 | }
69 |
70 | callHandlerNoParam(method: string, jsReturnValueHandler?: OnReturnValue): void {
71 | this.bridge.callHandlerNoParam(method, jsReturnValueHandler)
72 | }
73 |
74 |
75 | setClosePageListener(listener: OnCloseWindowListener) {
76 | this.bridge.setClosePageListener(listener)
77 | }
78 |
79 |
80 | destroy() {
81 | this.javaScriptNamespaceInterfaces.clear()
82 | this.bridge.destroy()
83 | }
84 |
85 | runJavaScript(script: string): Promise {
86 | return this.controller.runJavaScript(script);
87 | }
88 |
89 | registerJavaScriptProxy(object: object, name: string, methodList: string[]) {
90 | this.controller.registerJavaScriptProxy(object, name, methodList);
91 | this.refresh()
92 | }
93 |
94 | refresh() {
95 | this.controller.refresh()
96 | }
97 |
98 | getWebViewController(): webview.WebviewController {
99 | return this.controller
100 | }
101 |
102 | /**
103 | * @deprecated
104 | * @returns
105 | */
106 | getJavaScriptProxy(): RealJavaScriptProxy {
107 | let javaScriptProxy: RealJavaScriptProxy = {
108 | object: this.bridge.javaScriptProxy().object,
109 | name: this.bridge.javaScriptProxy().name,
110 | methodList: this.bridge.javaScriptProxy().methodList,
111 | controller: this.controller
112 | }
113 | return javaScriptProxy
114 | }
115 |
116 | get javaScriptProxy(): RealJavaScriptProxy {
117 | return {
118 | object: this.bridge.javaScriptProxy().object,
119 | name: this.bridge.javaScriptProxy().name,
120 | methodList: this.bridge.javaScriptProxy().methodList,
121 | controller: this.controller
122 | }
123 | }
124 |
125 |
126 | static createController(obj?: object): WebViewControllerProxy {
127 | return new WebViewControllerProxy(new webview.WebviewController(), obj)
128 | }
129 | }
130 |
131 | /**
132 | * ohos相关的api
133 | */
134 | export interface IOhosWebviewController {
135 | getWebViewController(): webview.WebviewController
136 |
137 | /**
138 | * @deprecated
139 | * @returns
140 | */
141 | getJavaScriptProxy(): RealJavaScriptProxy
142 |
143 | get javaScriptProxy(): RealJavaScriptProxy
144 | }
145 |
146 | export interface RealJavaScriptProxy {
147 | object: JsInterface,
148 | name: string,
149 | methodList: Array,
150 | controller: webview.WebviewController
151 | }
--------------------------------------------------------------------------------
/library/src/main/ets/core/WebViewInterface.ts:
--------------------------------------------------------------------------------
1 | import { Args, JavaScriptProxy, OnCloseWindowListener, OnReturnValue,OnErrorMessageListener } from './Entity';
2 |
3 | /**
4 | * web组件api的代理接口,不能涉及到ohos的相关属性
5 | */
6 | export interface IWebViewControllerProxy {
7 |
8 | readonly javaScriptNamespaceInterfaces: Map
9 |
10 | runJavaScript(script: string): Promise
11 |
12 | /**
13 | * @deprecated
14 | * @param object
15 | * @param name
16 | * @param methodList
17 | */
18 | registerJavaScriptProxy(object: object, name: string, methodList: Array): void;
19 |
20 | refresh(): void;
21 |
22 | }
23 |
24 |
25 |
26 | export interface IBaseBridge {
27 |
28 | /**
29 | * 是否支持DSBridge2.0脚本
30 | * @param enable true:使用2.0脚本,false: 3.0脚本是默认的
31 | */
32 | supportDS2(enable: boolean): void
33 |
34 | destroy(): void;
35 |
36 | callJs(method: string, args?: Args[], jsReturnValueHandler?: OnReturnValue): void
37 |
38 | callJsNoParam(method: string, jsReturnValueHandler?: OnReturnValue): void
39 |
40 | setClosePageListener(listener: OnCloseWindowListener): void
41 |
42 | hasJavascriptMethod(method: string): Promise
43 |
44 | callHandler(method: string, args?: Args[], jsReturnValueHandler?: OnReturnValue): void
45 |
46 | callHandlerNoParam(method: string, jsReturnValueHandler?: OnReturnValue): void
47 |
48 | setGlobalErrorMessageListener(listener: OnErrorMessageListener): void
49 | }
50 |
51 |
52 |
--------------------------------------------------------------------------------
/library/src/main/ets/utils/LogUtils.ts:
--------------------------------------------------------------------------------
1 | import hilog from '@ohos.hilog'
2 |
3 | export class LogUtils {
4 | private static readonly LOG_TAG: string = "LogUtils"
5 | private static enable: boolean = true
6 |
7 | static logEnable(enabled: boolean) {
8 | this.enable = enabled
9 | }
10 |
11 | static dTag(tag: string, msg: any) {
12 | if (!this.enable) {
13 | return
14 | }
15 | hilog.info(0x0001, tag, msg)
16 | }
17 |
18 |
19 | static d(msg: any) {
20 | this.dTag(this.LOG_TAG, msg)
21 | }
22 |
23 | static eTag(tag: string, msg: any) {
24 | if (!this.enable) {
25 | return
26 | }
27 | hilog.error(0x0001, tag, msg)
28 | }
29 |
30 | static e(msg: any) {
31 | this.eTag(this.LOG_TAG, msg)
32 | }
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/library/src/main/ets/utils/ToastUtils.ts:
--------------------------------------------------------------------------------
1 | import { Prompt } from '@kit.ArkUI'
2 |
3 | export class ToastUtils {
4 | static show(msg: string) {
5 | Prompt.showToast({ message: msg })
6 | }
7 |
8 | }
--------------------------------------------------------------------------------
/library/src/main/ets/wait/BaseSendable.ets:
--------------------------------------------------------------------------------
1 | /**
2 | * BaseSendable子类的属性只支持number、boolean、string、SendableClass
3 | *
4 | */
5 | @Sendable
6 | export class BaseSendable {
7 | public enableLog: boolean = false
8 | public key: string = `key_${Date.now()}`
9 | public tag: string = "BaseSendable"
10 | private _isCompleted: boolean = false
11 |
12 | public get isCompleted(): boolean {
13 | return this._isCompleted
14 | }
15 |
16 |
17 | /**
18 | * 不能手动调用complete()、run()方法
19 | */
20 | async complete() {
21 | this._isCompleted = true
22 | }
23 |
24 | async run(): Promise {
25 | }
26 | }
--------------------------------------------------------------------------------
/library/src/main/ets/wait/TaskWait.ets:
--------------------------------------------------------------------------------
1 | import { LogUtils } from '../utils/LogUtils'
2 | import { BaseSendable } from './BaseSendable'
3 | import { taskpool } from '@kit.ArkTS'
4 |
5 |
6 | @Concurrent
7 | async function run(sendable: BaseSendable) {
8 | const currentTime = Date.now()
9 | await sendable.run()
10 | await sendable.complete()
11 | if (sendable.enableLog) {
12 | LogUtils.d(`${sendable.tag} task time: ${Date.now() - currentTime}`)
13 | }
14 |
15 | }
16 |
17 | /**
18 | * 通过taskWait方法可以在主线程同步方法内执行串行异步并发任务,主线程会同步等待异步结果
19 | * 不建议做耗时过长的任务,当任务超过3s,无论是否执行完毕都会结束,数据可能会丢失
20 | * 为何是3s呢,当主线程阻塞超过3s时,系统会发生anr无响应异常,闪退。
21 | * @param args
22 | * @returns
23 | */
24 | export function taskWait(args: BaseSendable): T {
25 | const task: taskpool.Task = new taskpool.Task(run, args)
26 | taskpool.execute(task)
27 | let currentTime = Date.now()
28 | while (true) {
29 | if (Date.now() - currentTime < 3000) {
30 | if (args.isCompleted) {
31 | break
32 | }
33 | } else {
34 | break
35 | }
36 | }
37 | if (!args.isCompleted) {
38 | taskpool.getTaskPoolInfo().taskInfos.forEach((v) => {
39 | if (args.enableLog) {
40 | LogUtils.d(`${args.tag} task info: ${JSON.stringify(v)}`)
41 | }
42 | try {
43 | taskpool.cancel(task)
44 | } catch (e) {
45 | LogUtils.e(JSON.stringify(e))
46 | }
47 | })
48 | }
49 | if (args.enableLog) {
50 | LogUtils.d(`${args.tag} task result: ${JSON.stringify(args)} time: ${Date.now() - currentTime}`)
51 | }
52 | return args as T
53 | }
--------------------------------------------------------------------------------
/library/src/main/module.json5:
--------------------------------------------------------------------------------
1 | {
2 | "module": {
3 | "name": "library",
4 | "type": "har",
5 | "deviceTypes": [
6 | "default",
7 | "tablet"
8 | ]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/library/src/main/resources/base/element/string.json:
--------------------------------------------------------------------------------
1 | {
2 | "string": [
3 | {
4 | "name": "page_show",
5 | "value": "page from npm package"
6 | }
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/libs/dsbridge.har:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-HarmonyOS/7515ee4037c91959c4a7e2244802cf67a3e3a7c3/libs/dsbridge.har
--------------------------------------------------------------------------------
/oh-package-lock.json5:
--------------------------------------------------------------------------------
1 | {
2 | "meta": {
3 | "stableOrder": true
4 | },
5 | "lockfileVersion": 3,
6 | "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
7 | "specifiers": {
8 | "@hzw/zrouter@^1.0.7": "@hzw/zrouter@1.1.0",
9 | "@ohos/hypium@1.0.6": "@ohos/hypium@1.0.6",
10 | "reflect-metadata@^0.1.13": "reflect-metadata@0.1.13"
11 | },
12 | "packages": {
13 | "@hzw/zrouter@1.1.0": {
14 | "name": "@hzw/zrouter",
15 | "version": "1.1.0",
16 | "integrity": "sha512-nYO+MiOSxnEBOncc+m2CMRzEw2csBbAbyeArtzUgjfHW4BLKD6cDVZzK9kjYMCVf7pk0GxQiONUL3Ff0zGiGrg==",
17 | "resolved": "https://ohpm.openharmony.cn/ohpm/@hzw/zrouter/-/zrouter-1.1.0.har",
18 | "registryType": "ohpm",
19 | "dependencies": {
20 | "reflect-metadata": "^0.1.13"
21 | }
22 | },
23 | "@ohos/hypium@1.0.6": {
24 | "name": "@ohos/hypium",
25 | "version": "1.0.6",
26 | "integrity": "sha512-bb3DWeWhYrFqj9mPFV3yZQpkm36kbcK+YYaeY9g292QKSjOdmhEIQR2ULPvyMsgSR4usOBf5nnYrDmaCCXirgQ==",
27 | "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.6.tgz",
28 | "shasum": "3f5fed65372633233264b3447705b0831dfe7ea1",
29 | "registryType": "ohpm"
30 | },
31 | "reflect-metadata@0.1.13": {
32 | "name": "reflect-metadata",
33 | "version": "0.1.13",
34 | "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
35 | "resolved": "https://repo.harmonyos.com/ohpm/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
36 | "shasum": "67ae3ca57c972a2aa1642b10fe363fe32d49dc08",
37 | "registryType": "ohpm"
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/oh-package.json5:
--------------------------------------------------------------------------------
1 | {
2 | "modelVersion": "5.0.0",
3 | "license": "",
4 | "devDependencies": {
5 | "@ohos/hypium": "1.0.6"
6 | },
7 | "author": "HZWei",
8 | "name": "dsbrige_demo",
9 | "description": "Please describe the basic information.",
10 | "main": "",
11 | "version": "1.0.0",
12 | "dependencies": {
13 | "@hzw/zrouter": "^1.0.7"
14 | },
15 | "dynamicDependencies": {}
16 | }
--------------------------------------------------------------------------------