应用侧调用前端页面函数
应用侧可以通过 runJavaScript() 方法调用前端页面的JavaScript相关函数。
在下面的示例中,点击应用侧的“runJavaScript”按钮时,来触发前端页面的htmlTest()方法。
- 前端页面代码。
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<h1 id="text">这是一个测试信息,默认字体为黑色,调用runJavaScript方法后字体为绿色,调用runJavaScriptCodePassed方法后字体为红色</h1>
<script>
// 调用有参函数时实现。
var param = "param: JavaScript Hello World!";
function htmlTest(param) {
document.getElementById('text').style.color = 'green';
console.log(param);
}
// 调用无参函数时实现。
function htmlTest() {
document.getElementById('text').style.color = 'green';
}
// Click Me!触发前端页面callArkTS()函数执行JavaScript传递的代码。
function callArkTS() {
changeColor();
}
</script>
</body>
</html>
- 应用侧代码。
// xxx.ets
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct WebComponent {
webviewController: webview.WebviewController = new webview.WebviewController();
aboutToAppear() {
// 配置Web开启调试模式
webview.WebviewController.setWebDebuggingAccess(true);
}
build() {
Column() {
Button('runJavaScript')
.onClick(() => {
// 前端页面函数无参时,将param删除。
this.webviewController.runJavaScript('htmlTest(param)');
})
Button('runJavaScriptCodePassed')
.onClick(() => {
// 传递runJavaScript侧代码方法。
this.webviewController.runJavaScript(`function changeColor(){document.getElementById('text').style.color = 'red'}`);
})
Web({ src: $rawfile('index.html'), controller: this.webviewController })
}
}
}
前端页面调用应用侧函数
开发者使用Web组件将应用侧代码注册到前端页面中,注册完成之后,前端页面中使用注册的对象名称就可以调用应用侧的函数,实现在前端页面中调用应用侧方法。
注册应用侧代码有两种方式,一种在Web组件初始化调用,使用 javaScriptProxy() 接口。另外一种在Web组件初始化完成后调用,使用 registerJavaScriptProxy() 接口。
在下面的示例中,将test()方法注册在前端页面中, 该函数可以在前端页面触发运行。
- javaScriptProxy() 接口使用示例如下。
// xxx.ets
import { webview } from '@kit.ArkWeb';
class testClass {
constructor() {
}
test(): string {
return 'ArkTS Hello World!';
}
}
@Entry
@Component
struct WebComponent {
webviewController: webview.WebviewController = new webview.WebviewController();
// 声明需要注册的对象
@State testObj: testClass = new testClass();
build() {
Column() {
// Web组件加载本地index.html页面
Web({ src: $rawfile('index.html'), controller: this.webviewController})
// 将对象注入到web端
.javaScriptProxy({
object: this.testObj,
name: "testObjName",
methodList: ["test"],
controller: this.webviewController
})
}
}
}
- 应用侧使用registerJavaScriptProxy()接口注册。
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class testClass {
constructor() {
}
test(): string {
return "ArkUI Web Component";
}
toString(): void {
console.log('Web Component toString');
}
}
@Entry
@Component
struct Index {
webviewController: webview.WebviewController = new webview.WebviewController();
@State testObj: testClass = new testClass();
build() {
Column() {
Button('refresh')
.onClick(() => {
try {
this.webviewController.refresh();
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Register JavaScript To Window')
.onClick(() => {
try {
this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.webviewController })
}
}
}
说明
使用 registerJavaScriptProxy() 接口注册方法时,注册后需调用 refresh() 接口生效。
- index.html前端页面触发应用侧代码。
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
function callArkTS() {
let str = testObjName.test();
document.getElementById("demo").innerHTML = str;
console.info('ArkTS Hello World! :' + str);
}
</script>
</body>
</html>
复杂类型使用方法
- 应用侧和前端页面之间传递Array。
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class testClass {
constructor() {
}
test(): Array<Number> {
return [1, 2, 3, 4]
}
toString(param: String): void {
console.log('Web Component toString' + param);
}
}
@Entry
@Component
struct Index {
webviewController: webview.WebviewController = new webview.WebviewController();
@State testObj: testClass = new testClass();
build() {
Column() {
Button('refresh')
.onClick(() => {
try {
this.webviewController.refresh();
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Register JavaScript To Window')
.onClick(() => {
try {
this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.webviewController })
}
}
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
function callArkTS() {
testObjName.toString(testObjName.test());
}
</script>
</body>
</html>
- 应用侧和前端页面之间传递基础类型,非Function等复杂类型。
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class student {
name: string = '';
age: string = '';
}
class testClass {
constructor() {
}
// 传递的基础类型name:"jeck", age:"12"。
test(): student {
let st: student = { name: "jeck", age: "12" };
return st;
}
toString(param: ESObject): void {
console.log('Web Component toString' + param["name"]);
}
}
@Entry
@Component
struct Index {
webviewController: webview.WebviewController = new webview.WebviewController();
@State testObj: testClass = new testClass();
build() {
Column() {
Button('refresh')
.onClick(() => {
try {
this.webviewController.refresh();
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Register JavaScript To Window')
.onClick(() => {
try {
this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.webviewController })
}
}
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
function callArkTS() {
testObjName.toString(testObjName.test());
}
</script>
</body>
</html>
- 应用侧调用前端页面的Callback。
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class testClass {
constructor() {
}
test(param: Function): void {
param("call callback");
}
toString(param: String): void {
console.log('Web Component toString' + param);
}
}
@Entry
@Component
struct Index {
webviewController: webview.WebviewController = new webview.WebviewController();
@State testObj: testClass = new testClass();
build() {
Column() {
Button('refresh')
.onClick(() => {
try {
this.webviewController.refresh();
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Register JavaScript To Window')
.onClick(() => {
try {
this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.webviewController })
}
}
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
function callArkTS() {
testObjName.test(function(param){testObjName.toString(param)});
}
</script>
</body>
</html>
- 应用侧调用前端页面Object里的Function。
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class testClass {
constructor() {
}
test(param: ESObject): void {
param.hello("call obj func");
}
toString(param: String): void {
console.log('Web Component toString' + param);
}
}
@Entry
@Component
struct Index {
webviewController: webview.WebviewController = new webview.WebviewController();
@State testObj: testClass = new testClass();
build() {
Column() {
Button('refresh')
.onClick(() => {
try {
this.webviewController.refresh();
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Register JavaScript To Window')
.onClick(() => {
try {
this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.webviewController })
}
}
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
// 写法1
class Student {
constructor(nameList) {
this.methodNameListForJsProxy = nameList;
}
hello(param) {
testObjName.toString(param)
}
}
var st = new Student(["hello"])
// 写法2
//创建一个构造器,构造函数首字母大写
function Obj1(){
this.methodNameListForJsProxy=["hello"];
this.hello=function(param){
testObjName.toString(param)
};
}
//利用构造器,通过new关键字生成对象
var st1 = new Obj1();
function callArkTS() {
testObjName.test(st);
testObjName.test(st1);
}
</script>
</body>
</html>
- 前端页面调用应用侧Object里的Function。
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class ObjOther {
methodNameListForJsProxy: string[]
constructor(list: string[]) {
this.methodNameListForJsProxy = list
}
testOther(json: string): void {
console.info(json)
}
}
class testClass {
ObjReturn: ObjOther
constructor() {
this.ObjReturn = new ObjOther(["testOther"]);
}
test(): ESObject {
return this.ObjReturn
}
toString(param: string): void {
console.log('Web Component toString' + param);
}
}
@Entry
@Component
struct Index {
webviewController: webview.WebviewController = new webview.WebviewController();
@State testObj: testClass = new testClass();
build() {
Column() {
Button('refresh')
.onClick(() => {
try {
this.webviewController.refresh();
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Register JavaScript To Window')
.onClick(() => {
try {
this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.webviewController })
}
}
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
function callArkTS() {
testObjName.test().testOther("call other object func");
}
</script>
</body>
</html>
- Promise场景。
第一种使用方法,在应用侧new Promise。
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class testClass {
constructor() {
}
test(): Promise<string> {
let p: Promise<string> = new Promise((resolve, reject) => { setTimeout(() => {console.log('执行完成'); reject('fail');}, 10000);});
return p;
}
toString(param:String): void {
console.log(" " + param);
}
}
@Entry
@Component
struct Index {
webviewController: webview.WebviewController = new webview.WebviewController();
@State testObj: testClass = new testClass();
build() {
Column() {
Button('refresh')
.onClick(() => {
try {
this.webviewController.refresh();
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Register JavaScript To Window')
.onClick(() => {
try {
this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.webviewController })
}
}
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
function callArkTS() {
testObjName.test().then((param)=>{testObjName.toString(param)}).catch((param)=>{testObjName.toString(param)})
}
</script>
</body>
</html>
第二种使用方法,在前端页面new Promise。
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class testClass {
constructor() {
}
test(param:Function): void {
setTimeout( () => { param("suc") }, 10000)
}
toString(param:String): void {
console.log(" " + param);
}
}
@Entry
@Component
struct Index {
webviewController: webview.WebviewController = new webview.WebviewController();
@State testObj: testClass = new testClass();
build() {
Column() {
Button('refresh')
.onClick(() => {
try {
this.webviewController.refresh();
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Register JavaScript To Window')
.onClick(() => {
try {
this.webviewController.registerJavaScriptProxy(this.testObj, "testObjName", ["test", "toString"]);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.webviewController })
}
}
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
function callArkTS() {
let funpromise
var p = new Promise(function(resolve, reject){funpromise=(param)=>{resolve(param)}})
testObjName.test(funpromise)
p.then((param)=>{testObjName.toString(param)})
}
</script>
</body>
</html>
建立应用侧与前端页面数据通道
前端页面和应用侧之间可以用 createWebMessagePorts() 接口创建消息端口来实现两端的通信。
在下面的示例中,应用侧页面中通过createWebMessagePorts方法创建消息端口,再把其中一个端口通过 postMessage() 接口发送到前端页面,便可以在前端页面和应用侧之间互相发送消息。
- 应用侧代码。
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct WebComponent {
controller: webview.WebviewController = new webview.WebviewController();
ports: webview.WebMessagePort[] = [];
@State sendFromEts: string = 'Send this message from ets to HTML';
@State receivedFromHtml: string = 'Display received message send from HTML';
build() {
Column() {
// 展示接收到的来自HTML的内容
Text(this.receivedFromHtml)
// 输入框的内容发送到HTML
TextInput({ placeholder: 'Send this message from ets to HTML' })
.onChange((value: string) => {
this.sendFromEts = value;
})
// 该内容可以放在onPageEnd生命周期中调用。
Button('postMessage')
.onClick(() => {
try {
// 1、创建两个消息端口。
this.ports = this.controller.createWebMessagePorts();
// 2、在应用侧的消息端口(如端口1)上注册回调事件。
this.ports[1].onMessageEvent((result: webview.WebMessage) => {
let msg = 'Got msg from HTML:';
if (typeof (result) === 'string') {
console.info(`received string message from html5, string is: ${result}`);
msg = msg + result;
} else if (typeof (result) === 'object') {
if (result instanceof ArrayBuffer) {
console.info(`received arraybuffer from html5, length is: ${result.byteLength}`);
msg = msg + 'lenght is ' + result.byteLength;
} else {
console.info('not support');
}
} else {
console.info('not support');
}
this.receivedFromHtml = msg;
})
// 3、将另一个消息端口(如端口0)发送到HTML侧,由HTML侧保存并使用。
this.controller.postMessage('__init_port__', [this.ports[0]], '*');
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
// 4、使用应用侧的端口给另一个已经发送到html的端口发送消息。
Button('SendDataToHTML')
.onClick(() => {
try {
if (this.ports && this.ports[1]) {
this.ports[1].postMessageEvent(this.sendFromEts);
} else {
console.error(`ports is null, Please initialize first`);
}
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.controller })
}
}
}
- 前端页面代码。
<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebView Message Port Demo</title>
</head>
<body>
<h1>WebView Message Port Demo</h1>
<div>
<input type="button" value="SendToEts" onclick="PostMsgToEts(msgFromJS.value);"/><br/>
<input id="msgFromJS" type="text" value="send this message from HTML to ets"/><br/>
</div>
<p class="output">display received message send from ets</p>
</body>
<script>
var h5Port;
var output = document.querySelector('.output');
window.addEventListener('message', function (event) {
if (event.data === '__init_port__') {
if (event.ports[0] !== null) {
h5Port = event.ports[0]; // 1. 保存从应用侧发送过来的端口。
h5Port.onmessage = function (event) {
// 2. 接收ets侧发送过来的消息。
var msg = 'Got message from ets:';
var result = event.data;
if (typeof(result) === 'string') {
console.info(`received string message from html5, string is: ${result}`);
msg = msg + result;
} else if (typeof(result) === 'object') {
if (result instanceof ArrayBuffer) {
console.info(`received arraybuffer from html5, length is: ${result.byteLength}`);
msg = msg + 'lenght is ' + result.byteLength;
} else {
console.info('not support');
}
} else {
console.info('not support');
}
output.innerHTML = msg;
}
}
}
})
// 3. 使用h5Port向应用侧发送消息。
function PostMsgToEts(data) {
if (h5Port) {
h5Port.postMessage(data);
} else {
console.error('h5Port is null, Please initialize first');
}
}
</script>
</html>
应用侧与前端页面的相互调用(C/C++)
本指导适用于ArkWeb应用侧与前端网页通信场景,开发者可根据应用架构选择使用ArkWeb native接口完成业务通信机制(以下简称JSBridge)。
适用的应用架构
应用使用ArkTS、C++语言混合开发,或本身应用架构较贴近于小程序架构,自带C++侧环境,推荐使用ArkWeb在native侧提供的 ArkWeb_ControllerAPI 、 ArkWeb_ComponentAPI 实现JSBridge功能。
上图为具有普适性的小程序一般架构,其中逻辑层需要应用自带JavaScript运行时,本身已存在C++环境,通过native接口可直接在C++环境中完成与视图层(ArkWeb作为渲染器)的通信,无需再返回ArkTS环境调用JSBridge相关接口。
native JSBridge方案可以解决ArkTS环境的冗余切换,同时允许回调在非UI线程上报,避免造成UI阻塞。
使用native接口实现JSBridge通信
native侧ArkWeb绑定
-
ArkWeb组件声明在ArkTS侧,需要用户自定义一个标识webTag,并将webTag通过NAPI传至应用C++侧,后续ArkWeb native接口使用,均需webTag作为对应组件的唯一标识。
-
ArkTS侧
// 自定义webTag,在WebviewController创建时作为入参传入,建立controller与webTag的映射关系
webTag: string = 'ArkWeb1';
controller: web_webview.WebviewController = new web_webview.WebviewController(this.webTag);
...
// aboutToAppear中将webTag通过NAPI接口传入C++侧,作为C++侧ArkWeb组件的唯一标识
aboutToAppear() {
console.info("aboutToAppear")
//初始化web ndk
testNapi.nativeWebInit(this.webTag);
}
...
- C++侧
// 解析存储webTag
static napi_value NativeWebInit(napi_env env, napi_callback_info info) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit start");
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 获取第一个参数 webTag
size_t webTagSize = 0;
napi_get_value_string_utf8(env, args[0], nullptr, 0, &webTagSize);
char *webTagValue = new (std::nothrow) char[webTagSize + 1];
size_t webTagLength = 0;
napi_get_value_string_utf8(env, args[0], webTagValue, webTagSize + 1, &webTagLength);
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit webTag:%{public}s", webTagValue);
// 将webTag保存在实例对象中
jsbridge_object_ptr = std::make_shared<JSBridgeObject>(webTagValue);
...
native侧API结构体获取
ArkWeb native侧API通过函数 OH_ArkWeb_GetNativeAPI 获取,根据入参type不同,可分别获取 ArkWeb_ControllerAPI 、 ArkWeb_ComponentAPI 函数指针结构体。其中 ArkWeb_ControllerAPI 对应ArkTS侧 web_webview.WebviewController API , ArkWeb_ComponentAPI 对应ArkTS侧 ArkWeb组件API 。
static ArkWeb_ControllerAPI *controller = nullptr;
static ArkWeb_ComponentAPI *component = nullptr;
...
controller = reinterpret_cast<ArkWeb_ControllerAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_CONTROLLER));
component = reinterpret_cast<ArkWeb_ComponentAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_COMPONENT));
native侧组件生命周期回调注册
通过 ArkWeb_ComponentAPI 注册组件生命周期回调,在调用API前建议通过 ARKWEB_MEMBER_MISSING 校验该函数结构体是否有对应函数指针,避免SDK与设备ROM不匹配导致crash问题。
if (!ARKWEB_MEMBER_MISSING(component, onControllerAttached)) {
component->onControllerAttached(webTagValue, ValidCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onControllerAttached func not exist");
}
if (!ARKWEB_MEMBER_MISSING(component, onPageBegin)) {
component->onPageBegin(webTagValue, LoadStartCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageBegin func not exist");
}
if (!ARKWEB_MEMBER_MISSING(component, onPageEnd)) {
component->onPageEnd(webTagValue, LoadEndCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageEnd func not exist");
}
if (!ARKWEB_MEMBER_MISSING(component, onDestroy)) {
component->onDestroy(webTagValue, DestroyCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onDestroy func not exist");
}
前端页面调用应用侧函数
通过 registerJavaScriptProxy 将应用侧函数注册至前端页面,推荐在 onControllerAttached 回调中注册,其它时机注册需要手动调用 refresh 才能生效。
// 注册对象
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RegisterJavaScriptProxy begin");
ArkWeb_ProxyMethod method1 = {"method1", ProxyMethod1, static_cast<void *>(jsbridge_object_ptr->GetWeakPt ())};
ArkWeb_ProxyMethod method2 = {"method2", ProxyMethod2, static_cast<void *>(jsbridge_object_ptr->GetWeakPt ())};
ArkWeb_ProxyMethod methodList[2] = {method1, method2};
// 调用ndk接口注册对象
// 如此注册的情况下,在H5页面就可以使用proxy.method1、proxy.method1调用此文件下的ProxyMethod1和ProxyMethod2方法了
ArkWeb_ProxyObject proxyObject = {"ndkProxy", methodList, 2};
controller->registerJavaScriptProxy(webTag, &proxyObject);
应用侧调用前端页面函数
通过 runJavaScript 调用前端页面函数。
// 构造runJS执行的结构体
char* jsCode = "runJSRetStr()";
ArkWeb_JavaScriptObject object = {(uint8_t *)jsCode, bufferSize, &JSBridgeObject::StaticRunJavaScriptCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
// 调用前端页面runJSRetStr()函数
controller->runJavaScript(webTagValue, &object);
完整示例
- 前端页面代码entry/src/main/resources/rawfile/runJS.html
<!DOCTYPE html>
<html lang="en-gb">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>run javascript demo</title>
</head>
<body>
<h1>run JavaScript Ext demo</h1>
<p id="webDemo"></p>
<br>
<button type="button" style="height:30px;width:200px" onclick="testNdkProxyObjMethod1()">test ndk method1 ! </button>
<br>
<br>
<button type="button" style="height:30px;width:200px" onclick="testNdkProxyObjMethod2()">test ndk method2 ! </button>
<br>
</body>
<script type="text/javascript">
function testNdkProxyObjMethod1() {
if (window.ndkProxy == undefined) {
document.getElementById("webDemo").innerHTML = "ndkProxy undefined"
return "objName undefined"
}
if (window.ndkProxy.method1 == undefined) {
document.getElementById("webDemo").innerHTML = "ndkProxy method1 undefined"
return "objName test undefined"
}
if (window.ndkProxy.method2 == undefined) {
document.getElementById("webDemo").innerHTML = "ndkProxy method2 undefined"
return "objName test undefined"
}
var retStr = window.ndkProxy.method1("hello", "world", [1.2, -3.4, 123.456], ["Saab", "Volvo", "BMW", undefined], 1.23456, 123789, true, false, 0, undefined);
document.getElementById("webDemo").innerHTML = "ndkProxy and method1 is ok, " + retStr;
}
function testNdkProxyObjMethod2() {
if (window.ndkProxy == undefined) {
document.getElementById("webDemo").innerHTML = "ndkProxy undefined"
return "objName undefined"
}
if (window.ndkProxy.method1 == undefined) {
document.getElementById("webDemo").innerHTML = "ndkProxy method1 undefined"
return "objName test undefined"
}
if (window.ndkProxy.method2 == undefined) {
document.getElementById("webDemo").innerHTML = "ndkProxy method2 undefined"
return "objName test undefined"
}
var student = {
name:"zhang",
sex:"man",
age:25
};
var cars = [student, 456, false, 4.567];
let params = "[\"{\\\"scope\\\"]";
var retStr = window.ndkProxy.method2("hello", "world", false, cars, params);
document.getElementById("webDemo").innerHTML = "ndkProxy and method2 is ok, " + retStr;
}
function runJSRetStr(data) {
const d = new Date();
let time = d.getTime();
return JSON.stringify(time)
}
</script>
</html>
- ArkTS侧代码entry/src/main/ets/pages/Index.ets
import testNapi from 'libentry.so';
import { webview } from '@kit.ArkWeb';
class testObj {
constructor() {
}
test(): string {
console.log('ArkUI Web Component');
return "ArkUI Web Component";
}
toString(): void {
console.log('Web Component toString');
}
}
@Entry
@Component
struct Index {
webTag: string = 'ArkWeb1';
controller: webview.WebviewController = new webview.WebviewController(this.webTag);
@State testObjtest: testObj = new testObj();
aboutToAppear() {
console.info("aboutToAppear")
//初始化web ndk
testNapi.nativeWebInit(this.webTag);
}
build() {
Column() {
Row() {
Button('runJS hello')
.fontSize(12)
.onClick(() => {
testNapi.runJavaScript(this.webTag, "runJSRetStr(\"" + "hello" + "\")");
})
}.height('20%')
Row() {
Web({ src: $rawfile('runJS.html'), controller: this.controller })
.javaScriptAccess(true)
.fileAccess(true)
.onControllerAttached(() => {
console.error("ndk onControllerAttached webId: " + this.controller.getWebId());
})
}.height('80%')
}
}
}
- NAPI侧暴露ArkTS接口entry/src/main/cpp/types/libentry/index.d.ts
export const nativeWebInit: (webName: string) => void;
export const runJavaScript: (webName: string, jsCode: string) => void;
- NAPI侧编译配置entry/src/main/cpp/CMakeLists.txt
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(NDKJSBridg)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED hello.cpp jsbridge_object.cpp)
find_library(
# Sets the name of the path variable.
hilog-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
hilog_ndk.z
)
target_link_libraries(entry PUBLIC libace_napi.z.so ${hilog-lib} libohweb.so)
- NAPI层代码entry/src/main/cpp/hello.cpp
#include "napi/native_api.h"
#include <bits/alltypes.h>
#include <memory>
#include <string>
#include <sys/types.h>
#include <thread>
#include "hilog/log.h"
#include "web/arkweb_interface.h"
#include "jsbridge_object.h"
constexpr unsigned int LOG_PRINT_DOMAIN = 0xFF00;
std::shared_ptr<JSBridgeObject> jsbridge_object_ptr = nullptr;
static ArkWeb_ControllerAPI *controller = nullptr;
static ArkWeb_ComponentAPI *component = nullptr;
// 发送JS脚本到H5侧执行,执行结果的回调。
static void RunJavaScriptCallback(const char *webTag, const char *result, void *userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RunJavaScriptCallback webTag:%{public}s", webTag);
if (!userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RunJavaScriptCallback userData is nullptr");
return;
}
std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
if (auto jsb_ptr = jsb_weak_ptr.lock()) {
jsb_ptr->RunJavaScriptCallback(result);
} else {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
"ndk RunJavaScriptCallback jsb_weak_ptr lock failed");
}
}
// 示例代码 ,注册了1个对象,2个方法
static void ProxyMethod1(const char *webTag, const ArkWeb_JavaScriptBridgeData *dataArray, size_t arraySize, void *userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod1 webTag:%{public}s", webTag);
if (!userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod1 userData is nullptr");
return;
}
std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
if (auto jsb_ptr = jsb_weak_ptr.lock()) {
jsb_ptr->ProxyMethod1(dataArray, arraySize);
} else {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod1 jsb_weak_ptr lock failed");
}
}
static void ProxyMethod2(const char *webTag, const ArkWeb_JavaScriptBridgeData *dataArray, size_t arraySize, void *userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod2 webTag:%{public}s", webTag);
if (!userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod2 userData is nullptr");
return;
}
std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
std::string jsCode = "runJSRetStr()";
ArkWeb_JavaScriptObject object = {(uint8_t *)jsCode.c_str(), jsCode.size(),
&JSBridgeObject::StaticRunJavaScriptCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
controller->runJavaScript(webTag, &object);
if (auto jsb_ptr = jsb_weak_ptr.lock()) {
jsb_ptr->ProxyMethod2(dataArray, arraySize);
} else {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ProxyMethod2 jsb_weak_ptr lock failed");
}
}
void ValidCallback(const char *webTag, void *userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ValidCallback webTag: %{public}s", webTag);
if (!userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ValidCallback userData is nullptr");
return;
}
std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
if (auto jsb_ptr = jsb_weak_ptr.lock()) {
jsb_ptr->SaySomething("ValidCallback");
} else {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk ValidCallback jsb_weak_ptr lock failed");
}
// 注册对象
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RegisterJavaScriptProxy begin");
ArkWeb_ProxyMethod method1 = {"method1", ProxyMethod1, static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
ArkWeb_ProxyMethod method2 = {"method2", ProxyMethod2, static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
ArkWeb_ProxyMethod methodList[2] = {method1, method2};
// 调用ndk接口注册对象
// 如此注册的情况下,在H5页面就可以使用proxy.method1、proxy.method1调用此文件下的ProxyMethod1和ProxyMethod2方法了
ArkWeb_ProxyObject proxyObject = {"ndkProxy", methodList, 2};
controller->registerJavaScriptProxy(webTag, &proxyObject);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk RegisterJavaScriptProxy end");
}
void LoadStartCallback(const char *webTag, void *userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadStartCallback webTag: %{public}s", webTag);
if (!userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadStartCallback userData is nullptr");
return;
}
std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
if (auto jsb_ptr = jsb_weak_ptr.lock()) {
jsb_ptr->SaySomething("LoadStartCallback");
} else {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadStartCallback jsb_weak_ptr lock failed");
}
}
void LoadEndCallback(const char *webTag, void *userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadEndCallback webTag: %{public}s", webTag);
if (!userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadEndCallback userData is nullptr");
return;
}
std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
if (auto jsb_ptr = jsb_weak_ptr.lock()) {
jsb_ptr->SaySomething("LoadEndCallback");
} else {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk LoadEndCallback jsb_weak_ptr lock failed");
}
}
void DestroyCallback(const char *webTag, void *userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk DestoryCallback webTag: %{public}s", webTag);
if (!userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk DestroyCallback userData is nullptr");
return;
}
std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
if (auto jsb_ptr = jsb_weak_ptr.lock()) {
jsb_ptr->SaySomething("DestroyCallback");
} else {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk DestroyCallback jsb_weak_ptr lock failed");
}
}
void SetComponentCallback(ArkWeb_ComponentAPI * component, const char* webTagValue) {
if (!ARKWEB_MEMBER_MISSING(component, onControllerAttached)) {
component->onControllerAttached(webTagValue, ValidCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onControllerAttached func not exist");
}
if (!ARKWEB_MEMBER_MISSING(component, onPageBegin)) {
component->onPageBegin(webTagValue, LoadStartCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageBegin func not exist");
}
if (!ARKWEB_MEMBER_MISSING(component, onPageEnd)) {
component->onPageEnd(webTagValue, LoadEndCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onPageEnd func not exist");
}
if (!ARKWEB_MEMBER_MISSING(component, onDestroy)) {
component->onDestroy(webTagValue, DestroyCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr()));
} else {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "component onDestroy func not exist");
}
}
// 解析存储webTag
static napi_value NativeWebInit(napi_env env, napi_callback_info info) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit start");
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 获取第一个参数 webTag
size_t webTagSize = 0;
napi_get_value_string_utf8(env, args[0], nullptr, 0, &webTagSize);
char *webTagValue = new (std::nothrow) char[webTagSize + 1];
size_t webTagLength = 0;
napi_get_value_string_utf8(env, args[0], webTagValue, webTagSize + 1, &webTagLength);
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit webTag:%{public}s", webTagValue);
// 将webTag保存在实例对象中
jsbridge_object_ptr = std::make_shared<JSBridgeObject>(webTagValue);
if (jsbridge_object_ptr)
jsbridge_object_ptr->Init();
controller = reinterpret_cast<ArkWeb_ControllerAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_CONTROLLER));
component = reinterpret_cast<ArkWeb_ComponentAPI *>(OH_ArkWeb_GetNativeAPI(ARKWEB_NATIVE_COMPONENT));
SetComponentCallback(component, webTagValue);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk NativeWebInit end");
return nullptr;
}
// 发送JS脚本到H5侧执行
static napi_value RunJavaScript(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 获取第一个参数 webTag
size_t webTagSize = 0;
napi_get_value_string_utf8(env, args[0], nullptr, 0, &webTagSize);
char *webTagValue = new (std::nothrow) char[webTagSize + 1];
size_t webTagLength = 0;
napi_get_value_string_utf8(env, args[0], webTagValue, webTagSize + 1, &webTagLength);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "ndk OH_NativeArkWeb_RunJavaScript webTag:%{public}s",
webTagValue);
// 获取第二个参数 jsCode
size_t bufferSize = 0;
napi_get_value_string_utf8(env, args[1], nullptr, 0, &bufferSize);
char *jsCode = new (std::nothrow) char[bufferSize + 1];
size_t byteLength = 0;
napi_get_value_string_utf8(env, args[1], jsCode, bufferSize + 1, &byteLength);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
"ndk OH_NativeArkWeb_RunJavaScript jsCode len:%{public}zu", strlen(jsCode));
// 构造runJS执行的结构体
ArkWeb_JavaScriptObject object = {(uint8_t *)jsCode, bufferSize, &JSBridgeObject::StaticRunJavaScriptCallback,
static_cast<void *>(jsbridge_object_ptr->GetWeakPtr())};
controller->runJavaScript(webTagValue, &object);
return nullptr;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"nativeWebInit", nullptr, NativeWebInit, nullptr, nullptr, nullptr, napi_default, nullptr},
{"runJavaScript", nullptr, RunJavaScript, nullptr, nullptr, nullptr, napi_default, nullptr},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
- Native侧业务代码entry/src/main/cpp/jsbridge_object.h、entry/src/main/cpp/jsbridge_object.cpp
#include "web/arkweb_type.h"
#include <string>
class JSBridgeObject : public std::enable_shared_from_this<JSBridgeObject> {
public:
JSBridgeObject(const char* webTag);
~JSBridgeObject() = default;
void Init();
std::weak_ptr<JSBridgeObject>* GetWeakPtr();
static void StaticRunJavaScriptCallback(const char *webTag, const ArkWeb_JavaScriptBridgeData *data, void *userData);
void RunJavaScriptCallback(const char *result);
void ProxyMethod1(const ArkWeb_JavaScriptBridgeData *dataArray, int32_t arraySize);
void ProxyMethod2(const ArkWeb_JavaScriptBridgeData *dataArray, int32_t arraySize);
void SaySomething(const char* say);
private:
std::string webTag_;
std::weak_ptr<JSBridgeObject> weak_ptr_;
};
#include "jsbridge_object.h"
#include "hilog/log.h"
constexpr unsigned int LOG_PRINT_DOMAIN = 0xFF00;
JSBridgeObject::JSBridgeObject(const char *webTag) : webTag_(webTag) {}
void JSBridgeObject::Init() { weak_ptr_ = shared_from_this(); }
std::weak_ptr<JSBridgeObject> *JSBridgeObject::GetWeakPtr() { return &weak_ptr_; }
void JSBridgeObject::StaticRunJavaScriptCallback(const char *webTag, const ArkWeb_JavaScriptBridgeData *data,
void *userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
"JSBridgeObject StaticRunJavaScriptCallback webTag:%{public}s", webTag);
if (!userData) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
"JSBridgeObject StaticRunJavaScriptCallback userData is nullptr");
return;
}
std::weak_ptr<JSBridgeObject> jsb_weak_ptr = *static_cast<std::weak_ptr<JSBridgeObject> *>(userData);
if (auto jsb_ptr = jsb_weak_ptr.lock()) {
std::string result((char *)data->buffer, data->size);
jsb_ptr->RunJavaScriptCallback(result.c_str());
} else {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
"JSBridgeObject StaticRunJavaScriptCallback jsb_weak_ptr lock failed");
}
}
void JSBridgeObject::RunJavaScriptCallback(const char *result) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
"JSBridgeObject OH_NativeArkWeb_RunJavaScript result:%{public}s", result);
}
void JSBridgeObject::ProxyMethod1(const ArkWeb_JavaScriptBridgeData *dataArray, int32_t arraySize) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "JSBridgeObject ProxyMethod1 argc:%{public}d",
arraySize);
for (int i = 0; i < arraySize; i++) {
std::string result((char *)dataArray[i].buffer, dataArray[i].size);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
"JSBridgeObject ProxyMethod1 argv[%{public}d]:%{public}s, size:%{public}d", i, result.c_str(),
dataArray[i].size);
}
}
void JSBridgeObject::ProxyMethod2(const ArkWeb_JavaScriptBridgeData *dataArray, int32_t arraySize) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "JSBridgeObject ProxyMethod2 argc:%{public}d",
arraySize);
for (int i = 0; i < arraySize; i++) {
std::string result((char *)dataArray[i].buffer, dataArray[i].size);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb",
"JSBridgeObject ProxyMethod2 argv[%{public}d]:%{public}s, size:%{public}d", i, result.c_str(),
dataArray[i].size);
}
}
void JSBridgeObject::SaySomething(const char *say) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ArkWeb", "JSBridgeObject SaySomething argc:%{public}s", say);
}
鸿蒙全栈开发全新学习指南
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以要有一份实用的鸿蒙(HarmonyOS NEXT)学习路线与学习文档用来跟着学习是非常有必要的。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
本路线共分为四个阶段:
第一阶段:鸿蒙初中级开发必备技能
第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH
第三阶段:应用开发中高级就业技术
第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH
《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:gitee.com/MNxiaona/733GH
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH
鸿蒙入门教学视频:
美团APP实战开发教学:gitee.com/MNxiaona/733GH
写在最后
- 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:
gitee.com/MNxiaona/733GH