01 小瓦AI答的实现

小瓦AI答是OpenHarmony专属智能助手,部署在Laval社区PC端和开发者手机Laval社区应用移动端。

开发者手机上的Laval社区应用有两个页面。一个是web页面,加载社区问答信息,页面上悬浮小瓦图标。第二个页面是AI问答页面,用于和AI提问交互。

本次培训着重分析web组件相关的JS注册与执行,也就是网页侧调用(ArkTS)应用侧的函数和应用侧调用web网页里面的函数。

具体场景是在某个问题的问答页面,点击gif时,可以主动将网页上的问答信息带到下一个原生页面上,以便进行后续操作。

laval社区首页 小瓦问答使用流程

img

img

02 web组件及布局

2.1 ArkWeb简介

ArkWeb(方舟Web)提供了Web组件,用于在应用程序中显示Web页面内容。常见使用场景包括:

⭕ 应用集成Web页面:应用可以在页面中使用Web组件,嵌入Web页面内容,以降低开发成本,提升开发、运营效率。

⭕ 浏览器网页浏览场景:浏览器类应用可以使用Web组件,打开三方网页,使用无痕模式浏览Web页面,设置广告拦截等。

⭕ 小程序:小程序类宿主应用可以使用Web组件,渲染小程序的页面,实现同层渲染,视频托管等小程序的功能。

img

img

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 })
    }
  }
}

img

2.3 Stack布局

img

img

img

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)
      }
    }
  }
}

img

2.5 Stack布局 ArkUI Inspector 调试

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

img

03 JS注册与执行

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

laval问题页面 laval获取信息页面

img

img

3.1 JS注册

img

img

img

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执行

img

img

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%')
  }
}

img

04 Web调测

img

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

img

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按钮配置

img

img

4.2 效果

一旦使用Chrome开启调试,几乎所有Chrome能用的调试方法都支持。

img

img

4.3 Web调测的另一种开启方式

还有一种开启debug的方法,观察源码,发现可以直接设置参数实现web调试。

hdc shell param set web.debug.devtools true 

img

参考

web组件:https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/reference/apis-arkweb/ts-basic-components-web.md

层叠布局 (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

应用侧调用前端页面函数:https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/web/web-in-app-frontend-page-function-invoking.md

web调试:https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/web/web-debugging-with-devtools.md

相关文件下载
OpenHarmony系列直播课1009.pptx
14.41 MB
下载
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐