OpenHarmony Web应用开发-以小瓦AI答为例
01 小瓦AI答的实现
小瓦AI答是OpenHarmony专属智能助手,部署在Laval社区PC端和开发者手机Laval社区应用移动端。
开发者手机上的Laval社区应用有两个页面。一个是web页面,加载社区问答信息,页面上悬浮小瓦图标。第二个页面是AI问答页面,用于和AI提问交互。
本次培训着重分析web组件相关的JS注册与执行,也就是网页侧调用(ArkTS)应用侧的函数和应用侧调用web网页里面的函数。
具体场景是在某个问题的问答页面,点击gif时,可以主动将网页上的问答信息带到下一个原生页面上,以便进行后续操作。
| laval社区首页 | 小瓦问答使用流程 |
|---|---|
|
|
|
02 web组件及布局
2.1 ArkWeb简介
ArkWeb(方舟Web)提供了Web组件,用于在应用程序中显示Web页面内容。常见使用场景包括:
⭕ 应用集成Web页面:应用可以在页面中使用Web组件,嵌入Web页面内容,以降低开发成本,提升开发、运营效率。
⭕ 浏览器网页浏览场景:浏览器类应用可以使用Web组件,打开三方网页,使用无痕模式浏览Web页面,设置广告拦截等。
⭕ 小程序:小程序类宿主应用可以使用Web组件,渲染小程序的页面,实现同层渲染,视频托管等小程序的功能。


2.2 ArkWeb实现laval首页访问
laval社区讨论页网址为https://laval.csdn.net/user/discuss ,所以我们只需要将该网址配置到src属性即可。
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct Index {
controller: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
Web({ src: 'https://laval.csdn.net/user/discuss', controller: this.controller })
}
}
}

2.3 Stack布局



2.4 Stack布局实现laval首页访问
使用Stack布局包裹之前的Column组件,再加上一个图片,布局方式选为BottomEnd,加上适当的偏移,就可以实现laval社区首页的布局。
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct StackPage {
controller: webview.WebviewController = new webview.WebviewController();
build() {
Stack({ alignContent: Alignment.BottomEnd }) {
Image($r('app.media.startIcon'))
.zIndex(2).width("10%").margin({ right: 80, bottom: 80 })
Column() {
Web({ src: 'https://laval.csdn.net/user/discuss', controller: this.controller })
.zIndex(1)
}
}
}
}

2.5 Stack布局 ArkUI Inspector 调试
对于ArkUI侧的UI布局信息查看与调整,可以使用ArkUI Inspector,但对于web组件里面的网页信息,似乎就不起作用了。04 我们会介绍Web网页的调试。

03 JS注册与执行
当在问题页面点击悬浮的小瓦图标后,需要实现将网页的信息带入到下一个页面。
我们先实现一个函数,点击后跳转到下一个页面。此时这个函数与网页没有交互,自然就获取不到网页信息。
我们需要让网页来调用们的跳转函数,一旦网页调用跳转函数,那我们就可以在跳转函数里面加一些我们需要的参数。
| laval问题页面 | laval获取信息页面 |
|---|---|
|
|
|
3.1 JS注册



import { webview } from '@kit.ArkWeb';
import { router } from '@kit.ArkUI';
class question {
title: string = '';
mainBody: string = '';
isUnableAnalyze: string = '';
quesId: string = '';
constructor(title: string, mainBody: string, isUnableAnalyze: string, quesId: string) {
this.title = title;
this.mainBody = mainBody;
this.isUnableAnalyze = isUnableAnalyze;
this.quesId = quesId;
}
}
class questionUtil {
getQuestionDetailToChatPage(title: string, mainBody: string, isUnableAnalyze: string, quesId: string): void {
router.pushUrl({
url: 'pages/ChatPage',
params: new question(title, mainBody, isUnableAnalyze, quesId)
});
}
}
@Entry
@Component
struct ClickPage {
controller: webview.WebviewController = new webview.WebviewController();
private que: questionUtil = new questionUtil();
build() {
Column() {
Web({ src: 'https://laval.csdn.net/user/discuss', controller: this.controller })
.zIndex(1)
.javaScriptProxy({
object: this.que,
name: "questionUtil",
methodList: ["getQuestionDetailToChatPage"],
controller: this.controller,
})
}
}
}
3.2 JS执行


Image($r('app.media.startIcon'))
.zIndex(2).width("10%").margin({ right: 80, bottom: 80 })
.onClick(() => {
this.controller.runJavaScript(
`questionUtil.getQuestionDetailToChatPage(document.querySelector('.discuss-detail-content .discuss-detail-title')?.innerText,
document.querySelector('.discuss-detail-content .discuss-detail-desc-show')?.innerText,
document.querySelectorAll('.discuss-detail-desc-show img,.discuss-detail-desc img').length !== 0,
location.href.split('/').pop())`
);
})
document.querySelector('.discuss-detail-content .discuss-detail-title')?.innerText
获取指定标题元素的文本内容(元素不存在时返回 undefined)。获取问答标题
document.querySelector('.discuss-detail-content .discuss-detail-desc-show')?.innerText
获取指定描述元素的文本内容(元素不存在时返回 undefined)。获取问答内容
document.querySelectorAll('.discuss-detail-desc-show img,.discuss-detail-desc img').length !== 0
判断指定区域是否存在图片(返回布尔值 true/false)。小瓦不支持回答有图片的问题
location.href.split('/').pop()
提取当前 URL 的最后一段内容(通常作为页面唯一标识)。获取链接,例如如下链接提取出68d9eacaa6dc56200e8ab724。https://laval.csdn.net/user/discuss/68d9eacaa6dc56200e8ab724
3.3 JS注册与执行完整代码
@Entry
@Component
struct ClickPage {
controller: webview.WebviewController = new webview.WebviewController();
private que: questionUtil = new questionUtil();
build() {
Stack({ alignContent: Alignment.BottomEnd }) {
Image($r('app.media.startIcon'))
.zIndex(2).width("10%").margin({ right: 80, bottom: 80 })
.onClick(() => {
this.controller.runJavaScript(
`questionUtil.getQuestionDetailToChatPage(document.querySelector('.discuss-detail-content .discuss-detail-title')?.innerText,
document.querySelector('.discuss-detail-content .discuss-detail-desc-show')?.innerText,
document.querySelectorAll('.discuss-detail-desc-show img,.discuss-detail-desc img').length !== 0,
location.href.split('/').pop())`
);
})
Column() {
Web({ src: 'https://laval.csdn.net/user/discuss', controller: this.controller })
.zIndex(1)
.javaScriptProxy({
object: this.que,
name: "questionUtil",
methodList: ["getQuestionDetailToChatPage"],
controller: this.controller,
})
}
}
}
}
3.4 接收页面的代码
class question {
title: string = '';
mainBody: string = '';
isUnableAnalyze: string = '';
quesId: string = '';
}
@Entry
@Component
struct ChatPage {
@State message: string = 'Hello World';
@State question: question = new question();
aboutToAppear(): void {
this.question = this.getUIContext().getRouter().getParams() as question;
}
build() {
Column() {
Text(`title:${this.question.title}\n`).fontSize(20).fontWeight(FontWeight.Bold).textAlign(TextAlign.Start)
.onClick(()=>{
this.getUIContext().getRouter().replaceUrl({ url:'pages/ClickPage' })
})
Text(`mainBody:${this.question.mainBody}\n`).fontSize(20).fontWeight(FontWeight.Bold).textAlign(TextAlign.Start)
Text(`isUnableAnalyze:${this.question.isUnableAnalyze}\n`).fontSize(20).fontWeight(FontWeight.Bold).textAlign(TextAlign.Start)
Text(`quesId:${this.question.quesId}\n`).fontSize(20).fontWeight(FontWeight.Bold).textAlign(TextAlign.Start)
}
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Start)
.height('100%')
.width('100%')
}
}

04 Web调测

0、设备需为4.1.0及以上版本
1、获取联网权限
2、打开开发者模式中的USB调试

1.先在hdc shell里执行如下命令,查询ArkWeb在设备里创建的domain socket。
2.在命令行里执行如下命令转发端口。
“webview_devtools_remote_” 后面的数字,代表ArkWeb所在应用的进程号, 该数字不是固定的。请将数字改为自己查询到的值。
如果应用的进程号发生变化(例如,应用重新启动),则需要重新进行端口转发。
C:\Users\ONE>hdc shell "cat /proc/net/unix | grep devtools"
ffffff80fd7e76c0: 00000002 00000000 00010000 0001 01 709115 @webview_devtools_remote_13148
C:\Users\ONE>hdc fport tcp:9222 localabstract:webview_devtools_remote_13148
Forwardport result:OK
C:\Users\ONE>hdc fport ls
373636343036653935613339062301ce tcp:9222 localabstract:webview_devtools_remote_13148 [Forward]
当应用代码调用setWebDebuggingAccess接口开启Web调试开关后,ArkWeb内核将启动一个domain socket的监听,以此实现DevTools对网页的调试功能。也可以参考自动映射WebView调试链接。
Chrome浏览器无法直接访问到设备上的domain socket, 因此需要将设备上的domain socket转发到电脑上。
4.1 Web调测
在电脑端Chrome浏览器地址栏中输入调试工具地址 chrome://inspect/#devices 并打开该页面。
修改Chrome调试工具的配置。
需要从本地的TCP 9222端口发现被调试网页,所以请确保已勾选 “Discover network targets”。然后再进行网络配置。
(1) 点击 “Configure” 按钮。
(2) 在 “Target discovery settings” 中添加要监听的本地端口localhost:9222。
等待发现被调试页面
如果前面的步骤执行成功,Chrome的调试页面将显示待调试的网页。然后点击inspect。
| devices页面 | Configure按钮配置 |
|---|---|
|
|
|
4.2 效果
一旦使用Chrome开启调试,几乎所有Chrome能用的调试方法都支持。


4.3 Web调测的另一种开启方式
还有一种开启debug的方法,观察源码,发现可以直接设置参数实现web调试。
hdc shell param set web.debug.devtools true

参考
层叠布局 (Stack):https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/ui/arkts-layout-development-stack-layout.md
前端页面调用应用侧函数:https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/web/web-in-page-app-function-invoking.md
页面路由 (@ohos.router):https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/ui/arkts-routing.md
web调试:https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/web/web-debugging-with-devtools.md
更多推荐






所有评论(0)