赞
踩
当应用需要对网络状态进行长期管理监控时,当网络不好时弹窗提示用户当前网络状态差,或者网络连接中断时需要弹窗提示用户网络断开、任务异常终止等。
我们知道弹窗只能在UI主线程弹出,因此最简单的处理方法是将需要弹窗的类型、弹窗内容都通过子线程发送到主线程,这样主线程再根据不同弹窗类型进行弹窗。但是这样主线程就会有大量的弹窗代码,业务耦合严重。
因此我们将要弹出什么样的窗的权利下放给子线程,这样子线程就可以根据自己的需要构建弹窗,主线程只需要提供弹窗的统一调用即可,这样就实现了弹窗开发与主线程开发的解耦(主线程不需要感知子线程需要弹什么样的框)。此种方法仅限于系统类型弹窗(如AlertDialog,Toast等)。但是,对于自定义弹窗,由于@Buidler方法无法通过sendable传递,因此需要通过在主线程构建统一的自定义弹窗框架,子线程将弹窗参数传递到主线程。
DialogBuilder为Sendable类型的共享接口,提供了showDialog方法,用于弹窗。DialogBuilderWrapper是一个包装类,将DialogBuilder包装后以实现从子线程向主线程的数据传递。
方案的优势:主线程不感知DialogBuilder具体实现类的实现,在主线程中只需要通过调用DialogBuilder的接口showDialog方法构建弹窗。子线程可以独立/按需构建各种弹窗对象,虽然最终的弹窗还是在主线程完成,但是主线程不再感知弹窗的细节。
方案的遗憾:对于非系统类弹窗(自定义弹窗),由于@Builder方法无法在Sendable类中使用,因此子线程只能构建出自定义弹窗的参数(customerDialogParam),将构建参数传递给主线程,在抓线程中显示实现弹窗。
Step1:构建Senbable共享类型的DialogBuilder接口,接口提供showDialog方法入参为uiContext,用于弹窗构建并弹窗。
import { lang } from '@kit.ArkTS'; type ISendable = lang.ISendable; export interface DialogBuilder extends ISendable { showDialog(uiContext: UIContext): void; } Step2:以弹出AlertDialog为例,AlertDialogBuilder需要实现DialogBuilder,并在showDialog方法中使用uiContext完成弹窗的构建。 @Sendable export class ToastDialogBuilder implements DialogBuilder { showDialog(uiContext: UIContext): void { uiContext.getPromptAction().showToast({ message: 'this toast was built by worker', duration: 2000 }) } }
Step3:实现一个DialogWorker,用来接收主线程通知并进行弹窗,在实际业务中可能是一个网络状态管理子线程。
// ./src/main/ets/show_dialog/workers/DialogWorker.ets ... workerPort.onmessage = (e: MessageEvents) => { switch (e.data) { case "showAlertDialog": { let alertDialogBuilder = new AlertDialogBuilder(); let builderWrapper: DialogBuilderWrapper = new DialogBuilderWrapper(DialogBuilderWrapper.SYSTEM_DIALOG, alertDialogBuilder); workerPort.postMessageWithSharedSendable(builderWrapper); break; } case "showToastDialog": { let toastDialogBuilder = new ToastDialogBuilder(); let builderWrapper : DialogBuilderWrapper = new DialogBuilderWrapper(DialogBuilderWrapper.SYSTEM_DIALOG, toastDialogBuilder); workerPort.postMessageWithSharedSendable(builderWrapper); break; } case "showCustomerDialog" : { let builderWrapper : DialogBuilderWrapper = new DialogBuilderWrapper(DialogBuilderWrapper.CUSTOMER_DIALOG, null, new CustomerDialogParam("this content is from worker")); workerPort.postMessageWithSharedSendable(builderWrapper); break; } } } ...
从代码中可以看出,在postMessageWithSharedSendable方法中,传递的参数不是之前定义的AlertDialogBuilder共享数据类型,而是DialogBuilderWrapper,这是因为在dialogWorker.onmessage方法中,无法将mesEvent.data as 为Sendable类型,因此在传递Sendable对象类型时,需要再其外面增加一层封装。
export class DialogBuilderWrapper { static SYSTEM_DIALOG : string = "systemDialog"; static CUSTOMER_DIALOG : string = "customerDialog"; dialogType = "systemDialog" customerDialogParam !: CustomerDialogParam | undefined; dialogBuilder !: DialogBuilder | null; constructor(dialogType : string, dialogBuilder : DialogBuilder | null, customerDialogParam ?: CustomerDialogParam) { this.dialogType = dialogType this.dialogBuilder = dialogBuilder; this.customerDialogParam = customerDialogParam; } }
注意
DialogBuilderWrapper对象不是Sendable类型的,因此不需要提供get/set方法,因为方法是无法完成序列化的。
DialogBuilderWrapper对象里面的dialogBuilder和customerDialogParam是Sendable类型,因此他们提供的方法跨线程依旧可以调用。
Step4:启动DialogWorker,并将他放到AppStorage中,以便后续页面需要使用此Worker时可以方便获取。因为在弹窗时希望弹窗的逻辑与UI是解耦的,因此在Abiiltiy的onWindowStage回调中启动Worker。
export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage): void { // 启动Worker let dialogWorker : worker.ThreadWorker = new worker.ThreadWorker('entry/ets/show_dialog/workers/DialogWorker.ets'); // 将Worker存入AppStorage AppStorage.setOrCreate("dialogWorker", dialogWorker); dialogWorker.onmessage = async (msgEvent : MessageEvents) => { let dialogBuilderWrapper = msgEvent.data as DialogBuilderWrapper if (dialogBuilderWrapper.dialogType == DialogBuilderWrapper.SYSTEM_DIALOG) { // 如果是系统弹框,将uiContext传入buidler方法,完成弹窗构建。 let dialogBuilder = dialogBuilderWrapper.dialogBuilder if (dialogBuilder) { // 调用具体的DialogBuilder构建弹窗 dialogBuilder.showDialog((await windowStage.getMainWindow()).getUIContext()) } } else { // 如果是自定义弹窗,则在主线程获取弹窗Builder,并将子线程传递来的弹窗参数传递给Builder if (dialogBuilderWrapper.customerDialogParam) { let uiContext = (await windowStage.getMainWindow()).getUIContext() let promptAction = uiContext.getPromptAction() let contentNode = new ComponentContent(uiContext, getCustomerDialogBuilder(), dialogBuilderWrapper.customerDialogParam); // 全局弹出自定义弹窗 promptAction.openCustomDialog(contentNode); } } } ... } ... }
Step5:对于自定义弹窗,需要提前构建好组件Builder函数,通过CustomerDialogParam定义自定义弹窗内的具体样式和内容。(这个不是本重点,可以参考其他弹窗类文章进行参考)
@Sendable export class CustomerDialogParam { private message : string = "" constructor(msg: string) { this.message = msg; } getMessage() { return this.message } } export function getCustomerDialogBuilder() { return wrapBuilder(buildText); } @Builder function buildText(params: CustomerDialogParam) { Column() { Text(params.getMessage()) .fontSize(25) .fontWeight(FontWeight.Bold) }.backgroundColor('#FFF0F0F0') .margin({bottom: 36}) }
Step6:在页面中通过按钮模拟弹窗。
@Component export struct ShowDialogFromWorkerPage { private dialogWorker : worker.ThreadWorker | undefined = AppStorage.get("dialogWorker"); build() { NavDestination() { Column() { Text('worker子线程弹窗') Button('弹AlertDialog').onClick(event => { this.showDialogFromWorker("showAlertDialog"); }) Button('弹Toast').onClick(event => { this.showDialogFromWorker("showToastDialog"); }) Button('弹自定义弹窗').onClick(event => { this.showDialogFromWorker("showCustomerDialog"); }) } .justifyContent(FlexAlign.SpaceEvenly) .height('100%') .width('100%') }.hideTitleBar(true) } private showDialogFromWorker(dialogType : string) { try { this.dialogWorker?.postMessage(dialogType); } catch (error) { promptAction.showToast({ message: 'Worker instance is not running, maybe worker is terminated when PostMessage', duration: 2000 }); } } }
为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】。
gitee.com/MNxiaona/733GH
gitee.com/MNxiaona/733GH
1.基本概念
2.构建第一个ArkTS应用
3.……
gitee.com/MNxiaona/733GH
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
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
gitee.com/MNxiaona/733GH
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。