HarmonyOS開(kāi)發(fā)學(xué)習(xí)筆記

HarmonyOS開(kāi)發(fā)學(xué)習(xí)筆記

基本步驟

  1. 開(kāi)發(fā)環(huán)境搭建 按照官網(wǎng)步驟
  2. 語(yǔ)言選擇 官方建議選擇arkTs(TypeScript的超集)
  3. 官方開(kāi)發(fā) 參考指南 入門(mén)必讀
  4. 官方開(kāi)發(fā) 參考api 實(shí)戰(zhàn)必讀

HarmonyOS實(shí)戰(zhàn)所需基礎(chǔ)能力

  1. 頁(yè)面布局能力
  2. 頁(yè)面之間的數(shù)據(jù)通信能力(Ability和page; Ability和Ability; page和page)
  3. 數(shù)據(jù)存儲(chǔ)和讀取能力
  4. 網(wǎng)絡(luò)請(qǐng)求能力
  5. 權(quán)限管理能力
  6. 媒體播放能力
  7. 通知管理能力
  8. 文件管理能力
  9. web接入交互能力

基于ArkTs應(yīng)用(Stage模型)

  • 工程目錄結(jié)構(gòu)
    • AppScope > app.json5:應(yīng)用的全局配置信息码荔。
    • entry:HarmonyOS工程模塊,編譯構(gòu)建生成一個(gè)HAP包该面。
      • src > main > ets:用于存放ArkTS源碼明吩。
      • src > main > ets > entryability:應(yīng)用/服務(wù)的入口怎炊。
      • src > main > ets > pages:應(yīng)用/服務(wù)包含的頁(yè)面右莱。
      • src > main > resources:用于存放應(yīng)用/服務(wù)所用到的資源文件救拉,如圖形、多媒體腐缤、字符串捌归、布局文件等。關(guān)于資源文件詳見(jiàn)資源分類與訪問(wèn)岭粤。
      • src > main > resources > profile > main_pages.json: 配置頁(yè)面路由
      • src > main > module.json5:Stage模型模塊配置文件惜索。主要包含HAP包的配置信息、應(yīng)用/服務(wù)在具體設(shè)備上的配置信息以及應(yīng)用/服務(wù)的全局配置信息剃浇。具體的配置文件說(shuō)明巾兆,詳見(jiàn)module.json5配置文件
      • build-profile.json5:當(dāng)前的模塊信息、編譯信息配置項(xiàng)偿渡,包括buildOption臼寄、targets配置等。其中targets中可配置當(dāng)前運(yùn)行環(huán)境溜宽,默認(rèn)為HarmonyOS。
      • hvigorfile.ts:模塊級(jí)編譯構(gòu)建任務(wù)腳本质帅,開(kāi)發(fā)者可以自定義相關(guān)任務(wù)和代碼實(shí)現(xiàn)适揉。
    • oh_modules:用于存放三方庫(kù)依賴信息留攒。關(guān)于原npm工程適配ohpm操作,請(qǐng)參考歷史工程遷移嫉嘀。
    • build-profile.json5:應(yīng)用級(jí)配置信息炼邀,包括簽名、產(chǎn)品配置等剪侮。
    • hvigorfile.ts:應(yīng)用級(jí)編譯構(gòu)建任務(wù)腳本拭宁。

HarmonyOS開(kāi)發(fā)中@Entry @Component @State作用

  1. @Entry:這個(gè)注解在自定義組件時(shí)使用,表示該自定義組件為入口組件瓣俯。
  2. @Component:這個(gè)注解在HarmonyOS的UI框架中杰标,表示一個(gè)組件。Component類位于ohos.agp.components包中彩匕,直接派生自java.lang.Object腔剂,并且是HarmonyOS中所有界面組件的直接或間接父類。每一個(gè)組件在屏幕上占用一個(gè)矩形區(qū)域驼仪,在這個(gè)區(qū)域中對(duì)繪制和事件處理做出響應(yīng)掸犬。
  3. @State:這個(gè)注解在組件中定義狀態(tài)變量。狀態(tài)變量變化會(huì)觸發(fā)UI刷新绪爸。它具有以下特征:
    支持多種類型:允許class湾碎、number、boolean奠货、string強(qiáng)類型的按值和按引用類型胜茧。允許這些強(qiáng)類型構(gòu)成的數(shù)組,即Array<class>仇味、Array<string>呻顽、Array<boolean>、Array<number>丹墨。不允許object和any廊遍。
    支持多實(shí)例:組件不同實(shí)例的內(nèi)部狀態(tài)數(shù)據(jù)獨(dú)立。
    內(nèi)部私有:標(biāo)記為@State的屬性是私有變量贩挣,只能在組件內(nèi)訪問(wèn)喉前。
    需要本地初始化:必須為所有@State變量分配初始值,將變量保持未初始化可能導(dǎo)致框架行為未定義王财。
    創(chuàng)建自定義組件時(shí)支持通過(guò)狀態(tài)變量名設(shè)置初始值:在創(chuàng)建組件實(shí)例時(shí)卵迂,可以通過(guò)變量名顯式指定@State狀態(tài)屬性的初始值。

在HarmonyOS開(kāi)發(fā)中Component中@Builder 和build()作用是否一樣

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

@Builder是一種裝飾器,用于修飾函數(shù)挂疆,以指示該函數(shù)是一個(gè)自定義構(gòu)建函數(shù)改览。被@Builder裝飾的函數(shù)需要遵循build()函數(shù)語(yǔ)法規(guī)則下翎,可以將重復(fù)使用的UI元素抽象成一個(gè)方法,然后在build方法中調(diào)用宝当。通過(guò)使用@Builder裝飾器视事,開(kāi)發(fā)者可以更方便地復(fù)用UI元素,簡(jiǎn)化代碼量庆揩。

build()函數(shù)是Component中一個(gè)重要的方法俐东,用于構(gòu)建UI界面。在build()函數(shù)中订晌,開(kāi)發(fā)者可以通過(guò)調(diào)用@Builder裝飾的函數(shù)來(lái)復(fù)用UI元素虏辫,也可以在build()函數(shù)中編寫(xiě)其他的UI構(gòu)建邏輯。因此腾仅,build()函數(shù)和@Builder裝飾器是緊密相關(guān)的乒裆,但并不是相同的概念。

綜上所述推励,@Builder和build()在HarmonyOS的Component中都是重要的開(kāi)發(fā)工具鹤耍,但它們的作用略有不同。@Builder用于定義自定義構(gòu)建函數(shù)验辞,而build()函數(shù)則用于構(gòu)建UI界面稿黄。

HarmonyOS中其它一些常用的裝飾器

  • @Builder/@BuilderParam:特殊的封裝UI描述的方法,細(xì)粒度的封裝和復(fù)用UI描述跌造。
  • @Extend/@Style:擴(kuò)展內(nèi)置組件和封裝屬性樣式杆怕,更靈活地組合內(nèi)置組件。
  • stateStyles:多態(tài)樣式壳贪,可以依據(jù)組件的內(nèi)部狀態(tài)的不同陵珍,設(shè)置不同樣式。

HarmonyOS中Component中幾個(gè)常用的生命周期方法

  • aboutToAppear 組件即將出現(xiàn)時(shí)回調(diào)該接口违施,具體時(shí)機(jī)為在創(chuàng)建自定義組件的新實(shí)例后互纯,在執(zhí)行其build()函數(shù)之前執(zhí)行。 組件生命周期方法
  • aboutToDisappear 在自定義組件即將析構(gòu)銷毀時(shí)執(zhí)行磕蒲。 組件生命周期方法

HarmonyOS中被@Entry裝飾的組件生命周期

  • onPageShow 頁(yè)面每次顯示時(shí)觸發(fā)留潦。 頁(yè)面生命周期方法
  • onPageHide 頁(yè)面每次隱藏時(shí)觸發(fā)一次。 頁(yè)面生命周期方法
  • onBackPress 當(dāng)用戶點(diǎn)擊返回按鈕時(shí)觸發(fā)辣往。頁(yè)面生命周期方法

HarmonyOS開(kāi)發(fā)所需ArkTs基本語(yǔ)法參考文檔

聲明式ui描述

  1. 創(chuàng)建組件
    • 無(wú)參數(shù)
      如果組件的接口定義沒(méi)有包含必選構(gòu)造參數(shù)兔院,則組件后面的“()”不需要配置任何內(nèi)容。例如站削,Divider組件不包含構(gòu)造參數(shù):
      Column() {
         Text('item 1')
         Divider()
         Text('item 2')
       }
      
    • 有參數(shù) 如果組件的接口定義包含構(gòu)造參數(shù)坊萝,則在組件后面的“()”配置相應(yīng)參數(shù)。
      • Image組件的必選參數(shù)src。
        Image('https://xyz/test.jpg')
        
      • Text組件的非必選參數(shù)content屹堰。
        // string類型的參數(shù)
        Text('test')
        // $r形式引入應(yīng)用資源肛冶,可應(yīng)用于多語(yǔ)言場(chǎng)景
        Text($r('app.string.title_value'))
        // 無(wú)參數(shù)形式
        Text()
        
      • 變量或表達(dá)式也可以用于參數(shù)賦值街氢,其中表達(dá)式返回的結(jié)果類型必須滿足參數(shù)類型要求扯键。
        例如,設(shè)置變量或表達(dá)式來(lái)構(gòu)造Image和Text組件的參數(shù)珊肃。
        Image(this.imagePath)
        Image('https://' + this.imageUrl)
        Text(`count: ${this.count}`)
        
  2. 配置屬性
    屬性方法以“.”鏈?zhǔn)秸{(diào)用的方式配置系統(tǒng)組件的樣式和其他屬性丧鸯,建議每個(gè)屬性方法單獨(dú)寫(xiě)一行躁锡。
    • 配置Text組件的字體大小。
      Text('test')
        .fontSize(12)
      
    • 配置組件的多個(gè)屬性。
      Image('test.jpg')
        .alt('error.jpg')    
        .width(100)    
        .height(100)
      
    • 除了直接傳遞常量參數(shù)外京髓,還可以傳遞變量或表達(dá)式。
      Text('hello')
        .fontSize(this.size)
      Image('test.jpg')
        .width(this.count % 2 === 0 ? 100 : 200)    
        .height(this.offset + 100)
      
    • 對(duì)于系統(tǒng)組件让蕾,ArkUI還為其屬性預(yù)定義了一些枚舉類型供開(kāi)發(fā)者調(diào)用铡俐,枚舉類型可以作為參數(shù)傳遞,但必須滿足參數(shù)類型要求招刹。
      例如恬试,可以按以下方式配置Text組件的顏色和字體樣式。
      Text('hello')
        .fontSize(20)
        .fontColor(Color.Red)
        .fontWeight(FontWeight.Bold)
      
  3. 配置事件
    事件方法以“.”鏈?zhǔn)秸{(diào)用的方式配置系統(tǒng)組件支持的事件疯暑,建議每個(gè)事件方法單獨(dú)寫(xiě)一行训柴。
    • 使用lambda表達(dá)式配置組件的事件方法。
      Button('Click me')
        .onClick(() => {
          this.myText = 'ArkUI';
        })
      
    • 使用匿名函數(shù)表達(dá)式配置組件的事件方法妇拯,要求使用bind幻馁,以確保函數(shù)體中的this指向當(dāng)前組件。
      Button('add counter')
        .onClick(function(){
          this.counter += 2;
        }.bind(this))
      
    • 使用組件的成員函數(shù)配置組件的事件方法越锈。
      myClickHandler(): void {
          this.counter += 2;
      }
      ...
        Button('add counter')
          .onClick(this.myClickHandler.bind(this))
      
  4. 配置子組件
    如果組件支持子組件配置仗嗦,則需在尾隨閉包"{...}"中為組件添加子組件的UI描述。Column甘凭、Row稀拐、Stack、Grid对蒲、List等組件都是容器組件钩蚊。
  • 以下是簡(jiǎn)單的Column組件配置子組件的示例。
        Column() {
        Text('Hello')
          .fontSize(100)
        Divider()
        Text(this.myText)
          .fontSize(100)
          .fontColor(Color.Red)
        }
    
  • 容器組件均支持子組件配置蹈矮,可以實(shí)現(xiàn)相對(duì)復(fù)雜的多級(jí)嵌套砰逻。
      Column() {
        Row() {
          Image('test1.jpg')
            .width(100)
            .height(100)
          Button('click +1')
            .onClick(() => {
              console.info('+1 clicked!');
            })
        }
      }
    

自定義組件

在ArkUI中,UI顯示的內(nèi)容均為組件泛鸟,由框架直接提供的稱為系統(tǒng)組件蝠咆,由開(kāi)發(fā)者定義的稱為自定義組件。
自定義組件具有以下特點(diǎn):

  • 可組合:允許開(kāi)發(fā)者組合使用系統(tǒng)組件、及其屬性和方法刚操。
  • 可重用:自定義組件可以被其他組件重用闸翅,并作為不同的實(shí)例在不同的父組件或容器中使用。
  • 數(shù)據(jù)驅(qū)動(dòng)UI更新:通過(guò)狀態(tài)變量的改變菊霜,來(lái)驅(qū)動(dòng)UI的刷新坚冀。

自定義組件的基本用法:

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

  build() {
    // HelloComponent自定義組件組合系統(tǒng)組件Row和Text
    Row() {
      Text(this.message)
        .onClick(() => {
          // 狀態(tài)變量message的改變驅(qū)動(dòng)UI刷新,UI從'Hello, World!'刷新為'Hello, ArkUI!'
          this.message = 'Hello, ArkUI!';
        })
    }
  }
}

HelloComponent可以在其他自定義組件中的build()函數(shù)中多次創(chuàng)建鉴逞,實(shí)現(xiàn)自定義組件的重用记某。

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

創(chuàng)建自定義組件

  1. 自定義組建的基本結(jié)構(gòu)
    • struct:自定義組件基于struct實(shí)現(xiàn),struct + 自定義組件名 + {...}的組合構(gòu)成自定義組件构捡,不能有繼承關(guān)系液南。對(duì)于struct的實(shí)例化,可以省略new勾徽。注:自定義組件名滑凉、類名、函數(shù)名不能和系統(tǒng)組件名相同喘帚。
    • @Component:@Component裝飾器僅能裝飾struct關(guān)鍵字聲明的數(shù)據(jù)結(jié)構(gòu)畅姊。struct被@Component裝飾后具備組件化的能力,需要實(shí)現(xiàn)build方法描述UI啥辨,一個(gè)struct只能被一個(gè)@Component裝飾涡匀。注: 從API version 9開(kāi)始,該裝飾器支持在ArkTS卡片中使用溉知。
       @Component
       struct MyComponent {
       }
      
  • build()函數(shù):build()函數(shù)用于定義自定義組件的聲明式UI描述陨瘩,自定義組件必須定義build()函數(shù)。
    @Component
    struct MyComponent {
      build() {
      }
    }
    
  • @Entry:@Entry裝飾的自定義組件將作為UI頁(yè)面的入口级乍。在單個(gè)UI頁(yè)面中舌劳,最多可以使用@Entry裝飾一個(gè)自定義組件。@Entry可以接受一個(gè)可選的LocalStorage的參數(shù). 注: 從API version 9開(kāi)始玫荣,該裝飾器支持在ArkTS卡片中使用.
      @Entry
      @Component
      struct MyComponent {
      }
    
  1. 成員函數(shù)/變量
    自定義組件除了必須要實(shí)現(xiàn)build()函數(shù)外甚淡,還可以實(shí)現(xiàn)其他成員函數(shù),成員函數(shù)具有以下約束:
    • 不支持靜態(tài)函數(shù)捅厂。
    • 成員函數(shù)的訪問(wèn)始終是私有的, 變量的訪問(wèn)規(guī)則與成員函數(shù)的訪問(wèn)規(guī)則相同贯卦。
    • 自定義組件的成員變量本地初始化有些是可選的,有些是必選的焙贷。具體是否需要本地初始化撵割,是否需要從父組件通過(guò)參數(shù)傳遞初始化子組件的成員變量,請(qǐng)參考狀態(tài)管理辙芍。
  2. 自定義組件參數(shù)規(guī)定
    我們已經(jīng)了解到啡彬,可以在build方法或者@Builder裝飾的函數(shù)里創(chuàng)建自定義組件羹与,在創(chuàng)建的過(guò)程中,參數(shù)可以被提供給組件往踢。
     @Component
     struct MyComponent {
       private countDownFrom: number = 0;
       private color: Color = Color.Blue;
    
       build() {
       }
     }
    
     @Entry
     @Component
     struct ParentComponent {
       private someColor: Color = Color.Pink;
    
       build() {
         Column() {
           // 創(chuàng)建MyComponent實(shí)例菲语,并將創(chuàng)建MyComponent成員變量countDownFrom初始化為10妄辩,將成員變量color初始化為this.someColor
           MyComponent({ countDownFrom: 10, color: this.someColor })
         }
       }
     }
    
  3. build()函數(shù)
    所有聲明在build()函數(shù)的語(yǔ)言佩憾,我們統(tǒng)稱為UI描述語(yǔ)言,UI描述語(yǔ)言需要遵循以下規(guī)則:
    • @Entry裝飾的自定義組件,其build()函數(shù)下的根節(jié)點(diǎn)唯一且必要,且必須為容器組件,其中ForEach禁止作為根節(jié)點(diǎn)。@Component裝飾的自定義組件,其build()函數(shù)下的根節(jié)點(diǎn)唯一且必要,可以為非容器組件创肥,其中ForEach禁止作為根節(jié)點(diǎn)。
        @Entry
        @Component
        struct MyComponent {
          build() {
            // 根節(jié)點(diǎn)唯一且必要,必須為容器組件
            Row() {
              ChildComponent() 
            }
          }
        }
      
        @Component
        struct ChildComponent {
          build() {
            // 根節(jié)點(diǎn)唯一且必要,可為非容器組件
            Image('test.jpg')
          }
        }
      
    • build()函數(shù)內(nèi)不允許聲明本地變量,反例如下。
      build() {
         // 反例:不允許聲明本地變量
         let a: number = 1;
       }
      
  • build()函數(shù)內(nèi)不允許在UI描述里直接使用console.info,但允許在方法或者函數(shù)里使用,反例如下。
    build() {
      // 反例:不允許console.info
      console.info('print debug log');
    }
    
  • build()函數(shù)內(nèi)不允許創(chuàng)建本地的作用域,反例如下。
    build() {
      // 反例:不允許本地作用域
      {
        ...
      }
    }
    
  • build()函數(shù)內(nèi)不允許調(diào)用除了被@Builder裝飾以外的方法,允許系統(tǒng)組件的參數(shù)是TS方法的返回值。
    @Component
    struct ParentComponent {
      doSomeCalculations() {
      }
    
      calcTextValue(): string {
        return 'Hello World';
      }
    
      @Builder doSomeRender() {
        Text(`Hello World`)
      }
    
      build() {
        Column() {
          // 反例:不能調(diào)用沒(méi)有用@Builder裝飾的方法
          this.doSomeCalculations();
          // 正例:可以調(diào)用
          this.doSomeRender();
          // 正例:參數(shù)可以為調(diào)用TS方法的返回值
          Text(this.calcTextValue())
        }
      }
    }
    
  • build()函數(shù)內(nèi)不允許switch語(yǔ)法,如果需要使用條件判斷,請(qǐng)使用if。反例如下。
    build() {
      Column() {
        // 反例:不允許使用switch語(yǔ)法
        switch (expression) {
          case 1:
            Text('...')
            break;
          case 2:
            Image('...')
            break;
          default:
            Text('...')
            break;
        }
      }
    }
    
  • build()函數(shù)內(nèi)不允許使用表達(dá)式,反例如下。
    build() {
      Column() {
        // 反例:不允許使用表達(dá)式
        (this.aVar > 10) ? Text('...') : Image('...')
      }
    }
    
  1. 自定義組件通用樣式
    自定義組件通過(guò)“.”鏈?zhǔn)秸{(diào)用的形式設(shè)置通用樣式振乏。
    @Component
     struct MyComponent2 {
       build() {
         Button(`Hello World`)
       }
     }
    
     @Entry
     @Component
     struct MyComponent {
       build() {
         Row() {
           MyComponent2()
             .width(200)
             .height(300)
             .backgroundColor(Color.Red)
         }
       }
     }
    
    注意: ArkUI給自定義組件設(shè)置樣式時(shí)误澳,相當(dāng)于給MyComponent2套了一個(gè)不可見(jiàn)的容器組件裆装,而這些樣式是設(shè)置在容器組件上的昙沦,而非直接設(shè)置給MyComponent2的Button組件懒熙。通過(guò)渲染結(jié)果我們可以很清楚的看到泌豆,背景顏色紅色并沒(méi)有直接生效在Button上,而是生效在Button所處的開(kāi)發(fā)者不可見(jiàn)的容器組件上。

頁(yè)面和自定義組件生命周期

頁(yè)面生命周期,即被@Entry裝飾的組件生命周期垢夹,提供以下生命周期接口:

  • onPageShow 頁(yè)面每次顯示時(shí)觸發(fā)。 頁(yè)面生命周期方法
  • onPageHide 頁(yè)面每次隱藏時(shí)觸發(fā)一次。 頁(yè)面生命周期方法
  • onBackPress 當(dāng)用戶點(diǎn)擊返回按鈕時(shí)觸發(fā)。頁(yè)面生命周期方法

組件生命周期,即一般用@Component裝飾的自定義組件的生命周期,提供以下生命周期接口:

  • aboutToAppear 組件即將出現(xiàn)時(shí)回調(diào)該接口,具體時(shí)機(jī)為在創(chuàng)建自定義組件的新實(shí)例后,在執(zhí)行其build()函數(shù)之前執(zhí)行。 組件生命周期方法
  • aboutToDisappear 在自定義組件即將析構(gòu)銷毀時(shí)執(zhí)行。 組件生命周期方法
    [圖片上傳失敗...(image-c82397-1697789591540)]
  1. 自定義組件的創(chuàng)建和渲染流程

    • 自定義組件的創(chuàng)建:自定義組件的實(shí)例由ArkUI框架創(chuàng)建。
    • 初始化自定義組件的成員變量:通過(guò)本地默認(rèn)值或者構(gòu)造方法傳遞參數(shù)來(lái)初始化自定義組件的成員變量柠新,初始化順序?yàn)槌蓡T變量的定義順序
    • 如果開(kāi)發(fā)者定義了aboutToAppear痢甘,則執(zhí)行aboutToAppear方法放椰。
    • 在首次渲染的時(shí)候,執(zhí)行build方法渲染系統(tǒng)組件影暴,如果子組件為自定義組件妆兑,則創(chuàng)建自定義組件的實(shí)例潭千。在執(zhí)行build()函數(shù)的過(guò)程中,框架會(huì)觀察每個(gè)狀態(tài)變量的讀取狀態(tài)蝶桶,將保存兩個(gè)map:
      • 狀態(tài)變量 -> UI組件(包括ForEach和if)。
      • UI組件 -> 此組件的更新函數(shù),即一個(gè)lambda方法透硝,作為build()函數(shù)的子集,創(chuàng)建對(duì)應(yīng)的UI組件并執(zhí)行其屬性方法,示意如下。
        build() {
           ...
           this.observeComponentCreation(() => {
             Button.create();
           })
        
           this.observeComponentCreation(() => {
             Text.create();
           })
           ...
         }
        
        當(dāng)應(yīng)用在后臺(tái)啟動(dòng)時(shí)喊巍,此時(shí)應(yīng)用進(jìn)程并沒(méi)有銷毀,所以僅需要執(zhí)行onPageShow海洼。
  2. 自定義組件重新渲染
    當(dāng)事件句柄被觸發(fā)(比如設(shè)置了點(diǎn)擊事件是整,即觸發(fā)點(diǎn)擊事件)改變了狀態(tài)變量時(shí),或者LocalStorage / AppStorage中的屬性更改球化,并導(dǎo)致綁定的狀態(tài)變量更改其值時(shí):

    1. 框架觀察到了變化句伶,將啟動(dòng)重新渲染。
    2. 根據(jù)框架持有的兩個(gè)map(自定義組件的創(chuàng)建和渲染流程中第4步)身冬,框架可以知道該狀態(tài)變量管理了哪些UI組件嘿歌,以及這些UI組件對(duì)應(yīng)的更新函數(shù)。執(zhí)行這些UI組件的更新函數(shù)沪编,實(shí)現(xiàn)最小化更新相嵌。
  3. 自定義組件的刪除
    如果if組件的分支改變饭宾,或者ForEach循環(huán)渲染中數(shù)組的個(gè)數(shù)改變批糟,組件將被刪除:

    1. 在刪除組件之前,將調(diào)用其aboutToDisappear生命周期函數(shù)看铆,標(biāo)記著該節(jié)點(diǎn)將要被銷毀。ArkUI的節(jié)點(diǎn)刪除機(jī)制是:后端節(jié)點(diǎn)直接從組件樹(shù)上摘下,后端節(jié)點(diǎn)被銷毀否淤,對(duì)前端節(jié)點(diǎn)解引用悄但,當(dāng)前端節(jié)點(diǎn)已經(jīng)沒(méi)有引用時(shí),將被JS虛擬機(jī)垃圾回收石抡。
    2. 自定義組件和它的變量將被刪除檐嚣,如果其有同步的變量,比如@Link汁雷、@Prop净嘀、@StorageLink,將從同步源上取消注冊(cè)侠讯。

    不建議在生命周期aboutToDisappear內(nèi)使用async await挖藏,如果在生命周期的aboutToDisappear使用異步操作(Promise或者回調(diào)方法),自定義組件將被保留在Promise的閉包中厢漩,直到回調(diào)方法被執(zhí)行完膜眠,這個(gè)行為阻止了自定義組件的垃圾回收。

    以下示例展示了生命周期的調(diào)用時(shí)機(jī):

    // Index.ets
       import router from '@ohos.router';
    
       @Entry
       @Component
       struct MyComponent {
         @State showChild: boolean = true;
    
         // 只有被@Entry裝飾的組件才可以調(diào)用頁(yè)面的生命周期
         onPageShow() {
           console.info('Index onPageShow');
         }
         // 只有被@Entry裝飾的組件才可以調(diào)用頁(yè)面的生命周期
         onPageHide() {
           console.info('Index onPageHide');
         }
    
         // 只有被@Entry裝飾的組件才可以調(diào)用頁(yè)面的生命周期
         onBackPress() {
           console.info('Index onBackPress');
         }
    
         // 組件生命周期
         aboutToAppear() {
           console.info('MyComponent aboutToAppear');
         }
    
         // 組件生命周期
         aboutToDisappear() {
           console.info('MyComponent aboutToDisappear');
         }
    
         build() {
           Column() {
             // this.showChild為true溜嗜,創(chuàng)建Child子組件宵膨,執(zhí)行Child aboutToAppear
             if (this.showChild) {
               Child()
             }
             // this.showChild為false,刪除Child子組件炸宵,執(zhí)行Child aboutToDisappear
             Button('create or delete Child').onClick(() => {
               this.showChild = false;
             })
             // push到Page2頁(yè)面辟躏,執(zhí)行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頁(yè)面包含兩個(gè)自定義組件土全,一個(gè)是被@Entry裝飾的MyComponent捎琐,也是頁(yè)面的入口組件,即頁(yè)面的根節(jié)點(diǎn)裹匙;一個(gè)是Child瑞凑,是MyComponent的子組件。只有@Entry裝飾的節(jié)點(diǎn)才可以生效頁(yè)面的生命周期方法概页,所以MyComponent中聲明了當(dāng)前Index頁(yè)面的頁(yè)面生命周期函數(shù)籽御。MyComponent和其子組件Child也同時(shí)也聲明了組件的生命周期函數(shù)。

    • 應(yīng)用冷啟動(dòng)的初始化流程為:MyComponent aboutToAppear --> MyComponent build --> Child aboutToAppear --> Child build --> Child build執(zhí)行完畢 --> MyComponent build執(zhí)行完畢 --> Index onPageShow惰匙。
    • 點(diǎn)擊“delete Child”技掏,if綁定的this.showChild變成false,刪除Child組件项鬼,會(huì)執(zhí)行Child aboutToDisappear方法零截。
    • 點(diǎn)擊“push to next page”,調(diào)用router.pushUrl接口秃臣,跳轉(zhuǎn)到另外一個(gè)頁(yè)面,當(dāng)前Index頁(yè)面隱藏,執(zhí)行頁(yè)面生命周期Index onPageHide奥此。此處調(diào)用的是router.pushUrl接口弧哎,Index頁(yè)面被隱藏,并沒(méi)有銷毀稚虎,所以只調(diào)用onPageHide撤嫩。跳轉(zhuǎn)到新頁(yè)面后,執(zhí)行初始化新頁(yè)面的生命周期的流程蠢终。
    • 如果調(diào)用的是router.replaceUrl序攘,則當(dāng)前Index頁(yè)面被銷毀,執(zhí)行的生命周期流程將變?yōu)椋篒ndex onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear寻拂。上文已經(jīng)提到,組件的銷毀是從組件樹(shù)上直接摘下子樹(shù),所以先調(diào)用父組件的aboutToDisappear衙四,再調(diào)用子組件的aboutToDisappear它改,然后執(zhí)行初始化新頁(yè)面的生命周期流程。
    • 點(diǎn)擊返回按鈕慌核,觸發(fā)頁(yè)面生命周期Index onBackPress距境,且觸發(fā)返回一個(gè)頁(yè)面后會(huì)導(dǎo)致當(dāng)前Index頁(yè)面被銷毀。
    • 最小化應(yīng)用或者應(yīng)用進(jìn)入后臺(tái)垮卓,觸發(fā)Index onPageHide垫桂。當(dāng)前Index頁(yè)面沒(méi)有被銷毀,所以并不會(huì)執(zhí)行組件的aboutToDisappear粟按。應(yīng)用回到前臺(tái)诬滩,執(zhí)行Index onPageShow。
    • 退出應(yīng)用钾怔,執(zhí)行Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear碱呼。

@Builder裝飾器

使用說(shuō)明:

  1. 自定義組件內(nèi)自定義構(gòu)建函數(shù)
  2. 全局自定義構(gòu)建函數(shù)
    • 全局的自定義構(gòu)建函數(shù)可以被整個(gè)應(yīng)用獲取,不允許使用this和bind方法宗侦。
    • 如果不涉及組件狀態(tài)變化愚臀,建議使用全局的自定義構(gòu)建方法。

參數(shù)傳遞規(guī)則:

  1. 按值傳遞
    • 調(diào)用@Builder裝飾的函數(shù)默認(rèn)按值傳遞矾利。當(dāng)傳遞的參數(shù)為狀態(tài)變量時(shí)姑裂,狀態(tài)變量的改變不會(huì)引起@Builder方法內(nèi)的UI刷新。所以當(dāng)使用狀態(tài)變量的時(shí)候男旗,推薦使用按引用傳遞舶斧。
       @Builder function ABuilder(paramA1: string) {
         Row() {
           Text(`UseStateVarByValue: ${paramA1} `)
         }
       }
       @Entry
       @Component
       struct Parent {
         label: string = 'Hello';
         build() {
           Column() {
             ABuilder(this.label)
           }
         }
       }
      
  2. 按引用傳遞
    • 按引用傳遞參數(shù)時(shí),傳遞的參數(shù)可為狀態(tài)變量察皇,且狀態(tài)變量的改變會(huì)引起@Builder方法內(nèi)的UI刷新茴厉。ArkUI提供$$作為按引用傳遞參數(shù)的范式泽台。
        //全局構(gòu)建
        @Builder function ABuilder($$: { paramA1: string }) {
            Row() {
              Text(`UseStateVarByReference: ${$$.paramA1} `)
            }
          }
      
        //組件內(nèi)構(gòu)建
        @Builder ABuilder( $$ : { paramA1: string } );
      
        @Entry
        @Component
        struct Parent {
          @State label: string = 'Hello';
          build() {
            Column() {
              // 在Parent組件中調(diào)用ABuilder的時(shí)候,將this.label引用傳遞給ABuilder
              ABuilder({ paramA1: this.label })
              Button('Click me').onClick(() => {
                // 點(diǎn)擊“Click me”后矾缓,UI從“Hello”刷新為“ArkUI”
                this.label = 'ArkUI';
              })
            }
          }
        }
      

@BuilderParam裝飾器:引用@Builder函數(shù)

使用場(chǎng)景:

  • 參數(shù)初始化組件
  • 尾隨閉包初始化組件: 開(kāi)發(fā)者可以將尾隨閉包內(nèi)的內(nèi)容看做@Builder裝飾的函數(shù)傳給@BuilderParam怀酷。示例如下:
    // xxx.ets
      @Component
      struct CustomContainer {
        @Prop header: string;
        @BuilderParam closer: () => void
    
        build() {
          Column() {
            Text(this.header)
              .fontSize(30)
            this.closer()
          }
        }
      }
    
      @Builder function specificParam(label1: string, label2: string) {
        Column() {
          Text(label1)
            .fontSize(30)
          Text(label2)
            .fontSize(30)
        }
      }
    
      @Entry
      @Component
      struct CustomContainerUser {
        @State text: string = 'header';
    
        build() {
          Column() {
            // 創(chuàng)建CustomContainer,在創(chuàng)建CustomContainer時(shí)嗜闻,通過(guò)其后緊跟一個(gè)大括號(hào)“{}”形成尾隨閉包
            // 作為傳遞給子組件CustomContainer @BuilderParam closer: () => void的參數(shù)
            CustomContainer({ header: this.text }) {
              Column() {
                specificParam('testA', 'testB')
              }.backgroundColor(Color.Yellow)
              .onClick(() => {
                this.text = 'changeHeader';
              })
            }
          }
        }
      }
    

@Styles裝飾器:定義組件重用樣式

@Styles裝飾器可以將多條樣式設(shè)置提煉成一個(gè)方法蜕依,直接在組件聲明的位置調(diào)用。通過(guò)@Styles裝飾器可以快速定義并復(fù)用自定義樣式琉雳。用于快速定義并復(fù)用自定義樣式样眠。可以類比為通用的css樣式
使用說(shuō)明:

  • 當(dāng)前@Styles僅支持通用屬性和通用事件翠肘。
  • @Styles方法不支持參數(shù)
  • @Styles可以定義在組件內(nèi)或全局檐束,在全局定義時(shí)需在方法名前面添加function關(guān)鍵字,組件內(nèi)定義時(shí)則不需要添加function關(guān)鍵字锯茄。
  • 定義在組件內(nèi)的@Styles可以通過(guò)this訪問(wèn)組件的常量和狀態(tài)變量厢塘,并可以在@Styles里通過(guò)事件來(lái)改變狀態(tài)變量的值
  • 組件內(nèi)@Styles的優(yōu)先級(jí)高于全局@Styles。
    框架優(yōu)先找當(dāng)前組件內(nèi)的@Styles肌幽,如果找不到晚碾,則會(huì)全局查找。
    // 定義在全局的@Styles封裝的樣式
    @Styles function globalFancy  () {
      .width(150)
      .height(100)
      .backgroundColor(Color.Pink)
    }

    @Entry
    @Component
    struct FancyUse {
      @State heightValue: number = 100
      // 定義在組件內(nèi)的@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)
          // 使用組件內(nèi)的@Styles封裝的樣式
          Text('FancyB')
            .fancy()
            .fontSize(30)
        }
      }
    }

@Extend裝飾器:定義擴(kuò)展組件樣式

使用說(shuō)明:
@Extend(UIComponentName) function functionName { ... }
使用規(guī)則:

  • 和@Styles不同喂急,@Extend僅支持定義在全局格嘁,不支持在組件內(nèi)部定義。
  • 和@Styles不同廊移,@Extend支持封裝指定的組件的私有屬性和私有事件和預(yù)定義相同組件的@Extend的方法糕簿。
        // @Extend(Text)可以支持Text的私有屬性fontColor
      @Extend(Text) function fancy () {
        .fontColor(Color.Red)
      }
      // superFancyText可以調(diào)用預(yù)定義的fancy
      @Extend(Text) function superFancyText(size:number) {
          .fontSize(size)
          .fancy()
      }
    
  • 和@Styles不同,@Extend裝飾的方法支持參數(shù)狡孔,開(kāi)發(fā)者可以在調(diào)用時(shí)傳遞參數(shù)懂诗,調(diào)用遵循TS方法傳值調(diào)用。
        // xxx.ets
        @Extend(Text) function fancy (fontSize: number) {
          .fontColor(Color.Red)
          .fontSize(fontSize)
        }
    
        @Entry
        @Component
        struct FancyUse {
          build() {
            Row({ space: 10 }) {
              Text('Fancy')
                .fancy(16)
              Text('Fancy')
                .fancy(24)
            }
          }
        }
    
  • @Extend裝飾的方法的參數(shù)可以為function苗膝,作為Event事件的句柄殃恒。
      @Extend(Text) function makeMeClick(onClick: () => void) {
        .backgroundColor(Color.Blue)
        .onClick(onClick)
      }
    
      @Entry
      @Component
      struct FancyUse {
        @State label: string = 'Hello World';
    
        onClickHandler() {
          this.label = 'Hello ArkUI';
        }
    
        build() {
          Row({ space: 10 }) {
            Text(`${this.label}`)
              .makeMeClick(this.onClickHandler.bind(this))
          }
        }
      }
    
  • @Extend的參數(shù)可以為狀態(tài)變量,當(dāng)狀態(tài)變量改變時(shí)辱揭,UI可以正常的被刷新渲染离唐。
      @Extend(Text) function fancy (fontSize: number) {
        .fontColor(Color.Red)
        .fontSize(fontSize)
      }
    
      @Entry
      @Component
      struct FancyUse {
        @State fontSizeValue: number = 20
        build() {
          Row({ space: 10 }) {
            Text('Fancy')
              .fancy(this.fontSizeValue)
              .onClick(() => {
                this.fontSizeValue = 30
              })
          }
        }
      }
    

stateStyles:多態(tài)樣式

@Styles和@Extend僅僅應(yīng)用于靜態(tài)頁(yè)面的樣式復(fù)用,stateStyles可以依據(jù)組件的內(nèi)部狀態(tài)的不同问窃,快速設(shè)置不同樣式亥鬓。這就是我們本章要介紹的內(nèi)容stateStyles(又稱為:多態(tài)樣式)。
stateStyles是屬性方法域庇,可以根據(jù)UI內(nèi)部狀態(tài)來(lái)設(shè)置樣式嵌戈,類似于css偽類覆积,但語(yǔ)法不同。ArkUI提供以下四種狀態(tài):

  • focused:獲焦態(tài)
  • normal:正常態(tài)咕别。
  • pressed:按壓態(tài)技健。
  • disabled:不可用態(tài)。
  1. @Styles和stateStyles聯(lián)合使用
    正常態(tài)和按壓態(tài)切換
    @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里使用常規(guī)變量和狀態(tài)變量
    Button默認(rèn)獲焦顯示紅色惰拱,點(diǎn)擊事件觸發(fā)后,獲焦態(tài)變?yōu)榉凵?
     @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狀態(tài)管理

  • @State:@State裝飾的變量擁有其所屬組件的狀態(tài)啊送,可以作為其子組件單向和雙向同步的數(shù)據(jù)源偿短。當(dāng)其數(shù)值改變時(shí),會(huì)引起相關(guān)組件的渲染刷新馋没。
  • @Prop:@Prop裝飾的變量可以和父組件建立單向同步關(guān)系昔逗,@Prop裝飾的變量是可變的,但修改不會(huì)同步回父組件篷朵。
  • @Link:@Link裝飾的變量和父組件構(gòu)建雙向同步關(guān)系的狀態(tài)變量勾怒,父組件會(huì)接受來(lái)自@Link裝飾的變量的修改的同步,父組件的更新也會(huì)同步給@Link裝飾的變量声旺。
  • @Provide/@Consume:@Provide/@Consume裝飾的變量用于跨組件層級(jí)(多層組件)同步狀態(tài)變量笔链,可以不需要通過(guò)參數(shù)命名機(jī)制傳遞,通過(guò)alias(別名)或者屬性名綁定腮猖。
  • @Observed:@Observed裝飾class鉴扫,需要觀察多層嵌套場(chǎng)景的class需要被@Observed裝飾。單獨(dú)使用@Observed沒(méi)有任何作用澈缺,需要和@ObjectLink坪创、@Prop連用。
  • @ObjectLink:@ObjectLink裝飾的變量接收@Observed裝飾的class的實(shí)例姐赡,應(yīng)用于觀察多層嵌套場(chǎng)景莱预,和父組件的數(shù)據(jù)源構(gòu)建雙向同步。

管理應(yīng)用擁有的狀態(tài)概述:
裝飾器僅能在頁(yè)面內(nèi)项滑,即一個(gè)組件樹(shù)上共享狀態(tài)變量依沮。如果開(kāi)發(fā)者要實(shí)現(xiàn)應(yīng)用級(jí)的,或者多個(gè)頁(yè)面的狀態(tài)數(shù)據(jù)共享杖们,就需要用到應(yīng)用級(jí)別的狀態(tài)管理的概念悉抵。ArkTS根據(jù)不同特性,提供了多種應(yīng)用狀態(tài)管理的能力:

  • LocalStorage:頁(yè)面級(jí)UI狀態(tài)存儲(chǔ)摘完,通常用于UIAbility內(nèi)姥饰、頁(yè)面間的狀態(tài)共享。
  • AppStorage:特殊的單例LocalStorage對(duì)象孝治,由UI框架在應(yīng)用程序啟動(dòng)時(shí)創(chuàng)建列粪,為應(yīng)用程序UI狀態(tài)屬性提供中央存儲(chǔ)审磁;
  • PersistentStorage:持久化存儲(chǔ)UI狀態(tài),通常和AppStorage配合使用岂座,選擇AppStorage存儲(chǔ)的數(shù)據(jù)寫(xiě)入磁盤(pán)态蒂,以確保這些屬性在應(yīng)用程序重新啟動(dòng)時(shí)的值與應(yīng)用程序關(guān)閉時(shí)的值相同;LocalStorage和AppStorage都是運(yùn)行時(shí)的內(nèi)存费什,但是在應(yīng)用退出再次啟動(dòng)后钾恢,依然能保存選定的結(jié)果,是應(yīng)用開(kāi)發(fā)中十分常見(jiàn)的現(xiàn)象鸳址,這就需要用到PersistentStorage瘩蚪。
    注意: PersistentStorage的持久化變量最好是小于2kb的數(shù)據(jù),不要大量的數(shù)據(jù)持久化稿黍,因?yàn)镻ersistentStorage寫(xiě)入磁盤(pán)的操作是同步的疹瘦,大量的數(shù)據(jù)本地化讀寫(xiě)會(huì)同步在UI線程中執(zhí)行,影響UI渲染性能巡球。如果開(kāi)發(fā)者需要存儲(chǔ)大量的數(shù)據(jù)言沐,建議使用數(shù)據(jù)庫(kù)api。PersistentStorage只能在UI頁(yè)面內(nèi)使用酣栈,否則將無(wú)法持久化數(shù)據(jù)险胰。
  • Environment:應(yīng)用程序運(yùn)行的設(shè)備的環(huán)境參數(shù),環(huán)境參數(shù)會(huì)同步到AppStorage中钉嘹,可以和AppStorage搭配使用鸯乃。

ArkTs渲染控制

ArkUI通過(guò)自定義組件的build()函數(shù)和@builder裝飾器中的聲明式UI描述語(yǔ)句構(gòu)建相應(yīng)的UI。在聲明式描述語(yǔ)句中開(kāi)發(fā)者除了使用系統(tǒng)組件外跋涣,還可以使用渲染控制語(yǔ)句來(lái)輔助UI的構(gòu)建缨睡,這些渲染控制語(yǔ)句包括控制組件是否顯示的條件渲染語(yǔ)句,基于數(shù)組數(shù)據(jù)快速生成組件的循環(huán)渲染語(yǔ)句以及針對(duì)大數(shù)據(jù)量場(chǎng)景的數(shù)據(jù)懶加載語(yǔ)句陈辱。

  • if/else:條件渲染
    更新機(jī)制:
    當(dāng)if奖年、else if后跟隨的狀態(tài)判斷中使用的狀態(tài)變量值變化時(shí),條件渲染語(yǔ)句會(huì)進(jìn)行更新沛贪,更新步驟如下:

    1. 評(píng)估if和else if的狀態(tài)判斷條件陋守,如果分支沒(méi)有變化,請(qǐng)無(wú)需執(zhí)行以下步驟利赋。如果分支有變化水评,則執(zhí)行2、3步驟:
    2. 刪除此前構(gòu)建的所有子組件媚送。
    3. 執(zhí)行新分支的構(gòu)造函數(shù)中燥,將獲取到的組件添加到if父容器中。如果缺少適用的else分支塘偎,則不構(gòu)建任何內(nèi)容疗涉。

    條件可以包括Typescript表達(dá)式拿霉。對(duì)于構(gòu)造函數(shù)中的表達(dá)式,此類表達(dá)式不得更改應(yīng)用程序狀態(tài)咱扣。

    @Component
    struct CounterView {
      @State counter: number = 0;
      label: string = 'unknown';
    
      build() {
        Row() {
          Text(`${this.label}`)
          Button(`counter ${this.counter} +1`)
            .onClick(() => {
              this.counter += 1;
            })
        }
      }
    }
    
    @Entry
    @Component
    struct MainView {
      @State toggle: boolean = true;
    
      build() {
        Column() {
          if (this.toggle) {
            CounterView({ label: 'CounterView #positive' })
          } else {
            CounterView({ label: 'CounterView #negative' })
          }
          Button(`toggle ${this.toggle}`)
            .onClick(() => {
              this.toggle = !this.toggle;
            })
        }
      }
    }
    
  • ForEach:循環(huán)渲染
    ForEach基于數(shù)組類型數(shù)據(jù)執(zhí)行循環(huán)渲染绽淘。

    • forEach接口描述
      ForEach(
        arr: any[], 
        itemGenerator: (item: any, index?: number) => void,
        keyGenerator?: (item: any, index?: number) => string 
      )
      
    • 使用限制:
      1. ForEach必須在容器組件內(nèi)使用。
      2. 生成的子組件應(yīng)當(dāng)是允許包含在ForEach父容器組件中的子組件闹伪。
      3. 允許子組件生成器函數(shù)中包含if/else條件渲染沪铭,同時(shí)也允許ForEach包含在if/else條件渲染語(yǔ)句中。
      4. itemGenerator函數(shù)的調(diào)用順序不一定和數(shù)組中的數(shù)據(jù)項(xiàng)相同偏瓤,在開(kāi)發(fā)過(guò)程中不要假設(shè)itemGenerator和keyGenerator函數(shù)是否執(zhí)行及其執(zhí)行順序伦意。例如,以下示例可能無(wú)法正確運(yùn)行:
         ForEach(anArray.map((item1, index1) => { return { i: index1 + 1, data: item1 }; }), 
         item => Text(`${item.i}. item.data.label`),
         item => item.data.id.toString())
        
  • LazyForEach:數(shù)據(jù)懶加載
    LazyForEach從提供的數(shù)據(jù)源中按需迭代數(shù)據(jù)硼补,并在每次迭代過(guò)程中創(chuàng)建相應(yīng)的組件。當(dāng)LazyForEach在滾動(dòng)容器中使用了熏矿,框架會(huì)根據(jù)滾動(dòng)容器可視區(qū)域按需創(chuàng)建組件已骇,當(dāng)組件滑出可視區(qū)域外時(shí),框架會(huì)進(jìn)行組件銷毀回收以降低內(nèi)存占用票编。

    • 接口描述
      LazyForEach(
          dataSource: IDataSource,             // 需要進(jìn)行數(shù)據(jù)迭代的數(shù)據(jù)源 
          itemGenerator: (item: any) => void,  // 子組件生成函數(shù)
          keyGenerator?: (item: any) => string // (可選) .鍵值生成函數(shù)
      ): void
      
    • 使用限制
      • LazyForEach必須在容器組件內(nèi)使用褪储,僅有List、Grid以及Swiper組件支持?jǐn)?shù)據(jù)懶加載(可配置cachedCount屬性慧域,即只加載可視部分以及其前后少量數(shù)據(jù)用于緩沖)鲤竹,其他組件仍然是一次性加載所有的數(shù)據(jù)。
      • LazyForEach在每次迭代中昔榴,必須創(chuàng)建且只允許創(chuàng)建一個(gè)子組件辛藻。
      • 生成的子組件必須是允許包含在LazyForEach父容器組件中的子組件。
      • 允許LazyForEach包含在if/else條件渲染語(yǔ)句中互订,也允許LazyForEach中出現(xiàn)if/else條件渲染語(yǔ)句吱肌。
      • 鍵值生成器必須針對(duì)每個(gè)數(shù)據(jù)生成唯一的值,如果鍵值相同仰禽,將導(dǎo)致鍵值相同的UI組件被框架忽略氮墨,從而無(wú)法在父容器內(nèi)顯示。
      • LazyForEach必須使用DataChangeListener對(duì)象來(lái)進(jìn)行更新吐葵,第一個(gè)參數(shù)dataSource使用狀態(tài)變量時(shí)规揪,狀態(tài)變量改變不會(huì)觸發(fā)LazyForEach的UI刷新。
      • 為了高性能渲染温峭,通過(guò)DataChangeListener對(duì)象的onDataChange方法來(lái)更新UI時(shí)猛铅,需要生成不同于原來(lái)的鍵值來(lái)觸發(fā)組件刷新。
      • itemGenerator函數(shù)的調(diào)用順序不一定和數(shù)據(jù)源中的數(shù)據(jù)項(xiàng)相同诚镰,在開(kāi)發(fā)過(guò)程中不要假設(shè)itemGenerator和keyGenerator函數(shù)是否執(zhí)行及其執(zhí)行順序奕坟。例如祥款,以下示例可能無(wú)法正常運(yùn)行:
         LazyForEach(dataSource, 
           item => Text(`${item.i}. item.data.label`),
           item => item.data.id.toString())
        
    • 示例
      // Basic implementation of IDataSource to handle data listener
      class BasicDataSource implements IDataSource {
        private listeners: DataChangeListener[] = [];
      
        public totalCount(): number {
          return 0;
        }
      
        public getData(index: number): any {
          return undefined;
        }
      
        registerDataChangeListener(listener: DataChangeListener): void {
          if (this.listeners.indexOf(listener) < 0) {
            console.info('add listener');
            this.listeners.push(listener);
          }
        }
      
        unregisterDataChangeListener(listener: DataChangeListener): void {
          const pos = this.listeners.indexOf(listener);
          if (pos >= 0) {
            console.info('remove listener');
            this.listeners.splice(pos, 1);
          }
        }
      
        notifyDataReload(): void {
          this.listeners.forEach(listener => {
            listener.onDataReloaded();
          })
        }
      
        notifyDataAdd(index: number): void {
          this.listeners.forEach(listener => {
            listener.onDataAdd(index);
          })
        }
      
        notifyDataChange(index: number): void {
          this.listeners.forEach(listener => {
            listener.onDataChange(index);
          })
        }
      
        notifyDataDelete(index: number): void {
          this.listeners.forEach(listener => {
            listener.onDataDelete(index);
          })
        }
      
        notifyDataMove(from: number, to: number): void {
          this.listeners.forEach(listener => {
            listener.onDataMove(from, to);
          })
        }
      }
      
      class MyDataSource extends BasicDataSource {
        private dataArray: string[] = [];
      
        public totalCount(): number {
          return this.dataArray.length;
        }
      
        public getData(index: number): any {
          return this.dataArray[index];
        }
      
        public addData(index: number, data: string): void {
          this.dataArray.splice(index, 0, data);
          this.notifyDataAdd(index);
        }
      
        public pushData(data: string): void {
          this.dataArray.push(data);
          this.notifyDataAdd(this.dataArray.length - 1);
        }
      }
      
      @Entry
      @Component
      struct MyComponent {
        aboutToAppear() {
          for (var i = 100; i >= 80; i--) {
            this.data.pushData(`Hello ${i}`)
          }
        }
      
        private data: MyDataSource = new MyDataSource();
      
        build() {
          List({ space: 3 }) {
            LazyForEach(this.data, (item: string) => {
              ListItem() {
                Row() {
                  Text(item).fontSize(50)
                    .onAppear(() => {
                      console.info("appear:" + item)
                    })
                }.margin({ left: 10, right: 10 })
              }
              .onClick(() => {
                this.data.pushData(`Hello ${this.data.totalCount()}`);
              })
            }, item => item)
          }.cachedCount(5)
        }
      }
      

stage模型應(yīng)用

Stage模型開(kāi)發(fā)概述

[圖片上傳失敗...(image-ed30bb-1697789591540)]

  • UIAbility組件和ExtensionAbility組件
    Stage模型提供UIAbility和ExtensionAbility兩種類型的組件,這兩種組件都有具體的類承載月杉,支持面向?qū)ο蟮拈_(kāi)發(fā)方式刃跛。

    • UIAbility組件是一種包含UI界面的應(yīng)用組件,主要用于和用戶交互苛萎。
    • ExtensionAbility組件是一種面向特定場(chǎng)景的應(yīng)用組件桨昙。
  • WindowStage
    每個(gè)UIAbility類實(shí)例都會(huì)與一個(gè)WindowStage類實(shí)例綁定,該類提供了應(yīng)用進(jìn)程內(nèi)窗口管理器的作用腌歉。它包含一個(gè)主窗口蛙酪。也就是說(shuō)UIAbility通過(guò)WindowStage持有了一個(gè)窗口,該窗口為ArkUI提供了繪制區(qū)域翘盖。

  • Context
    在Stage模型上桂塞,Context及其派生類向開(kāi)發(fā)者提供在運(yùn)行期可以調(diào)用的各種能力。UIAbility組件和各種ExtensionAbility派生類都有各自不同的Context類馍驯,他們都繼承自基類Context阁危,但是各自又根據(jù)所屬組件,提供不同的能力汰瘫。

  • AbilityStage
    每個(gè)Entry類型或者Feature類型的HAP在運(yùn)行期都有一個(gè)AbilityStage類實(shí)例狂打,當(dāng)HAP中的代碼首次被加載到進(jìn)程中的時(shí)候,系統(tǒng)會(huì)先創(chuàng)建AbilityStage實(shí)例混弥。每個(gè)在該HAP中定義的UIAbility類趴乡,在實(shí)例化后都會(huì)與該實(shí)例產(chǎn)生關(guān)聯(lián)。開(kāi)發(fā)者可以使用AbilityStage獲取該HAP中UIAbility實(shí)例的運(yùn)行時(shí)信息蝗拿。

Stage模型應(yīng)用/組件級(jí)配置

  • 應(yīng)用包名配置
    應(yīng)用需要在工程的AppScope目錄下的app.json5配置文件中配置bundleName標(biāo)簽晾捏,該標(biāo)簽用于標(biāo)識(shí)應(yīng)用的唯一性丁逝。推薦采用反域名形式命名(如com.example.demo官份,建議第一級(jí)為域名后綴com逛薇,第二級(jí)為廠商/個(gè)人名引润,第三級(jí)為應(yīng)用名酒甸,也可以多級(jí))兼贸。
  • 應(yīng)用圖標(biāo)和標(biāo)簽配置
    Stage模型的應(yīng)用需要配置應(yīng)用圖標(biāo)和應(yīng)用標(biāo)簽忍宋。應(yīng)用圖標(biāo)和標(biāo)簽是在設(shè)置應(yīng)用中使用咧党,例如設(shè)置應(yīng)用中的應(yīng)用列表俗或,會(huì)顯示出對(duì)應(yīng)的圖標(biāo)和標(biāo)簽市怎。
    應(yīng)用圖標(biāo)需要在工程的AppScope目錄下的app.json5配置文件中配置icon標(biāo)簽。應(yīng)用圖標(biāo)需配置為圖片的資源索引辛慰,配置完成后区匠,該圖片即為應(yīng)用的圖標(biāo)。
    應(yīng)用標(biāo)簽需要在工程的AppScope模塊下的app.json5配置文件中配置label標(biāo)簽。標(biāo)識(shí)應(yīng)用對(duì)用戶顯示的名稱驰弄,需要配置為字符串資源的索引麻汰。
  • 入口圖標(biāo)和標(biāo)簽配置
    Stage模型支持對(duì)組件配置入口圖標(biāo)和入口標(biāo)簽。入口圖標(biāo)和入口標(biāo)簽會(huì)顯示在桌面上戚篙。
    入口圖標(biāo)需要在module.json5配置文件中配置五鲫,在abilities標(biāo)簽下面有icon標(biāo)簽。例如希望在桌面上顯示該UIAbility的圖標(biāo)岔擂,則需要在skills標(biāo)簽下面的entities中添加"entity.system.home"位喂、actions中添加"action.system.home"。同一個(gè)應(yīng)用有多個(gè)UIAbility配置上述字段時(shí)乱灵,桌面上會(huì)顯示出多個(gè)圖標(biāo)塑崖,分別對(duì)應(yīng)各自的UIAbility。

UIAbility組件概述

  • UIAbility概述:
    UIAbility組件是一種包含UI界面的應(yīng)用組件痛倚,主要用于和用戶交互规婆。
    UIAbility組件是系統(tǒng)調(diào)度的基本單元,為應(yīng)用提供繪制界面的窗口蝉稳;一個(gè)UIAbility組件中可以通過(guò)多個(gè)頁(yè)面來(lái)實(shí)現(xiàn)一個(gè)功能模塊聋呢。每一個(gè)UIAbility組件實(shí)例,都對(duì)應(yīng)于一個(gè)最近任務(wù)列表中的任務(wù)颠区。

  • UIAbility組件生命周期

    • UIAbility的生命周期包括Create、Foreground通铲、Background毕莱、Destroy四個(gè)狀態(tài)
  • UIAbility組件啟動(dòng)模式

    • singleton(單實(shí)例模式)
      每次調(diào)用startAbility()方法時(shí),如果應(yīng)用進(jìn)程中該類型的UIAbility實(shí)例已經(jīng)存在颅夺,則復(fù)用系統(tǒng)中的UIAbility實(shí)例朋截。系統(tǒng)中只存在唯一一個(gè)該UIAbility實(shí)例,即在最近任務(wù)列表中只存在一個(gè)該類型的UIAbility實(shí)例吧黄。
      如果需要使用singleton啟動(dòng)模式部服,在module.json5配置文件中的"launchType"字段配置為"singleton"即可。
          {
          "module": {
            // ...
            "abilities": [
              {
                "launchType": "singleton",
                // ...
              }
            ]
          }
        }
      
    • standard(標(biāo)準(zhǔn)實(shí)例模式)
      standard啟動(dòng)模式為標(biāo)準(zhǔn)實(shí)例模式拗慨,每次調(diào)用startAbility()方法時(shí)廓八,都會(huì)在應(yīng)用進(jìn)程中創(chuàng)建一個(gè)新的該類型UIAbility實(shí)例。即在最近任務(wù)列表中可以看到有多個(gè)該類型的UIAbility實(shí)例赵抢。這種情況下可以將UIAbility配置為standard(標(biāo)準(zhǔn)實(shí)例模式)剧蹂。
      standard啟動(dòng)模式的開(kāi)發(fā)使用,在module.json5配置文件中的"launchType"字段配置為"standard"即可烦却。
          {
          "module": {
            // ...
            "abilities": [
              {
                "launchType": "standard",
                // ...
              }
            ]
          }
        }
      
    • specified(指定實(shí)例模式)
      specified啟動(dòng)模式為指定實(shí)例模式宠叼,針對(duì)一些特殊場(chǎng)景使用(例如文檔應(yīng)用中每次新建文檔希望都能新建一個(gè)文檔實(shí)例,重復(fù)打開(kāi)一個(gè)已保存的文檔希望打開(kāi)的都是同一個(gè)文檔實(shí)例)其爵。
      在UIAbility實(shí)例創(chuàng)建之前冒冬,允許開(kāi)發(fā)者為該實(shí)例創(chuàng)建一個(gè)唯一的字符串Key伸蚯,創(chuàng)建的UIAbility實(shí)例綁定Key之后,后續(xù)每次調(diào)用startAbility()方法時(shí)简烤,都會(huì)詢問(wèn)應(yīng)用使用哪個(gè)Key對(duì)應(yīng)的UIAbility實(shí)例來(lái)響應(yīng)startAbility()請(qǐng)求剂邮。運(yùn)行時(shí)由UIAbility內(nèi)部業(yè)務(wù)決定是否創(chuàng)建多實(shí)例,如果匹配有該UIAbility實(shí)例的Key乐埠,則直接拉起與之綁定的UIAbility實(shí)例抗斤,否則創(chuàng)建一個(gè)新的UIAbility實(shí)例。
  • UIAbility組件基本用法
    UIAbility組件的基本用法包括:指定UIAbility的啟動(dòng)頁(yè)面以及獲取UIAbility的上下文UIAbilityContext丈咐。

    • 指定UIAbility的啟動(dòng)頁(yè)面
      應(yīng)用中的UIAbility在啟動(dòng)過(guò)程中瑞眼,需要指定啟動(dòng)頁(yè)面,否則應(yīng)用啟動(dòng)后會(huì)因?yàn)闆](méi)有默認(rèn)加載頁(yè)面而導(dǎo)致白屏棵逊∩烁恚可以在UIAbility的onWindowStageCreate()生命周期回調(diào)中,通過(guò)WindowStage對(duì)象的loadContent()方法設(shè)置啟動(dòng)頁(yè)面辆影。

        import UIAbility from '@ohos.app.ability.UIAbility';
        import Window from '@ohos.window';
      
          export default class EntryAbility extends UIAbility {
            onWindowStageCreate(windowStage: Window.WindowStage) {
                // Main window is created, set main page for this ability
                windowStage.loadContent('pages/Index', (err, data) => {
                    // ...
                });
            }
      
            // ...
        }
      
    • 獲取UIAbility的上下文信息

      • 在UIAbility中可以通過(guò)this.context獲取UIAbility實(shí)例的上下文信息徒像。
        let context = this.context;
      • 在頁(yè)面中獲取UIAbility實(shí)例的上下文信息,包括導(dǎo)入依賴資源context模塊和在組件中定義一個(gè)context變量?jī)蓚€(gè)部分蛙讥。
        import common from '@ohos.app.ability.common';
        @Entry
        @Component
        struct Index {
          private context = getContext(this) as common.UIAbilityContext;
        
          startAbilityTest() {
            let want = {
              // Want參數(shù)信息
            };
            this.context.startAbility(want);
          }
        
          // 頁(yè)面展示
          build() {
            // ...
          }
        }
        
        也可以在導(dǎo)入依賴資源context模塊后锯蛀,在具體使用UIAbilityContext前進(jìn)行變量定義。
          startAbilityTest() {
          let context = getContext(this) as common.UIAbilityContext;
          let want = {
            // Want參數(shù)信息
          };
          context.startAbility(want);
        }
        
  • UIAbility組件與UI的數(shù)據(jù)同步
    基于HarmonyOS的應(yīng)用模型次慢,可以通過(guò)以下兩種方式來(lái)實(shí)現(xiàn)UIAbility組件與UI之間的數(shù)據(jù)同步旁涤。

    1. EventHub:基于發(fā)布訂閱模式來(lái)實(shí)現(xiàn),事件需要先訂閱后發(fā)布迫像,訂閱者收到消息后進(jìn)行處理劈愚。
      可類比為android中的EventBus
    2. globalThis:ArkTS引擎實(shí)例內(nèi)部的一個(gè)全局對(duì)象,在ArkTS引擎實(shí)例內(nèi)部都能訪問(wèn)闻妓。
      使用globalThis進(jìn)行數(shù)據(jù)同步[圖片上傳失敗...(image-852b6f-1697789591540)]
      • UIAbility和Page之間使用globalThis
        globalThis為ArkTS引擎實(shí)例下的全局對(duì)象菌羽,可以通過(guò)globalThis綁定屬性/方法來(lái)進(jìn)行UIAbility組件與UI的數(shù)據(jù)同步。例如在UIAbility組件中綁定want參數(shù)由缆,即可在UIAbility對(duì)應(yīng)的UI界面上使用want參數(shù)信息注祖。
        1. 調(diào)用startAbility()方法啟動(dòng)一個(gè)UIAbility實(shí)例時(shí),被啟動(dòng)的UIAbility創(chuàng)建完成后會(huì)進(jìn)入onCreate()生命周期回調(diào)均唉,且在onCreate()生命周期回調(diào)中能夠接受到傳遞過(guò)來(lái)的want參數(shù)氓轰,可以將want參數(shù)綁定到globalThis上。
            import UIAbility from '@ohos.app.ability.UIAbility'
          
            export default class EntryAbility extends UIAbility {
                onCreate(want, launch) {
                    globalThis.entryAbilityWant = want;
                    // ...
                }
          
                // ...
            }
          
        2. 在UI界面中即可通過(guò)globalThis獲取到want參數(shù)信息浸卦。
          let entryAbilityWant;
          
          @Entry
          @Component
          struct Index {
            aboutToAppear() {
              entryAbilityWant = globalThis.entryAbilityWant;
            }
          
            // 頁(yè)面展示
            build() {
              // ...
            }
          }
          
      • UIAbility和UIAbility之間使用globalThis
        同理可類比上述使用方式
      • UIAbility和ExtensionAbility之間使用globalThis
        同理可類比上述使用方式
      • globalThis使用的注意事項(xiàng)
        • Stage模型下進(jìn)程內(nèi)的UIAbility組件共享ArkTS引擎實(shí)例署鸡,使用globalThis時(shí)需要避免存放相同名稱的對(duì)象。例如AbilityA和AbilityB可以使用globalThis共享數(shù)據(jù),在存放相同名稱的對(duì)象時(shí)靴庆,先存放的對(duì)象會(huì)被后存放的對(duì)象覆蓋时捌。
        • FA模型因?yàn)槊總€(gè)UIAbility組件之間引擎隔離,不會(huì)存在該問(wèn)題炉抒。
        • 對(duì)于綁定在globalThis上的對(duì)象奢讨,其生命周期與ArkTS虛擬機(jī)實(shí)例相同,建議在使用完成之后將其賦值為null焰薄,以減少對(duì)應(yīng)用內(nèi)存的占用拿诸。
  • UIAbility組件間交互(設(shè)備內(nèi))
    方法類比原生Android

AbilityStage組件容器

AbilityStage是一個(gè)Module級(jí)別的組件容器,應(yīng)用的HAP在首次加載時(shí)會(huì)創(chuàng)建一個(gè)AbilityStage實(shí)例塞茅,可以對(duì)該Module進(jìn)行初始化等操作亩码。

AbilityStage與Module一一對(duì)應(yīng),即一個(gè)Module擁有一個(gè)AbilityStage野瘦。
我理解可以類比為Android的Application

具體創(chuàng)建步驟如下:

  1. 在工程Module對(duì)應(yīng)的ets目錄下描沟,右鍵選擇“New > Directory”,新建一個(gè)目錄并命名為myabilitystage鞭光。
  2. 在myabilitystage目錄吏廉,右鍵選擇“New > TypeScript File”,新建一個(gè)TypeScript文件并命名為MyAbilityStage.ts惰许。
  3. 打開(kāi)MyAbilityStage.ts文件席覆,導(dǎo)入AbilityStage的依賴包,自定義類繼承AbilityStage并加上需要的生命周期回調(diào)汹买,示例中增加了一個(gè)onCreate()生命周期回調(diào)娜睛。
  4. 在module.json5配置文件中,通過(guò)配置srcEntry參數(shù)來(lái)指定模塊對(duì)應(yīng)的代碼路徑卦睹,以作為HAP加載的入口。
     {
     "module": {
       "name": "entry",
       "type": "entry",
       "srcEntry": "./ets/myabilitystage/MyAbilityStage.ts",
       ...
     }
    }
    

AbilityStage擁有onCreate()生命周期回調(diào)和onAcceptWant()方库、onConfigurationUpdated()结序、onMemoryLevel()事件回調(diào)。

  • onCreate()生命周期回調(diào):在開(kāi)始加載對(duì)應(yīng)Module的第一個(gè)UIAbility實(shí)例之前會(huì)先創(chuàng)建 AbilityStage纵潦,并在AbilityStage創(chuàng)建完成之后執(zhí)行其onCreate()生命周期回調(diào)徐鹤。AbilityStage模塊提供在Module加載的時(shí)候,通知開(kāi)發(fā)者邀层,可以在此進(jìn)行該Module的初始化(如資源預(yù)加載返敬,線程創(chuàng)建等)能力。
  • onAcceptWant()事件回調(diào):UIAbility指定實(shí)例模式(specified)啟動(dòng)時(shí)候觸發(fā)的事件回調(diào)寥院,具體使用請(qǐng)參見(jiàn)UIAbility啟動(dòng)模式綜述劲赠。
  • onConfigurationUpdated()事件回調(diào):當(dāng)系統(tǒng)全局配置發(fā)生變更時(shí)觸發(fā)的事件,系統(tǒng)語(yǔ)言、深淺色等凛澎,配置項(xiàng)目前均定義在Configuration類中霹肝。
  • onMemoryLevel()事件回調(diào):當(dāng)系統(tǒng)調(diào)整內(nèi)存時(shí)觸發(fā)的事件。

應(yīng)用被切換到后臺(tái)時(shí)塑煎,系統(tǒng)會(huì)將在后臺(tái)的應(yīng)用保留在緩存中沫换。即使應(yīng)用處于緩存中,也會(huì)影響系統(tǒng)整體性能最铁。當(dāng)系統(tǒng)資源不足時(shí)讯赏,系統(tǒng)會(huì)通過(guò)多種方式從應(yīng)用中回收內(nèi)存,必要時(shí)會(huì)完全停止應(yīng)用冷尉,從而釋放內(nèi)存用于執(zhí)行關(guān)鍵任務(wù)漱挎。為了進(jìn)一步保持系統(tǒng)內(nèi)存的平衡,避免系統(tǒng)停止用戶的應(yīng)用進(jìn)程网严,可以在AbilityStage中的onMemoryLevel()生命周期回調(diào)中訂閱系統(tǒng)內(nèi)存的變化情況识樱,釋放不必要的資源。

應(yīng)用上下文Context

[圖片上傳失敗...(image-fac77d-1697789591541)]

訂閱進(jìn)程內(nèi)Ability生命周期變化

在應(yīng)用內(nèi)的DFX統(tǒng)計(jì)場(chǎng)景震束,如需要統(tǒng)計(jì)對(duì)應(yīng)頁(yè)面停留時(shí)間和訪問(wèn)頻率等信息怜庸,可以使用訂閱進(jìn)程內(nèi)Ability生命周期變化功能。

在進(jìn)程內(nèi)Ability生命周期變化時(shí)垢村,如創(chuàng)建割疾、可見(jiàn)/不可見(jiàn)、獲焦/失焦嘉栓、銷毀等宏榕,會(huì)觸發(fā)進(jìn)入相應(yīng)的回調(diào),其中返回的此次注冊(cè)監(jiān)聽(tīng)生命周期的ID(每次注冊(cè)該ID會(huì)自增+1侵佃,當(dāng)超過(guò)監(jiān)聽(tīng)上限數(shù)量2^63-1時(shí)麻昼,返回-1),以在UIAbilityContext中使用為例進(jìn)行說(shuō)明馋辈。

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. 通過(guò)context屬性獲取applicationContext
        let applicationContext = this.context.getApplicationContext();
        // 2. 通過(guò)applicationContext注冊(cè)監(jiān)聽(tīng)?wèi)?yīng)用內(nèi)生命周期
        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:在啟動(dòng)Ability時(shí)指定了abilityName和bundleName的Want稱為顯式Want抚芦。
    當(dāng)有明確處理請(qǐng)求的對(duì)象時(shí),通過(guò)提供目標(biāo)Ability所在應(yīng)用的包名信息(bundleName)迈螟,并在Want內(nèi)指定abilityName便可啟動(dòng)目標(biāo)Ability叉抡。顯式Want通常用于在當(dāng)前應(yīng)用開(kāi)發(fā)中啟動(dòng)某個(gè)已知的Ability。
      let wantInfo = {
        deviceId: '', // deviceId為空表示本設(shè)備
        bundleName: 'com.example.myapplication',
        abilityName: 'FuncAbility',
    }
    
  • 隱式的Want
    在啟動(dòng)UIAbility時(shí)未指定abilityName的Want稱為隱式Want答毫。
    當(dāng)請(qǐng)求處理的對(duì)象不明確時(shí)褥民,希望在當(dāng)前應(yīng)用中使用其他應(yīng)用提供的某個(gè)能力(通過(guò)skills標(biāo)簽定義),而不關(guān)心提供該能力的具體應(yīng)用洗搂,可以使用隱式Want消返。例如使用隱式Want描述需要打開(kāi)一個(gè)鏈接的請(qǐng)求载弄,而不關(guān)心通過(guò)具體哪個(gè)應(yīng)用打開(kāi),系統(tǒng)將匹配聲明支持該請(qǐng)求的所有應(yīng)用侦副。
      let wantInfo = {
        // uncomment line below if wish to implicitly query only in the specific bundle.
        // bundleName: 'com.example.myapplication',
        action: 'ohos.want.action.search',
        // entities can be omitted
        entities: [ 'entity.system.browsable' ],
        uri: 'https://www.test.com:8080/query/student',
        type: 'text/plain',
    };
    
    • 根據(jù)系統(tǒng)中待匹配Ability的匹配情況不同侦锯,使用隱式Want啟動(dòng)Ability時(shí)會(huì)出現(xiàn)以下三種情況。
      • 未匹配到滿足條件的Ability:?jiǎn)?dòng)失敗秦驯。
      • 匹配到一個(gè)滿足條件的Ability:直接啟動(dòng)該Ability尺碰。
      • 匹配到多個(gè)滿足條件的Ability(UIAbility):彈出選擇框讓用戶選擇。
    • 調(diào)用方傳入的want參數(shù)中不帶有abilityName和bundleName译隘,則不允許通過(guò)隱式Want啟動(dòng)所有應(yīng)用的ServiceExtensionAbility亲桥。
    • 調(diào)用方傳入的want參數(shù)中帶有bundleName,則允許使用startServiceExtensionAbility()方法隱式Want啟動(dòng)ServiceExtensionAbility固耘,默認(rèn)返回優(yōu)先級(jí)最高的ServiceExtensionAbility题篷,如果優(yōu)先級(jí)相同,返回第一個(gè)厅目。

Want參數(shù)說(shuō)明在此

顯式Want與隱式Want匹配規(guī)則在此

常見(jiàn)action與entities

action:表示調(diào)用方要執(zhí)行的通用操作(如查看番枚、分享、應(yīng)用詳情)损敷。在隱式Want中葫笼,您可定義該字段,配合uri或parameters來(lái)表示對(duì)數(shù)據(jù)要執(zhí)行的操作拗馒。如打開(kāi)路星,查看該uri數(shù)據(jù)。例如诱桂,當(dāng)uri為一段網(wǎng)址洋丐,action為ohos.want.action.viewData則表示匹配可查看該網(wǎng)址的Ability。在Want內(nèi)聲明action字段表示希望被調(diào)用方應(yīng)用支持聲明的操作挥等。在被調(diào)用方應(yīng)用配置文件skills字段內(nèi)聲明actions表示該應(yīng)用支持聲明操作友绝。

  • 常見(jiàn)action
    • ACTION_HOME:?jiǎn)?dòng)應(yīng)用入口組件的動(dòng)作,需要和ENTITY_HOME配合使用肝劲;系統(tǒng)桌面應(yīng)用圖標(biāo)就是顯式的入口組件迁客,點(diǎn)擊也是啟動(dòng)入口組件;入口組件可以配置多個(gè)涡相。
    • ACTION_CHOOSE:選擇本地資源數(shù)據(jù),例如聯(lián)系人剩蟀、相冊(cè)等催蝗;系統(tǒng)一般對(duì)不同類型的數(shù)據(jù)有對(duì)應(yīng)的Picker應(yīng)用,例如聯(lián)系人和圖庫(kù)育特。
    • ACTION_VIEW_DATA:查看數(shù)據(jù)丙号,當(dāng)使用網(wǎng)址uri時(shí)先朦,則表示顯示該網(wǎng)址對(duì)應(yīng)的內(nèi)容。
    • ACTION_VIEW_MULTIPLE_DATA:發(fā)送多個(gè)數(shù)據(jù)記錄的操作犬缨。

entities:表示目標(biāo)Ability的類別信息(如瀏覽器喳魏、視頻播放器),在隱式Want中是對(duì)action的補(bǔ)充怀薛。在隱式Want中刺彩,開(kāi)發(fā)者可定義該字段,來(lái)過(guò)濾匹配應(yīng)用的類別枝恋,例如必須是瀏覽器创倔。在Want內(nèi)聲明entities字段表示希望被調(diào)用方應(yīng)用屬于聲明的類別。在被調(diào)用方應(yīng)用配置文件skills字段內(nèi)聲明entites表示該應(yīng)用支持的類別焚碌。

  • 常用entities
    • ENTITY_DEFAULT:默認(rèn)類別無(wú)實(shí)際意義畦攘。
    • ENTITY_HOME:主屏幕有圖標(biāo)點(diǎn)擊入口類別。
    • ENTITY_BROWSABLE:指示瀏覽器類別十电。

使用顯式Want啟動(dòng)Ability在此

使用隱式Want打開(kāi)網(wǎng)址

應(yīng)用間使用Want分享數(shù)據(jù)

進(jìn)程模型

  • 應(yīng)用中(同一包名)的所有UIAbility運(yùn)行在同一個(gè)獨(dú)立進(jìn)程中知押。
  • WebView擁有獨(dú)立的渲染進(jìn)程。

基于HarmonyOS的進(jìn)程模型鹃骂,系統(tǒng)提供了公共事件機(jī)制用于一對(duì)多的通信場(chǎng)景台盯,公共事件發(fā)布者可能存在多個(gè)訂閱者同時(shí)接收事件。

公共事件訂閱概述

公共事件服務(wù)提供了動(dòng)態(tài)訂閱和靜態(tài)訂閱兩種訂閱方式。動(dòng)態(tài)訂閱與靜態(tài)訂閱最大的區(qū)別在于出吹,動(dòng)態(tài)訂閱是應(yīng)用運(yùn)行時(shí)行為付秕,而靜態(tài)訂閱是后臺(tái)服務(wù)無(wú)需處于運(yùn)行狀態(tài)。

  • 動(dòng)態(tài)訂閱:指訂閱方在運(yùn)行時(shí)調(diào)用公共事件訂閱的API實(shí)現(xiàn)對(duì)公共事件的訂閱温亲,詳見(jiàn)動(dòng)態(tài)訂閱公共事件
  • 靜態(tài)訂閱:訂閱方通過(guò)配置文件聲明和實(shí)現(xiàn)繼承自StaticSubscriberExtensionAbility的類實(shí)現(xiàn)對(duì)公共事件的訂閱杯矩,詳見(jiàn)靜態(tài)訂閱公共事件栈虚。

動(dòng)態(tài)訂閱公共事件

場(chǎng)景介紹:
動(dòng)態(tài)訂閱是指當(dāng)應(yīng)用在運(yùn)行狀態(tài)時(shí)對(duì)某個(gè)公共事件進(jìn)行訂閱,在運(yùn)行期間如果有訂閱的事件發(fā)布那么訂閱了這個(gè)事件的應(yīng)用將會(huì)收到該事件及其傳遞的參數(shù)史隆。例如魂务,某應(yīng)用希望在其運(yùn)行期間收到電量過(guò)低的事件,并根據(jù)該事件降低其運(yùn)行功耗泌射,那么該應(yīng)用便可動(dòng)態(tài)訂閱電量過(guò)低事件粘姜,收到該事件后關(guān)閉一些非必要的任務(wù)來(lái)降低功耗。訂閱部分系統(tǒng)公共事件需要先申請(qǐng)權(quán)限熔酷,訂閱這些事件所需要的權(quán)限請(qǐng)見(jiàn)公共事件權(quán)限列表孤紧。

動(dòng)態(tài)訂閱接口說(shuō)明

開(kāi)發(fā)步驟:

  1. 導(dǎo)入CommonEvent模塊。import commonEvent from '@ohos.commonEventManager';
  2. 創(chuàng)建訂閱者信息拒秘,詳細(xì)的訂閱者信息數(shù)據(jù)類型及包含的參數(shù)請(qǐng)見(jiàn)CommonEventSubscribeInfo文檔介紹号显。
     // 用于保存創(chuàng)建成功的訂閱者對(duì)象臭猜,后續(xù)使用其完成訂閱及退訂的動(dòng)作
     let subscriber = null;
     // 訂閱者信息
     let subscribeInfo = {
         events: ["usual.event.SCREEN_OFF"], // 訂閱滅屏公共事件
     }
    
  3. 創(chuàng)建訂閱者,保存返回的訂閱者對(duì)象subscriber押蚤,用于執(zhí)行后續(xù)的訂閱蔑歌、退訂等操作。
    // 創(chuàng)建訂閱者回調(diào)
     commonEvent.createSubscriber(subscribeInfo, (err, data) => {
         if (err) {
             console.error(`[CommonEvent] CreateSubscriberCallBack err=${JSON.stringify(err)}`);
         } else {
             console.info(`[CommonEvent] CreateSubscriber success`);
             subscriber = data;
             // 訂閱公共事件回調(diào)
         }
     })
    
  4. 創(chuàng)建訂閱回調(diào)函數(shù)揽碘,訂閱回調(diào)函數(shù)會(huì)在接收到事件時(shí)觸發(fā)次屠。訂閱回調(diào)函數(shù)返回的data內(nèi)包含了公共事件的名稱、發(fā)布者攜帶的數(shù)據(jù)等信息钾菊,公共事件數(shù)據(jù)的詳細(xì)參數(shù)和數(shù)據(jù)類型請(qǐng)見(jiàn)CommonEventData文檔介紹帅矗。
     // 訂閱公共事件回調(diào)
     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`);
     }
    

靜態(tài)訂閱公共事件(僅對(duì)系統(tǒng)應(yīng)用開(kāi)放)

靜態(tài)訂閱者在未接收訂閱的目標(biāo)事件時(shí),處于未拉起狀態(tài)煞烫,當(dāng)系統(tǒng)或應(yīng)用發(fā)布了指定的公共事件后浑此,靜態(tài)訂閱者將被拉起,并執(zhí)行onReceiveEvent回調(diào)滞详,開(kāi)發(fā)者可通過(guò)在onReceiveEvent回調(diào)中執(zhí)行業(yè)務(wù)邏輯凛俱,實(shí)現(xiàn)當(dāng)應(yīng)用接收到特定公共事件時(shí)執(zhí)行業(yè)務(wù)邏輯的目的。例如料饥,某應(yīng)用希望在設(shè)備開(kāi)機(jī)的時(shí)候執(zhí)行一些初始化任務(wù)蒲犬,那么該應(yīng)用可以靜態(tài)訂閱開(kāi)機(jī)事件,在收到開(kāi)機(jī)事件后會(huì)拉起該應(yīng)用岸啡,然后執(zhí)行初始化任務(wù)原叮。靜態(tài)訂閱是通過(guò)配置文件聲明和實(shí)現(xiàn)繼承自StaticSubscriberExtensionAbility的類實(shí)現(xiàn)對(duì)公共事件的訂閱。需要注意的是巡蘸,靜態(tài)訂閱公共事件對(duì)系統(tǒng)功耗有一定影響奋隶,建議謹(jǐn)慎使用。

取消動(dòng)態(tài)訂閱公共事件

動(dòng)態(tài)訂閱者完成業(yè)務(wù)需要時(shí)悦荒,需要主動(dòng)取消訂閱唯欣,訂閱者通過(guò)調(diào)用unsubscribe()方法取消訂閱事件。
開(kāi)發(fā)步驟:

  1. 導(dǎo)入CommonEvent模塊搬味。
    import commonEvent from '@ohos.commonEventManager';
  2. 根據(jù)動(dòng)態(tài)訂閱公共事件章節(jié)的步驟來(lái)訂閱某個(gè)事件境氢。
  3. 調(diào)用CommonEvent中的unsubscribe方法取消訂閱某事件。
     // subscriber為訂閱事件時(shí)創(chuàng)建的訂閱者對(duì)象
     if (subscriber !== null) {
         commonEvent.unsubscribe(subscriber, (err) => {
             if (err) {
                 console.error(`[CommonEvent] UnsubscribeCallBack err=${JSON.stringify(err)}`)
             } else {
                 console.info(`[CommonEvent] Unsubscribe`)
                 subscriber = null
             }
         })
     }
    

公共事件發(fā)布

當(dāng)需要發(fā)布某個(gè)自定義公共事件時(shí)碰纬,可以通過(guò)publish()方法發(fā)布事件萍聊。發(fā)布的公共事件可以攜帶數(shù)據(jù),供訂閱者解析并進(jìn)行下一步處理悦析。
接口說(shuō)明

  • 發(fā)布不攜帶信息的公共事件(不攜帶信息的公共事件寿桨,只能發(fā)布無(wú)序公共事件。)

    1. 導(dǎo)入CommonEvent模塊她按。
      import commonEvent from '@ohos.commonEventManager';
      
    2. 傳入需要發(fā)布的事件名稱和回調(diào)函數(shù)牛隅,發(fā)布事件。
       // 發(fā)布公共事件
       commonEvent.publish("usual.event.SCREEN_OFF", (err) => {
           if (err) {
               console.error(`[CommonEvent] PublishCallBack err=${JSON.stringify(err)}`);
           } else {
               console.info(`[CommonEvent] Publish success`);
           }
       })
      
  • 發(fā)布攜帶信息的公共事件:
    攜帶信息的公共事件酌泰,可以發(fā)布為無(wú)序公共事件媒佣、有序公共事件和粘性事件,可以通過(guò)參數(shù)CommonEventPublishData的isOrdered陵刹、isSticky的字段進(jìn)行設(shè)置默伍。

    1. 導(dǎo)入CommonEvent模塊。
      import commonEvent from '@ohos.commonEventManager';
    2. 傳入需要發(fā)布的事件名稱和回調(diào)函數(shù)衰琐,發(fā)布事件也糊。
       // 公共事件相關(guān)信息
       let options = {
           code: 1, // 公共事件的初始代碼
           data: "initial data", // 公共事件的初始數(shù)據(jù)
       }
      
    3. 傳入需要發(fā)布的事件名稱、需要發(fā)布的指定信息和回調(diào)函數(shù)羡宙,發(fā)布事件狸剃。
       // 發(fā)布公共事件
       commonEvent.publish("usual.event.SCREEN_OFF", options, (err) => {
           if (err) {
               console.error('[CommonEvent] PublishCallBack err=' + JSON.stringify(err));
           } else {
               console.info('[CommonEvent] Publish success')
           }
       })
      

線程模型

線程模型概述

HarmonyOS應(yīng)用中每個(gè)進(jìn)程都會(huì)有一個(gè)主線程,主線程有如下職責(zé):

  1. 執(zhí)行UI繪制狗热;
  2. 管理主線程的ArkTS引擎實(shí)例钞馁,使多個(gè)UIAbility組件能夠運(yùn)行在其之上;
  3. 管理其他線程(例如Worker線程)的ArkTS引擎實(shí)例匿刮,例如啟動(dòng)和終止其他線程僧凰;
  4. 分發(fā)交互事件;
  5. 處理應(yīng)用代碼的回調(diào)熟丸,包括事件處理和生命周期管理训措;
  6. 接收Worker線程發(fā)送的消息;

除主線程外光羞,還有一類與主線程并行的獨(dú)立線程Worker绩鸣,主要用于執(zhí)行耗時(shí)操作,但不可以直接操作UI狞山。Worker線程在主線程中創(chuàng)建全闷,與主線程相互獨(dú)立。最多可以創(chuàng)建8個(gè)Worker

基于HarmonyOS的線程模型萍启,不同的業(yè)務(wù)功能運(yùn)行在不同的線程上总珠,業(yè)務(wù)功能的交互就需要線程間通信。線程間通信目前主要有EmitterWorker兩種方式勘纯,其中Emitter主要適用于線程間的事件同步局服, Worker主要用于新開(kāi)一個(gè)線程執(zhí)行耗時(shí)任務(wù)

  • 注意: Stage模型只提供了主線程和Worker線程驳遵,Emitter主要用于主線程內(nèi)或者主線程和Worker線程的事件同步淫奔。

使用Emitter進(jìn)行線程間通信

Emitter主要提供線程間發(fā)送和處理事件的能力,包括對(duì)持續(xù)訂閱事件或單次訂閱事件的處理堤结、取消訂閱事件唆迁、發(fā)送事件到事件隊(duì)列等鸭丛。

  • 開(kāi)發(fā)步驟
    1. 訂閱事件
       import emitter from "@ohos.events.emitter";
      
       // 定義一個(gè)eventId為1的事件
       let event = {
           eventId: 1
       };
      
       // 收到eventId為1的事件后執(zhí)行該回調(diào)
       let callback = (eventData) => {
           console.info('event callback');
       };
      
       // 訂閱eventId為1的事件
       emitter.on(event, callback);
      
    2. 發(fā)送事件
       import emitter from "@ohos.events.emitter";
      
       // 定義一個(gè)eventId為1的事件,事件優(yōu)先級(jí)為L(zhǎng)ow
       let event = {
           eventId: 1,
           priority: emitter.EventPriority.LOW
       };
      
       let eventData = {
           data: {
               "content": "c",
               "id": 1,
               "isEmpty": false,
           }
       };
      
       // 發(fā)送eventId為1的事件唐责,事件內(nèi)容為eventData
       emitter.emit(event, eventData);
      

使用Worker進(jìn)行線程間通信

Worker是與主線程并行的獨(dú)立線程鳞溉。創(chuàng)建Worker的線程被稱為宿主線程,Worker工作的線程被稱為Worker線程鼠哥。創(chuàng)建Worker時(shí)傳入的腳本文件在Worker線程中執(zhí)行熟菲,通常在Worker線程中處理耗時(shí)的操作,需要注意的是朴恳,Worker中不能直接更新Page抄罕。

  • 開(kāi)發(fā)步驟
    1. 在工程的模塊級(jí)build-profile.json5文件的buildOption屬性中添加配置信息。
         "buildOption": {
         "sourceOption": {
           "workers": [
             "./src/main/ets/workers/worker.ts"
           ]
         }
       }
      
    2. 根據(jù)build-profile.json5中的配置創(chuàng)建對(duì)應(yīng)的worker.ts文件于颖。
       import worker from '@ohos.worker';
      
       let parent = worker.workerPort;
      
       // 處理來(lái)自主線程的消息
       parent.onmessage = function(message) {
           console.info("onmessage: " + message)
           // 發(fā)送消息到主線程
           parent.postMessage("message from worker thread.")
       }
      
    3. 主線程中使用如下方式初始化和使用worker呆贿。
      • Stage模型
         import worker from '@ohos.worker';
        
         let wk = new worker.ThreadWorker("entry/ets/workers/worker.ts");
        
         // 發(fā)送消息到worker線程
         wk.postMessage("message from main thread.")
        
         // 處理來(lái)自worker線程的消息
         wk.onmessage = function(message) {
             console.info("message from worker: " + message)
        
             // 根據(jù)業(yè)務(wù)按需停止worker線程
             wk.terminate()
         }
        
  • 說(shuō)明:
    • build-profile.json5中配置的worker.ts的相對(duì)路徑都為./src/main/ets/workers/worker.ts時(shí),在Stage模型下創(chuàng)建worker需要傳入路徑entry/ets/workers/worker.ts森渐;在FA模型下創(chuàng)建worker需要傳入路徑../workers/worker.ts榨崩。

    • 主線程與Worker線程間支持的數(shù)據(jù)類型參考序列化支持類型

    • 傳遞通過(guò)自定義class創(chuàng)建出來(lái)的object時(shí)章母,不會(huì)發(fā)生序列化錯(cuò)誤母蛛,但是自定義class的屬性(如Function)無(wú)法通過(guò)序列化傳遞。

    • Worker存在數(shù)量限制乳怎,當(dāng)前支持最多同時(shí)存在8個(gè)Worker彩郊。

    • 主動(dòng)銷毀Worker可以調(diào)用新創(chuàng)建Worker對(duì)象的terminate()或workerPort.close()方法。

    • 自API version 9版本開(kāi)始蚪缀,若Worker處于已經(jīng)銷毀或正在銷毀等非運(yùn)行狀態(tài)時(shí)秫逝,調(diào)用其功能接口,會(huì)拋出相應(yīng)的BusinessError询枚。

    • Worker的創(chuàng)建和銷毀耗費(fèi)性能违帆,建議管理已創(chuàng)建的Worker并重復(fù)使用。

    • 創(chuàng)建Worker工程時(shí)金蜀,new worker.Worker構(gòu)造函數(shù)和new worker.ThreadWorker構(gòu)造函數(shù)不能同時(shí)使用刷后,否則將導(dǎo)致工程中Worker的功能異常。自API version 9版本開(kāi)始渊抄,建議使用new worker.ThreadWorker構(gòu)造函數(shù)尝胆,在API version 8及之前的版本,建議使用new worker.Worker構(gòu)造函數(shù)护桦。

    • 創(chuàng)建Worker工程時(shí)含衔,在Worker線程的文件中(比如本文中worker.ts)不能導(dǎo)入任何有關(guān)構(gòu)建UI的方法(比如ETS文件等),否則會(huì)導(dǎo)致Worker的功能失效。排查方式:解壓生成的Hap包贪染,在創(chuàng)建Worker線程的文件目錄中找到"worker.js"缓呛,全局搜索"View"關(guān)鍵字。如果存在該關(guān)鍵字杭隙,說(shuō)明在worker.js中打包進(jìn)去了構(gòu)建UI的方法强经,會(huì)導(dǎo)致Worker的功能失效,建議在創(chuàng)建Worker線程的文件中修改 "import “xxx” from src"中src的目錄層級(jí)寺渗。

    • 線程間通信時(shí)傳遞的數(shù)據(jù)量最大限制為16M。

UI開(kāi)發(fā)

方舟開(kāi)發(fā)框架(ArkUI)概述

開(kāi)發(fā)布局

  • 線性布局(Row兰迫、Column)
    如果布局內(nèi)子元素超過(guò)1個(gè)信殊,且能夠以某種方式線性排列時(shí)優(yōu)先考慮此布局。

  • 層疊布局(Stack)
    組件需要有堆疊效果時(shí)優(yōu)先考慮此布局汁果,層疊布局的堆疊效果不會(huì)占用或影響其他同容器內(nèi)子組件的布局空間涡拘。例如Panel作為子組件彈出時(shí)將其他組件覆蓋更為合理,則優(yōu)先考慮在外層使用堆疊布局据德。

  • 彈性布局(Flex)
    彈性布局是與線性布局類似的布局方式鳄乏。區(qū)別在于彈性布局默認(rèn)能夠使子組件壓縮或拉伸。在子組件需要計(jì)算拉伸或壓縮比例時(shí)優(yōu)先使用此布局棘利,可使得多個(gè)容器內(nèi)子組件能有更好的視覺(jué)上的填充容器效果橱野。

  • 相對(duì)布局(RelativeContainer)
    相對(duì)布局是在二維空間中的布局方式,不需要遵循線性布局的規(guī)則善玫,布局方式更為自由水援。通過(guò)在子組件上設(shè)置錨點(diǎn)規(guī)則(AlignRules)使子組件能夠?qū)⒆约涸跈M軸、縱軸中的位置與容器或容器內(nèi)其他子組件的位置對(duì)齊茅郎。設(shè)置的錨點(diǎn)規(guī)則可以天然支持子元素壓縮蜗元、拉伸,堆疊或形成多行效果系冗。在頁(yè)面元素分布復(fù)雜或通過(guò)線性布局會(huì)使容器嵌套層數(shù)過(guò)深時(shí)推薦使用奕扣。

  • 柵格布局(GridRow、GridCol)
    柵格是多設(shè)備場(chǎng)景下通用的輔助定位工具掌敬,通過(guò)將空間分割為有規(guī)律的柵格惯豆。柵格不同于網(wǎng)格布局固定的空間劃分,它可以實(shí)現(xiàn)不同設(shè)備下不同的布局奔害,空間劃分更隨心所欲循帐,從而顯著降低適配不同屏幕尺寸的設(shè)計(jì)及開(kāi)發(fā)成本,使得整體設(shè)計(jì)和開(kāi)發(fā)流程更有秩序和節(jié)奏感舀武,同時(shí)也保證多設(shè)備上應(yīng)用顯示的協(xié)調(diào)性和一致性拄养,提升用戶體驗(yàn)。推薦手機(jī)、大屏瘪匿、平板等不同設(shè)備跛梗,內(nèi)容相同但布局不同時(shí)使用。

  • 媒體查詢(@ohos.mediaquery)
    媒體查詢可根據(jù)不同設(shè)備類型或同設(shè)備不同狀態(tài)修改應(yīng)用的樣式棋弥。例如根據(jù)設(shè)備和應(yīng)用的不同屬性信息設(shè)計(jì)不同的布局核偿,以及屏幕發(fā)生動(dòng)態(tài)改變時(shí)更新應(yīng)用的頁(yè)面布局。

  • 列表(List)
    使用列表可以輕松高效地顯示結(jié)構(gòu)化顽染、可滾動(dòng)的信息漾岳。在ArkUI中,列表具有垂直和水平布局能力和自適應(yīng)交叉軸方向上排列個(gè)數(shù)的布局能力粉寞,超出屏幕時(shí)可以滾動(dòng)尼荆。列表適合用于呈現(xiàn)同類數(shù)據(jù)類型或數(shù)據(jù)類型集,例如圖片和文本唧垦。

  • 網(wǎng)格(Grid)
    網(wǎng)格布局具有較強(qiáng)的頁(yè)面均分能力捅儒,子組件占比控制能力,是一種重要自適應(yīng)布局振亮。網(wǎng)格布局可以控制元素所占的網(wǎng)格數(shù)量巧还、設(shè)置子組件橫跨幾行或者幾列,當(dāng)網(wǎng)格容器尺寸發(fā)生變化時(shí)坊秸,所有子組件以及間距等比例調(diào)整麸祷。推薦在需要按照固定比例或者均勻分配空間的布局場(chǎng)景下使用,例如計(jì)算器褒搔、相冊(cè)摇锋、日歷等。

  • 輪播(Swiper)
    輪播組件通常用于實(shí)現(xiàn)廣告輪播站超、圖片預(yù)覽荸恕、可滾動(dòng)應(yīng)用等。

添加組件

添加常用組件

添加氣泡和菜單

設(shè)置頁(yè)面路由和組件導(dǎo)航

頁(yè)面路由router

組件導(dǎo)航

  • Navigation
    Navigation組件一般作為頁(yè)面的根容器死相,包括單頁(yè)面融求、分欄和自適應(yīng)三種顯示模式。同時(shí)算撮,Navigation提供了屬性來(lái)設(shè)置頁(yè)面的標(biāo)題欄生宛、工具欄、導(dǎo)航欄等肮柜。

  • Tabs
    當(dāng)頁(yè)面信息較多時(shí)陷舅,為了讓用戶能夠聚焦于當(dāng)前顯示的內(nèi)容,需要對(duì)頁(yè)面內(nèi)容進(jìn)行分類审洞,提高頁(yè)面空間利用率莱睁。Tabs組件可以在一個(gè)頁(yè)面內(nèi)快速實(shí)現(xiàn)視圖內(nèi)容的切換,一方面提升查找信息的效率,另一方面精簡(jiǎn)用戶單次獲取到的信息量仰剿。

    基本布局
    底部導(dǎo)航
    頂部導(dǎo)航
    側(cè)邊導(dǎo)航
    限制導(dǎo)航欄的滑動(dòng)切換
    固定導(dǎo)航欄
    滾動(dòng)導(dǎo)航欄
    自定義導(dǎo)航欄
    切換至指定頁(yè)簽
    滑動(dòng)切換導(dǎo)航欄

顯示圖形

使用動(dòng)畫(huà)

頁(yè)面內(nèi)動(dòng)畫(huà)

支持交互事件

使用通用事件

  • 觸屏事件
    • 點(diǎn)擊事件
    • 拖拽事件
    • 觸摸事件
  • 鍵鼠事件
    • 鼠標(biāo)事件
    • 按鍵事件
  • 焦點(diǎn)事件

使用手勢(shì)事件

Web相關(guān)

ArkTS語(yǔ)言基礎(chǔ)類庫(kù)概述

  • 并發(fā)
  • 容器類庫(kù)

安全

網(wǎng)絡(luò)與連接

電話服務(wù)

數(shù)據(jù)管理

文件管理

后臺(tái)任務(wù)

設(shè)備管理

通知

窗口管理

WebGL

WebGL的全稱為Web Graphic Library(網(wǎng)頁(yè)圖形庫(kù))创淡,主要用于交互式渲染2D圖形和3D圖形。目前HarmonyOS中使用的WebGL是基于OpenGL裁剪的OpenGL ES南吮,可以在HTML5的canvas元素對(duì)象中使用琳彩,無(wú)需使用插件,支持跨平臺(tái)部凑。WebGL程序是由JavaScript代碼組成的露乏,其中使用的API可以利用用戶設(shè)備提供的GPU硬件完成圖形渲染和加速。更多信息請(qǐng)參考WebGL?標(biāo)準(zhǔn)涂邀。

媒體

自動(dòng)化測(cè)試

Native Api相關(guān)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瘟仿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子必孤,更是在濱河造成了極大的恐慌,老刑警劉巖瑞躺,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敷搪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡幢哨,警方通過(guò)查閱死者的電腦和手機(jī)赡勘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)捞镰,“玉大人闸与,你說(shuō)我怎么就攤上這事“妒郏” “怎么了践樱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)凸丸。 經(jīng)常有香客問(wèn)我拷邢,道長(zhǎng),這世上最難降的妖魔是什么屎慢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任瞭稼,我火速辦了婚禮,結(jié)果婚禮上腻惠,老公的妹妹穿的比我還像新娘环肘。我一直安慰自己,他們只是感情好集灌,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布悔雹。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荠商。 梳的紋絲不亂的頭發(fā)上寂恬,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音莱没,去河邊找鬼初肉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的机隙。 我是一名探鬼主播祷肯,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼妄壶!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起寄狼,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤丁寄,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后泊愧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體伊磺,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年删咱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屑埋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痰滋,死狀恐怖摘能,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情敲街,我是刑警寧澤团搞,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站多艇,受9級(jí)特大地震影響莺丑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜墩蔓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一梢莽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奸披,春花似錦昏名、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)洪鸭。三九已至,卻和暖如春仑扑,著一層夾襖步出監(jiān)牢的瞬間览爵,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工镇饮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜓竹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓储藐,卻偏偏與公主長(zhǎng)得像俱济,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钙勃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容