HarmonyOS相关

HarmonyOS开发学习笔记

2023-10-19  本文已影响0人  小仙女喂得猪呀

HarmonyOS开发学习笔记

基本步骤

  1. 开发环境搭建 按照官网步骤
  2. 语言选择 官方建议选择arkTs(TypeScript的超集)
  3. 官方开发 参考指南 入门必读
  4. 官方开发 参考api 实战必读

HarmonyOS实战所需基础能力

  1. 页面布局能力
  2. 页面之间的数据通信能力(Ability和page; Ability和Ability; page和page)
  3. 数据存储和读取能力
  4. 网络请求能力
  5. 权限管理能力
  6. 媒体播放能力
  7. 通知管理能力
  8. 文件管理能力
  9. web接入交互能力

基于ArkTs应用(Stage模型)

HarmonyOS开发中@Entry @Component @State作用

  1. @Entry:这个注解在自定义组件时使用,表示该自定义组件为入口组件。
  2. @Component:这个注解在HarmonyOS的UI框架中,表示一个组件。Component类位于ohos.agp.components包中,直接派生自java.lang.Object,并且是HarmonyOS中所有界面组件的直接或间接父类。每一个组件在屏幕上占用一个矩形区域,在这个区域中对绘制和事件处理做出响应。
  3. @State:这个注解在组件中定义状态变量。状态变量变化会触发UI刷新。它具有以下特征:
    支持多种类型:允许class、number、boolean、string强类型的按值和按引用类型。允许这些强类型构成的数组,即Array<class>、Array<string>、Array<boolean>、Array<number>。不允许object和any。
    支持多实例:组件不同实例的内部状态数据独立。
    内部私有:标记为@State的属性是私有变量,只能在组件内访问。
    需要本地初始化:必须为所有@State变量分配初始值,将变量保持未初始化可能导致框架行为未定义。
    创建自定义组件时支持通过状态变量名设置初始值:在创建组件实例时,可以通过变量名显式指定@State状态属性的初始值。

在HarmonyOS开发中Component中@Builder 和build()作用是否一样

在HarmonyOS的Component中,@Builder和build()的作用并不完全相同。

@Builder是一种装饰器,用于修饰函数,以指示该函数是一个自定义构建函数。被@Builder装饰的函数需要遵循build()函数语法规则,可以将重复使用的UI元素抽象成一个方法,然后在build方法中调用。通过使用@Builder装饰器,开发者可以更方便地复用UI元素,简化代码量。

build()函数是Component中一个重要的方法,用于构建UI界面。在build()函数中,开发者可以通过调用@Builder装饰的函数来复用UI元素,也可以在build()函数中编写其他的UI构建逻辑。因此,build()函数和@Builder装饰器是紧密相关的,但并不是相同的概念。

综上所述,@Builder和build()在HarmonyOS的Component中都是重要的开发工具,但它们的作用略有不同。@Builder用于定义自定义构建函数,而build()函数则用于构建UI界面。

HarmonyOS中其它一些常用的装饰器

HarmonyOS中Component中几个常用的生命周期方法

HarmonyOS中被@Entry装饰的组件生命周期

HarmonyOS开发所需ArkTs基本语法参考文档

声明式ui描述

  1. 创建组件
    • 无参数
      如果组件的接口定义没有包含必选构造参数,则组件后面的“()”不需要配置任何内容。例如,Divider组件不包含构造参数:
      Column() {
         Text('item 1')
         Divider()
         Text('item 2')
       }
      
    • 有参数 如果组件的接口定义包含构造参数,则在组件后面的“()”配置相应参数。
      • Image组件的必选参数src。
        Image('https://xyz/test.jpg')
        
      • Text组件的非必选参数content。
        // string类型的参数
        Text('test')
        // $r形式引入应用资源,可应用于多语言场景
        Text($r('app.string.title_value'))
        // 无参数形式
        Text()
        
      • 变量或表达式也可以用于参数赋值,其中表达式返回的结果类型必须满足参数类型要求。
        例如,设置变量或表达式来构造Image和Text组件的参数。
        Image(this.imagePath)
        Image('https://' + this.imageUrl)
        Text(`count: ${this.count}`)
        
  2. 配置属性
    属性方法以“.”链式调用的方式配置系统组件的样式和其他属性,建议每个属性方法单独写一行。
    • 配置Text组件的字体大小。
      Text('test')
        .fontSize(12)
      
    • 配置组件的多个属性。
      Image('test.jpg')
        .alt('error.jpg')    
        .width(100)    
        .height(100)
      
    • 除了直接传递常量参数外,还可以传递变量或表达式。
      Text('hello')
        .fontSize(this.size)
      Image('test.jpg')
        .width(this.count % 2 === 0 ? 100 : 200)    
        .height(this.offset + 100)
      
    • 对于系统组件,ArkUI还为其属性预定义了一些枚举类型供开发者调用,枚举类型可以作为参数传递,但必须满足参数类型要求。
      例如,可以按以下方式配置Text组件的颜色和字体样式。
      Text('hello')
        .fontSize(20)
        .fontColor(Color.Red)
        .fontWeight(FontWeight.Bold)
      
  3. 配置事件
    事件方法以“.”链式调用的方式配置系统组件支持的事件,建议每个事件方法单独写一行。
    • 使用lambda表达式配置组件的事件方法。
      Button('Click me')
        .onClick(() => {
          this.myText = 'ArkUI';
        })
      
    • 使用匿名函数表达式配置组件的事件方法,要求使用bind,以确保函数体中的this指向当前组件。
      Button('add counter')
        .onClick(function(){
          this.counter += 2;
        }.bind(this))
      
    • 使用组件的成员函数配置组件的事件方法。
      myClickHandler(): void {
          this.counter += 2;
      }
      ...
        Button('add counter')
          .onClick(this.myClickHandler.bind(this))
      
  4. 配置子组件
    如果组件支持子组件配置,则需在尾随闭包"{...}"中为组件添加子组件的UI描述。Column、Row、Stack、Grid、List等组件都是容器组件。

自定义组件

在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。
自定义组件具有以下特点:

自定义组件的基本用法:

@Component
struct HelloComponent {
  @State message: string = 'Hello, World!';

  build() {
    // HelloComponent自定义组件组合系统组件Row和Text
    Row() {
      Text(this.message)
        .onClick(() => {
          // 状态变量message的改变驱动UI刷新,UI从'Hello, World!'刷新为'Hello, ArkUI!'
          this.message = 'Hello, ArkUI!';
        })
    }
  }
}

HelloComponent可以在其他自定义组件中的build()函数中多次创建,实现自定义组件的重用。

@Entry
@Component
struct ParentComponent {
  build() {
    Column() {
      Text('ArkUI message')
      HelloComponent({ message: 'Hello, World!' });
      Divider()
      HelloComponent({ message: '你好!' });
    }
  }
}

创建自定义组件

  1. 自定义组建的基本结构
    • struct:自定义组件基于struct实现,struct + 自定义组件名 + {...}的组合构成自定义组件,不能有继承关系。对于struct的实例化,可以省略new。注:自定义组件名、类名、函数名不能和系统组件名相同。
    • @Component:@Component装饰器仅能装饰struct关键字声明的数据结构。struct被@Component装饰后具备组件化的能力,需要实现build方法描述UI,一个struct只能被一个@Component装饰。注: 从API version 9开始,该装饰器支持在ArkTS卡片中使用。
       @Component
       struct MyComponent {
       }
      
  1. 成员函数/变量
    自定义组件除了必须要实现build()函数外,还可以实现其他成员函数,成员函数具有以下约束:
    • 不支持静态函数。
    • 成员函数的访问始终是私有的, 变量的访问规则与成员函数的访问规则相同。
    • 自定义组件的成员变量本地初始化有些是可选的,有些是必选的。具体是否需要本地初始化,是否需要从父组件通过参数传递初始化子组件的成员变量,请参考状态管理。
  2. 自定义组件参数规定
    我们已经了解到,可以在build方法或者@Builder装饰的函数里创建自定义组件,在创建的过程中,参数可以被提供给组件。
     @Component
     struct MyComponent {
       private countDownFrom: number = 0;
       private color: Color = Color.Blue;
    
       build() {
       }
     }
    
     @Entry
     @Component
     struct ParentComponent {
       private someColor: Color = Color.Pink;
    
       build() {
         Column() {
           // 创建MyComponent实例,并将创建MyComponent成员变量countDownFrom初始化为10,将成员变量color初始化为this.someColor
           MyComponent({ countDownFrom: 10, color: this.someColor })
         }
       }
     }
    
  3. build()函数
    所有声明在build()函数的语言,我们统称为UI描述语言,UI描述语言需要遵循以下规则:
    • @Entry装饰的自定义组件,其build()函数下的根节点唯一且必要,且必须为容器组件,其中ForEach禁止作为根节点。@Component装饰的自定义组件,其build()函数下的根节点唯一且必要,可以为非容器组件,其中ForEach禁止作为根节点。
        @Entry
        @Component
        struct MyComponent {
          build() {
            // 根节点唯一且必要,必须为容器组件
            Row() {
              ChildComponent() 
            }
          }
        }
      
        @Component
        struct ChildComponent {
          build() {
            // 根节点唯一且必要,可为非容器组件
            Image('test.jpg')
          }
        }
      
    • build()函数内不允许声明本地变量,反例如下。
      build() {
         // 反例:不允许声明本地变量
         let a: number = 1;
       }
      
  1. 自定义组件通用样式
    自定义组件通过“.”链式调用的形式设置通用样式。
    @Component
     struct MyComponent2 {
       build() {
         Button(`Hello World`)
       }
     }
    
     @Entry
     @Component
     struct MyComponent {
       build() {
         Row() {
           MyComponent2()
             .width(200)
             .height(300)
             .backgroundColor(Color.Red)
         }
       }
     }
    
    注意: ArkUI给自定义组件设置样式时,相当于给MyComponent2套了一个不可见的容器组件,而这些样式是设置在容器组件上的,而非直接设置给MyComponent2的Button组件。通过渲染结果我们可以很清楚的看到,背景颜色红色并没有直接生效在Button上,而是生效在Button所处的开发者不可见的容器组件上。

页面和自定义组件生命周期

页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:

组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:

  1. 自定义组件的创建和渲染流程

    • 自定义组件的创建:自定义组件的实例由ArkUI框架创建。
    • 初始化自定义组件的成员变量:通过本地默认值或者构造方法传递参数来初始化自定义组件的成员变量,初始化顺序为成员变量的定义顺序
    • 如果开发者定义了aboutToAppear,则执行aboutToAppear方法。
    • 在首次渲染的时候,执行build方法渲染系统组件,如果子组件为自定义组件,则创建自定义组件的实例。在执行build()函数的过程中,框架会观察每个状态变量的读取状态,将保存两个map:
      • 状态变量 -> UI组件(包括ForEach和if)。
      • UI组件 -> 此组件的更新函数,即一个lambda方法,作为build()函数的子集,创建对应的UI组件并执行其属性方法,示意如下。
        build() {
           ...
           this.observeComponentCreation(() => {
             Button.create();
           })
        
           this.observeComponentCreation(() => {
             Text.create();
           })
           ...
         }
        
        当应用在后台启动时,此时应用进程并没有销毁,所以仅需要执行onPageShow。
  2. 自定义组件重新渲染
    当事件句柄被触发(比如设置了点击事件,即触发点击事件)改变了状态变量时,或者LocalStorage / AppStorage中的属性更改,并导致绑定的状态变量更改其值时:

    1. 框架观察到了变化,将启动重新渲染。
    2. 根据框架持有的两个map(自定义组件的创建和渲染流程中第4步),框架可以知道该状态变量管理了哪些UI组件,以及这些UI组件对应的更新函数。执行这些UI组件的更新函数,实现最小化更新。
  3. 自定义组件的删除
    如果if组件的分支改变,或者ForEach循环渲染中数组的个数改变,组件将被删除:

    1. 在删除组件之前,将调用其aboutToDisappear生命周期函数,标记着该节点将要被销毁。ArkUI的节点删除机制是:后端节点直接从组件树上摘下,后端节点被销毁,对前端节点解引用,当前端节点已经没有引用时,将被JS虚拟机垃圾回收。
    2. 自定义组件和它的变量将被删除,如果其有同步的变量,比如@Link、@Prop、@StorageLink,将从同步源上取消注册。

    不建议在生命周期aboutToDisappear内使用async await,如果在生命周期的aboutToDisappear使用异步操作(Promise或者回调方法),自定义组件将被保留在Promise的闭包中,直到回调方法被执行完,这个行为阻止了自定义组件的垃圾回收。

    以下示例展示了生命周期的调用时机:

    // Index.ets
       import router from '@ohos.router';
    
       @Entry
       @Component
       struct MyComponent {
         @State showChild: boolean = true;
    
         // 只有被@Entry装饰的组件才可以调用页面的生命周期
         onPageShow() {
           console.info('Index onPageShow');
         }
         // 只有被@Entry装饰的组件才可以调用页面的生命周期
         onPageHide() {
           console.info('Index onPageHide');
         }
    
         // 只有被@Entry装饰的组件才可以调用页面的生命周期
         onBackPress() {
           console.info('Index onBackPress');
         }
    
         // 组件生命周期
         aboutToAppear() {
           console.info('MyComponent aboutToAppear');
         }
    
         // 组件生命周期
         aboutToDisappear() {
           console.info('MyComponent aboutToDisappear');
         }
    
         build() {
           Column() {
             // this.showChild为true,创建Child子组件,执行Child aboutToAppear
             if (this.showChild) {
               Child()
             }
             // this.showChild为false,删除Child子组件,执行Child aboutToDisappear
             Button('create or delete Child').onClick(() => {
               this.showChild = false;
             })
             // push到Page2页面,执行onPageHide
             Button('push to next page')
               .onClick(() => {
                 router.pushUrl({ url: 'pages/Page2' });
               })
           }
    
         }
       }
    
       @Component
       struct Child {
         @State title: string = 'Hello World';
         // 组件生命周期
         aboutToDisappear() {
           console.info('[lifeCycle] Child aboutToDisappear')
         }
         // 组件生命周期
         aboutToAppear() {
           console.info('[lifeCycle] Child aboutToAppear')
         }
    
         build() {
           Text(this.title).fontSize(50).onClick(() => {
             this.title = 'Hello ArkUI';
           })
         }
       }
    

    以上示例中,Index页面包含两个自定义组件,一个是被@Entry装饰的MyComponent,也是页面的入口组件,即页面的根节点;一个是Child,是MyComponent的子组件。只有@Entry装饰的节点才可以生效页面的生命周期方法,所以MyComponent中声明了当前Index页面的页面生命周期函数。MyComponent和其子组件Child也同时也声明了组件的生命周期函数。

    • 应用冷启动的初始化流程为:MyComponent aboutToAppear --> MyComponent build --> Child aboutToAppear --> Child build --> Child build执行完毕 --> MyComponent build执行完毕 --> Index onPageShow。
    • 点击“delete Child”,if绑定的this.showChild变成false,删除Child组件,会执行Child aboutToDisappear方法。
    • 点击“push to next page”,调用router.pushUrl接口,跳转到另外一个页面,当前Index页面隐藏,执行页面生命周期Index onPageHide。此处调用的是router.pushUrl接口,Index页面被隐藏,并没有销毁,所以只调用onPageHide。跳转到新页面后,执行初始化新页面的生命周期的流程。
    • 如果调用的是router.replaceUrl,则当前Index页面被销毁,执行的生命周期流程将变为:Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear。上文已经提到,组件的销毁是从组件树上直接摘下子树,所以先调用父组件的aboutToDisappear,再调用子组件的aboutToDisappear,然后执行初始化新页面的生命周期流程。
    • 点击返回按钮,触发页面生命周期Index onBackPress,且触发返回一个页面后会导致当前Index页面被销毁。
    • 最小化应用或者应用进入后台,触发Index onPageHide。当前Index页面没有被销毁,所以并不会执行组件的aboutToDisappear。应用回到前台,执行Index onPageShow。
    • 退出应用,执行Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear。

@Builder装饰器

使用说明:

  1. 自定义组件内自定义构建函数
  2. 全局自定义构建函数
    • 全局的自定义构建函数可以被整个应用获取,不允许使用this和bind方法。
    • 如果不涉及组件状态变化,建议使用全局的自定义构建方法。

参数传递规则:

  1. 按值传递
    • 调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递。
       @Builder function ABuilder(paramA1: string) {
         Row() {
           Text(`UseStateVarByValue: ${paramA1} `)
         }
       }
       @Entry
       @Component
       struct Parent {
         label: string = 'Hello';
         build() {
           Column() {
             ABuilder(this.label)
           }
         }
       }
      
  2. 按引用传递
    • 按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder方法内的UI刷新。ArkUI提供$$作为按引用传递参数的范式。
        //全局构建
        @Builder function ABuilder($$: { paramA1: string }) {
            Row() {
              Text(`UseStateVarByReference: ${$$.paramA1} `)
            }
          }
      
        //组件内构建
        @Builder ABuilder( $$ : { paramA1: string } );
      
        @Entry
        @Component
        struct Parent {
          @State label: string = 'Hello';
          build() {
            Column() {
              // 在Parent组件中调用ABuilder的时候,将this.label引用传递给ABuilder
              ABuilder({ paramA1: this.label })
              Button('Click me').onClick(() => {
                // 点击“Click me”后,UI从“Hello”刷新为“ArkUI”
                this.label = 'ArkUI';
              })
            }
          }
        }
      

@BuilderParam装饰器:引用@Builder函数

使用场景:

@Styles装饰器:定义组件重用样式

@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用。通过@Styles装饰器可以快速定义并复用自定义样式。用于快速定义并复用自定义样式。可以类比为通用的css样式
使用说明:

    // 定义在全局的@Styles封装的样式
    @Styles function globalFancy  () {
      .width(150)
      .height(100)
      .backgroundColor(Color.Pink)
    }

    @Entry
    @Component
    struct FancyUse {
      @State heightValue: number = 100
      // 定义在组件内的@Styles封装的样式
      @Styles fancy() {
        .width(200)
        .height(this.heightValue)
        .backgroundColor(Color.Yellow)
        .onClick(() => {
          this.heightValue = 200
        })
      }

      build() {
        Column({ space: 10 }) {
          // 使用全局的@Styles封装的样式
          Text('FancyA')
            .globalFancy ()
            .fontSize(30)
          // 使用组件内的@Styles封装的样式
          Text('FancyB')
            .fancy()
            .fontSize(30)
        }
      }
    }

@Extend装饰器:定义扩展组件样式

使用说明:
@Extend(UIComponentName) function functionName { ... }
使用规则:

stateStyles:多态样式

@Styles和@Extend仅仅应用于静态页面的样式复用,stateStyles可以依据组件的内部状态的不同,快速设置不同样式。这就是我们本章要介绍的内容stateStyles(又称为:多态样式)。
stateStyles是属性方法,可以根据UI内部状态来设置样式,类似于css伪类,但语法不同。ArkUI提供以下四种状态:

  1. @Styles和stateStyles联合使用
    正常态和按压态切换
    @Entry
    @Component
    struct MyComponent {
      @Styles normalStyle() {
        .backgroundColor(Color.Gray)
      }
    
      @Styles pressedStyle() {
        .backgroundColor(Color.Red)
      }
    
      build() {
        Column() {
          Text('Text1')
            .fontSize(50)
            .fontColor(Color.White)
            .stateStyles({
              normal: this.normalStyle,
              pressed: this.pressedStyle,
            })
        }
      }
    }
    
  2. 在stateStyles里使用常规变量和状态变量
    Button默认获焦显示红色,点击事件触发后,获焦态变为粉色。
     @Entry
     @Component
     struct CompWithInlineStateStyles {
       @State focusedColor: Color = Color.Red;
       normalColor: Color = Color.Green
    
       build() {
         Button('clickMe').height(100).width(100)
           .stateStyles({
             normal: {
               .backgroundColor(this.normalColor)
             },
             focused: {
               .backgroundColor(this.focusedColor)
             }
           })
           .onClick(() => {
             this.focusedColor = Color.Pink
           })
           .margin('30%')
       }
     }
    

ArkTs状态管理

管理应用拥有的状态概述:
装饰器仅能在页面内,即一个组件树上共享状态变量。如果开发者要实现应用级的,或者多个页面的状态数据共享,就需要用到应用级别的状态管理的概念。ArkTS根据不同特性,提供了多种应用状态管理的能力:

ArkTs渲染控制

ArkUI通过自定义组件的build()函数和@builder装饰器中的声明式UI描述语句构建相应的UI。在声明式描述语句中开发者除了使用系统组件外,还可以使用渲染控制语句来辅助UI的构建,这些渲染控制语句包括控制组件是否显示的条件渲染语句,基于数组数据快速生成组件的循环渲染语句以及针对大数据量场景的数据懒加载语句。

stage模型应用

Stage模型开发概述

[图片上传失败...(image-ed30bb-1697789591540)]

Stage模型应用/组件级配置

UIAbility组件概述

AbilityStage组件容器

AbilityStage是一个Module级别的组件容器,应用的HAP在首次加载时会创建一个AbilityStage实例,可以对该Module进行初始化等操作。

AbilityStage与Module一一对应,即一个Module拥有一个AbilityStage。
我理解可以类比为Android的Application

具体创建步骤如下:

  1. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为myabilitystage。
  2. 在myabilitystage目录,右键选择“New > TypeScript File”,新建一个TypeScript文件并命名为MyAbilityStage.ts。
  3. 打开MyAbilityStage.ts文件,导入AbilityStage的依赖包,自定义类继承AbilityStage并加上需要的生命周期回调,示例中增加了一个onCreate()生命周期回调。
  4. 在module.json5配置文件中,通过配置srcEntry参数来指定模块对应的代码路径,以作为HAP加载的入口。
     {
     "module": {
       "name": "entry",
       "type": "entry",
       "srcEntry": "./ets/myabilitystage/MyAbilityStage.ts",
       ...
     }
    }
    

AbilityStage拥有onCreate()生命周期回调和onAcceptWant()、onConfigurationUpdated()、onMemoryLevel()事件回调。

应用被切换到后台时,系统会将在后台的应用保留在缓存中。即使应用处于缓存中,也会影响系统整体性能。当系统资源不足时,系统会通过多种方式从应用中回收内存,必要时会完全停止应用,从而释放内存用于执行关键任务。为了进一步保持系统内存的平衡,避免系统停止用户的应用进程,可以在AbilityStage中的onMemoryLevel()生命周期回调中订阅系统内存的变化情况,释放不必要的资源。

应用上下文Context

[图片上传失败...(image-fac77d-1697789591541)]

订阅进程内Ability生命周期变化

在应用内的DFX统计场景,如需要统计对应页面停留时间和访问频率等信息,可以使用订阅进程内Ability生命周期变化功能。

在进程内Ability生命周期变化时,如创建、可见/不可见、获焦/失焦、销毁等,会触发进入相应的回调,其中返回的此次注册监听生命周期的ID(每次注册该ID会自增+1,当超过监听上限数量2^63-1时,返回-1),以在UIAbilityContext中使用为例进行说明。

import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';

const TAG: string = "[Example].[Entry].[EntryAbility]";

export default class EntryAbility extends UIAbility {
    lifecycleId: number;

    onCreate(want, launchParam) {
        let abilityLifecycleCallback = {
            onAbilityCreate(ability) {
                console.info(TAG, "onAbilityCreate ability:" + JSON.stringify(ability));
            },
            onWindowStageCreate(ability, windowStage) {
                console.info(TAG, "onWindowStageCreate ability:" + JSON.stringify(ability));
                console.info(TAG, "onWindowStageCreate windowStage:" + JSON.stringify(windowStage));
            },
            onWindowStageActive(ability, windowStage) {
                console.info(TAG, "onWindowStageActive ability:" + JSON.stringify(ability));
                console.info(TAG, "onWindowStageActive windowStage:" + JSON.stringify(windowStage));
            },
            onWindowStageInactive(ability, windowStage) {
                console.info(TAG, "onWindowStageInactive ability:" + JSON.stringify(ability));
                console.info(TAG, "onWindowStageInactive windowStage:" + JSON.stringify(windowStage));
            },
            onWindowStageDestroy(ability, windowStage) {
                console.info(TAG, "onWindowStageDestroy ability:" + JSON.stringify(ability));
                console.info(TAG, "onWindowStageDestroy windowStage:" + JSON.stringify(windowStage));
            },
            onAbilityDestroy(ability) {
                console.info(TAG, "onAbilityDestroy ability:" + JSON.stringify(ability));
            },
            onAbilityForeground(ability) {
                console.info(TAG, "onAbilityForeground ability:" + JSON.stringify(ability));
            },
            onAbilityBackground(ability) {
                console.info(TAG, "onAbilityBackground ability:" + JSON.stringify(ability));
            },
            onAbilityContinue(ability) {
                console.info(TAG, "onAbilityContinue ability:" + JSON.stringify(ability));
            }
        }
        // 1. 通过context属性获取applicationContext
        let applicationContext = this.context.getApplicationContext();
        // 2. 通过applicationContext注册监听应用内生命周期
        this.lifecycleId = applicationContext.on("abilityLifecycle", abilityLifecycleCallback);
        console.info(TAG, "register callback number: " + JSON.stringify(this.lifecycleId));
    }

    onDestroy() {
        let applicationContext = this.context.getApplicationContext();
        applicationContext.off("abilityLifecycle", this.lifecycleId, (error, data) => {
            console.info(TAG, "unregister callback success, err: " + JSON.stringify(error));
        });
    }
}

信息传递载体Want

作用类比于Android中的Intent

Want的类型

Want参数说明在此

显式Want与隐式Want匹配规则在此

常见action与entities

action:表示调用方要执行的通用操作(如查看、分享、应用详情)。在隐式Want中,您可定义该字段,配合uri或parameters来表示对数据要执行的操作。如打开,查看该uri数据。例如,当uri为一段网址,action为ohos.want.action.viewData则表示匹配可查看该网址的Ability。在Want内声明action字段表示希望被调用方应用支持声明的操作。在被调用方应用配置文件skills字段内声明actions表示该应用支持声明操作。

entities:表示目标Ability的类别信息(如浏览器、视频播放器),在隐式Want中是对action的补充。在隐式Want中,开发者可定义该字段,来过滤匹配应用的类别,例如必须是浏览器。在Want内声明entities字段表示希望被调用方应用属于声明的类别。在被调用方应用配置文件skills字段内声明entites表示该应用支持的类别。

使用显式Want启动Ability在此

使用隐式Want打开网址

应用间使用Want分享数据

进程模型

基于HarmonyOS的进程模型,系统提供了公共事件机制用于一对多的通信场景,公共事件发布者可能存在多个订阅者同时接收事件。

公共事件订阅概述

公共事件服务提供了动态订阅和静态订阅两种订阅方式。动态订阅与静态订阅最大的区别在于,动态订阅是应用运行时行为,而静态订阅是后台服务无需处于运行状态。

动态订阅公共事件

场景介绍:
动态订阅是指当应用在运行状态时对某个公共事件进行订阅,在运行期间如果有订阅的事件发布那么订阅了这个事件的应用将会收到该事件及其传递的参数。例如,某应用希望在其运行期间收到电量过低的事件,并根据该事件降低其运行功耗,那么该应用便可动态订阅电量过低事件,收到该事件后关闭一些非必要的任务来降低功耗。订阅部分系统公共事件需要先申请权限,订阅这些事件所需要的权限请见公共事件权限列表

动态订阅接口说明

开发步骤:

  1. 导入CommonEvent模块。import commonEvent from '@ohos.commonEventManager';
  2. 创建订阅者信息,详细的订阅者信息数据类型及包含的参数请见CommonEventSubscribeInfo文档介绍。
     // 用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作
     let subscriber = null;
     // 订阅者信息
     let subscribeInfo = {
         events: ["usual.event.SCREEN_OFF"], // 订阅灭屏公共事件
     }
    
  3. 创建订阅者,保存返回的订阅者对象subscriber,用于执行后续的订阅、退订等操作。
    // 创建订阅者回调
     commonEvent.createSubscriber(subscribeInfo, (err, data) => {
         if (err) {
             console.error(`[CommonEvent] CreateSubscriberCallBack err=${JSON.stringify(err)}`);
         } else {
             console.info(`[CommonEvent] CreateSubscriber success`);
             subscriber = data;
             // 订阅公共事件回调
         }
     })
    
  4. 创建订阅回调函数,订阅回调函数会在接收到事件时触发。订阅回调函数返回的data内包含了公共事件的名称、发布者携带的数据等信息,公共事件数据的详细参数和数据类型请见CommonEventData文档介绍。
     // 订阅公共事件回调
     if (subscriber !== null) {
         commonEvent.subscribe(subscriber, (err, data) => {
             if (err) {
                 console.error(`[CommonEvent] SubscribeCallBack err=${JSON.stringify(err)}`);
             } else {
                 console.info(`[CommonEvent] SubscribeCallBack data=${JSON.stringify(data)}`);
             }
         })
     } else {
         console.error(`[CommonEvent] Need create subscriber`);
     }
    

静态订阅公共事件(仅对系统应用开放)

静态订阅者在未接收订阅的目标事件时,处于未拉起状态,当系统或应用发布了指定的公共事件后,静态订阅者将被拉起,并执行onReceiveEvent回调,开发者可通过在onReceiveEvent回调中执行业务逻辑,实现当应用接收到特定公共事件时执行业务逻辑的目的。例如,某应用希望在设备开机的时候执行一些初始化任务,那么该应用可以静态订阅开机事件,在收到开机事件后会拉起该应用,然后执行初始化任务。静态订阅是通过配置文件声明和实现继承自StaticSubscriberExtensionAbility的类实现对公共事件的订阅。需要注意的是,静态订阅公共事件对系统功耗有一定影响,建议谨慎使用。

取消动态订阅公共事件

动态订阅者完成业务需要时,需要主动取消订阅,订阅者通过调用unsubscribe()方法取消订阅事件。
开发步骤:

  1. 导入CommonEvent模块。
    import commonEvent from '@ohos.commonEventManager';
  2. 根据动态订阅公共事件章节的步骤来订阅某个事件。
  3. 调用CommonEvent中的unsubscribe方法取消订阅某事件。
     // subscriber为订阅事件时创建的订阅者对象
     if (subscriber !== null) {
         commonEvent.unsubscribe(subscriber, (err) => {
             if (err) {
                 console.error(`[CommonEvent] UnsubscribeCallBack err=${JSON.stringify(err)}`)
             } else {
                 console.info(`[CommonEvent] Unsubscribe`)
                 subscriber = null
             }
         })
     }
    

公共事件发布

当需要发布某个自定义公共事件时,可以通过publish()方法发布事件。发布的公共事件可以携带数据,供订阅者解析并进行下一步处理。
接口说明

线程模型

线程模型概述

HarmonyOS应用中每个进程都会有一个主线程,主线程有如下职责:

  1. 执行UI绘制;
  2. 管理主线程的ArkTS引擎实例,使多个UIAbility组件能够运行在其之上;
  3. 管理其他线程(例如Worker线程)的ArkTS引擎实例,例如启动和终止其他线程;
  4. 分发交互事件;
  5. 处理应用代码的回调,包括事件处理和生命周期管理;
  6. 接收Worker线程发送的消息;

除主线程外,还有一类与主线程并行的独立线程Worker,主要用于执行耗时操作,但不可以直接操作UI。Worker线程在主线程中创建,与主线程相互独立。最多可以创建8个Worker

基于HarmonyOS的线程模型,不同的业务功能运行在不同的线程上,业务功能的交互就需要线程间通信。线程间通信目前主要有EmitterWorker两种方式,其中Emitter主要适用于线程间的事件同步Worker主要用于新开一个线程执行耗时任务

使用Emitter进行线程间通信

Emitter主要提供线程间发送和处理事件的能力,包括对持续订阅事件或单次订阅事件的处理、取消订阅事件、发送事件到事件队列等。

使用Worker进行线程间通信

Worker是与主线程并行的独立线程。创建Worker的线程被称为宿主线程,Worker工作的线程被称为Worker线程。创建Worker时传入的脚本文件在Worker线程中执行,通常在Worker线程中处理耗时的操作,需要注意的是,Worker中不能直接更新Page。

UI开发

方舟开发框架(ArkUI)概述

开发布局

添加组件

添加常用组件

添加气泡和菜单

设置页面路由和组件导航

页面路由router

组件导航

显示图形

使用动画

页面内动画

支持交互事件

使用通用事件

使用手势事件

Web相关

ArkTS语言基础类库概述

安全

网络与连接

电话服务

数据管理

文件管理

后台任务

设备管理

通知

窗口管理

WebGL

WebGL的全称为Web Graphic Library(网页图形库),主要用于交互式渲染2D图形和3D图形。目前HarmonyOS中使用的WebGL是基于OpenGL裁剪的OpenGL ES,可以在HTML5的canvas元素对象中使用,无需使用插件,支持跨平台。WebGL程序是由JavaScript代码组成的,其中使用的API可以利用用户设备提供的GPU硬件完成图形渲染和加速。更多信息请参考WebGL™标准。

媒体

自动化测试

Native Api相关

上一篇下一篇

猜你喜欢

热点阅读