鴻蒙開發(fā)指南

背景

鴻蒙系統(tǒng)是中國首款完全自主研發(fā)的分布式操作系統(tǒng),其底層技術實現(xiàn)完全自主可控,擺脫了對安卓和iOS的依賴。尤其在當前國際科技競爭加劇的背景下,鴻蒙的誕生降低了我國在操作系統(tǒng)領域對外國技術的依賴,為國家信息安全和產(chǎn)業(yè)安全筑起防線。

除了移動端系統(tǒng),鴻蒙電腦也將在5月份發(fā)布,鴻蒙PC端應用開發(fā),工具、環(huán)境和移動端都是一樣的,可以真正實現(xiàn)一次開發(fā),多端部署,所以大家了解一下還是有價值的。

1.視圖

視圖單位

image.png

vp類似于Android中的dp,fp類似于Android中的sp

生命周期

UIAbility生命周期

UIAbility是一種包含UI的應用組件,主要用于和用戶交互,類似于Android中的Activity

  • 如果開發(fā)者希望在任務視圖中看到一個任務,建議使用“一個UIAbility+多個頁面”的方式,可以避免不必要的資源加載。

  • 如果開發(fā)者希望在任務視圖中看到多個任務,或者需要同時開啟多個窗口,建議使用多個UIAbility實現(xiàn)不同的功能。

生命周期如下:

image.png

UIAbility實例創(chuàng)建完成之后,在進入Foreground之前,系統(tǒng)會創(chuàng)建一個WindowStage。WindowStage創(chuàng)建完成后會進入onWindowStageCreate()回調,可以在該回調中設置UI入口頁面(windowStage.loadContent),類似于Android中Fragment的onCreateView()

image.png

頁面生命周期

image.png

頁面生命周期會區(qū)分組件類型,頁面入口組件和普通自定義組件生命周期是不一樣的

生命周期有兩類:

  1. 頁面生命周期,即被@Entry 裝飾的組件生命周期,有以下生命周期接口:
  • onPageShow:頁面每次顯示時觸發(fā)一次,包括路由過程、應用進入前臺等場景。

  • onPageHide:頁面每次隱藏時觸發(fā)一次,包括路由過程、應用進入后臺等場景。

  • onBackPress:當用戶點擊返回按鈕時觸發(fā)。

  1. 組件生命周期,即一般用@Component裝飾的自定義組件的生命周期,有以下生命周期接口:
  • aboutToAppear:組件即將出現(xiàn)時回調該接口,具體時機為在創(chuàng)建自定義組件的新實例后,在執(zhí)行其build()函數(shù)之前執(zhí)行。

  • onDidBuild:組件build()函數(shù)執(zhí)行完成之后回調該接口,開發(fā)者可以在這個階段進行埋點數(shù)據(jù)上報等不影響實際UI的功能。

  • aboutToDisappear:aboutToDisappear函數(shù)在自定義組件析構銷毀之前執(zhí)行。

頁面入口組件:同時被@Entry @Component修飾的頁面,擁有頁面級和組件級雙重生命周期,即上面的生命周期都能走到

普通自定義組件:僅被@Component修飾的頁面,僅包含組件級生命周期,普通自定義組件無法使用 onPageShow/onPageHide/onBackPress 等頁面級函數(shù),即便是代碼里都能實現(xiàn)生命周期函數(shù),系統(tǒng)也不會回調

適配多尺寸

隨著手機形態(tài)和尺寸越來越多,不知道大家有沒有覺得設備條件判斷越來越復雜了。

設計原理:

為了提升用戶的全場景體驗,需要考慮多設備體驗的連續(xù)性

  • 原則一:兩個寬度相近的窗口,頁面布局應相同。

  • 原則二:高度相對寬度較小的窗口,呈現(xiàn)橫向窗口或類方型窗口時,頁面布局進行差異化設計。

設計方案:

橫向和縱向維度分別代表窗口的不同特征,作為判斷頁面布局和交互體驗的條件:

  • 橫向斷點以窗口寬度值區(qū)分,代表窗口寬度,單位是vp

  • 縱向斷點以窗口高寬比區(qū)分,代表窗口相對高度,可以表示橫向、方型或縱向窗口。

image.png

橫向維度以應用窗口寬度為判斷條件,推薦按照不同的閾值分成5個區(qū)間:

image.png

縱向維度以應用窗口的高寬比為判斷條件,推薦按照不同的閾值分成3個區(qū)間:

image.png

可以解決目前一些痛點問題:

·設備判斷條件問題:

這種方法判斷與設備類型無關,不需要判斷機型,可減少判斷條件。比如目前你想修改輸入法面板上內容,需要多個判斷,比如是否是折疊屏,是否是展開態(tài),是否是小折,是否橫豎屏等等,非常麻煩;

設備無關的話,同一套代碼,遷移到不同廠商就會簡單一些,因為屏幕分辨率接口是一樣的;

·布局/皮膚復用問題:

同一個維度區(qū)域內的設備可以共用一套皮膚,減少皮膚數(shù)量;未來新增設備如果落在已適配的方格區(qū)域內,則不需要額外適配。比如現(xiàn)在適配了華為新出的闊折疊,如果未來再出小折就不需要再額外適配

·懸浮鍵盤縮放問題:

目前懸浮鍵盤的縮放邏輯,自由縮放就會帶來很多顯示問題,找一個統(tǒng)一的縮放算法很難,可以通過這個方案去解決,懸浮鍵盤調節(jié)后,用寬度和高寬比唯一確定一個形態(tài),每個方格區(qū)域的懸浮形態(tài)使用同一個固定縮放值。

輸入法高度限制

和Android輸入法不同,Android端輸入法Dialog可以占滿全屏,但在鴻蒙系統(tǒng)中,輸入法高度最高不能超過屏幕高度70%,輸入法進程顯示的內容也不能超過向系統(tǒng)申請的高度,比如隱私彈框等彈框的高度都不能超過輸入法申請的高度

輸入法生命周期


import { Want } from '@kit.AbilityKit';

import keyboardController from './model/KeyboardController';

import { InputMethodExtensionAbility } from '@kit.IMEKit';

export default class InputDemoService extends InputMethodExtensionAbility {

 onCreate(want: Want): void {

 keyboardController.onCreate(this.context); // 初始化窗口并注冊對輸入法框架的事件監(jiān)聽

 }

 onDestroy(): void {

 console.log("onDestroy.");

 keyboardController.onDestroy(); // 銷毀窗口并去注冊事件監(jiān)聽

 }

}

// 調用輸入法框架的getInputMethodAbility方法獲取實例,并由此實例調用輸入法框架功能接口

const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility();

export class KeyboardController {

 private mContext: InputMethodExtensionContext | undefined = undefined; // 保存InputMethodExtensionAbility中的context屬性

 private panel: inputMethodEngine.Panel | undefined = undefined; 

 private textInputClient: inputMethodEngine.InputClient | undefined = undefined; 

 private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined;

 constructor() {

 }

 public onCreate(context: InputMethodExtensionContext): void

 {

 this.mContext = context;

 this.initWindow(); // 初始化窗口

 this.registerListener(); // 注冊對輸入法框架的事件監(jiān)聽

 }

 public onDestroy(): void // 應用生命周期銷毀

 {

 this.unRegisterListener(); // 去注冊事件監(jiān)聽

 if(this.panel) { // 銷毀窗口

 inputMethodAbility.destroyPanel(this.panel);

 }

 if(this.mContext) {

 this.mContext.destroy();

 }

 }

 public insertText(text: string): void {

 if(this.textInputClient) {

 this.textInputClient.insertText(text);

 }

 }

 public deleteForward(length: number): void {

 if(this.textInputClient) {

 this.textInputClient.deleteForward(length);

 }

 }

 private initWindow(): void // 初始化窗口

 {

 if(this.mContext === undefined) {

 return;

 }

 let dis = display.getDefaultDisplaySync();

 let dWidth = dis.width;

 let dHeight = dis.height;

 let keyHeightRate = 0.47;

 let keyHeight = dHeight * keyHeightRate;

 let nonBarPosition = dHeight - keyHeight;

 let panelInfo: inputMethodEngine.PanelInfo = {

 type: inputMethodEngine.PanelType.SOFT_KEYBOARD,

 flag: inputMethodEngine.PanelFlag.FLG_FIXED

 };

 inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => {

 this.panel = inputPanel;

 if(this.panel) {

 await this.panel.resize(dWidth, keyHeight);

 await this.panel.moveTo(0, nonBarPosition);

 await this.panel.setUiContent('InputMethodExtensionAbility/pages/Index');

 }

 });

 }

 private registerListener(): void

 {

 this.registerInputListener(); // 注冊對輸入法框架服務的監(jiān)聽

 // 注冊隱藏鍵盤事件監(jiān)聽等

 }

 private registerInputListener(): void { // 注冊對輸入法框架服務的開啟及停止事件監(jiān)聽

 inputMethodAbility.on('inputStart', (kbController, textInputClient) => {

 this.textInputClient = textInputClient; // 此為輸入法客戶端實例,由此調用輸入法框架提供給輸入法應用的功能接口

 this.keyboardController = kbController;

 })

 inputMethodAbility.on('inputStop', () => {

 this.onDestroy(); // 銷毀KeyboardController

 });

 }

 private unRegisterListener(): void

 {

 inputMethodAbility.off('inputStart');

 inputMethodAbility.off('inputStop', () => {});

 }

}

const keyboardController = new KeyboardController();

export default keyboardController;

與Android輸入法框架不同,InputMethodExtensionAbility僅提供onCreate()和onDestroy()兩個生命周期方法,其他的事件(inputStart、inputStop等)需要自己去注冊監(jiān)聽。

2.數(shù)據(jù)

多進程

和Android端不同的是,鴻蒙輸入法App端和鍵盤端是不一樣的進程

鴻蒙目前不支持手動設置多進程,進程分配均由系統(tǒng)設置

  • 通常情況下,應用中(同一Bundle名稱)的所有UIAbility均是運行在同一個獨立進程(主進程)中,如下圖中綠色部分的“Main Process”。

  • 應用中(同一Bundle名稱)的所有同一類型ExtensionAbility均是運行在一個獨立進程中,如下圖中藍色部分的“FormExtensionAbility Process”、“InputMethodExtensionAbility Process”、其他ExtensionAbility Process。

  • WebView擁有獨立的渲染進程,如下圖中黃色部分的“Render Process”。

image.png

輸入法目前會存在3個進程,App端進程(UIAbility),鍵盤端進程(InputMethodExtensionAbility),換機克隆進程(BackupExtensionAbility)

進程間通信通過公共事件機制(commonEventManager) 實現(xiàn),只能通過異步消息通信,無法實現(xiàn)Android中類似AIDL的同步通信

獨立沙箱

image.png
image.png

和Android一樣,有全局的App Context,也有頁面級別的UI Context,不同的Context獲取的資源目錄不一樣:

image.png

基礎模式/完整模式

這個是輸入法鍵盤端獨有的設置,用戶可以在系統(tǒng)設置項控制,僅控制鍵盤端,App端不受影響,相當于在系統(tǒng)級別實現(xiàn)了純凈模式

  1. 基礎模式下,輸入法擴展(InputMethodExtensionAbility)進程無法拉起其他UIAbility或ExtensionAbility。

  2. 基礎模式下,輸入法擴展會受到系統(tǒng)管控,不能使用涉及訪問或泄漏用戶個人數(shù)據(jù)的各種接口,同時無法將數(shù)據(jù)傳遞出進程。管控功能包括但不限于:網(wǎng)絡、短信、電話、麥克風、定位、相機、藍牙、壁紙、支付、日歷、游戲、揚聲器、Wi-Fi、剪切板、多媒體、聯(lián)系人、公共事件、系統(tǒng)賬號、健康數(shù)據(jù)、地圖服務、推送服務、融合搜索、共享內存、分布式特性、廣告設備標識、振動等。

共享沙箱

image.png

為了方便基礎模式下,鍵盤端和App端能共享數(shù)據(jù),專門設計了共享沙箱。比如基礎模式下,設置項的開關,鍵盤端需要讀取

基礎模式下,輸入法鍵盤端對共享沙箱只讀,對輸入法鍵盤端自己的獨立沙箱可讀寫;App端可以對共享沙箱可讀寫。

共享沙箱的使用,需要向鴻蒙申請group id,并在應用的profile里面配置data-group-ids和在InputMethodExtensionAbility所在的module.json5里面配置dataGroupIds實現(xiàn)

代碼中,如果需要和基礎模式的鍵盤端共享數(shù)據(jù),需要在創(chuàng)建Preference和數(shù)據(jù)庫的時候,傳入dataGroupId,才能和基礎模式鍵盤共享數(shù)據(jù);如果需要共享文件,需要將文件放在getGroupDir目錄。

3.性能

async/await:

async/await 是 JavaScript 的語法特性,它是 ES2017(ES8)中引入的異步編程語法糖,旨在簡化基于 Promise 的異步操作,使代碼更接近同步風格

鴻蒙系統(tǒng)功能接口一般會提供兩個接口,一個是同步的,一個是異步的,這樣會讓人誤以為異步接口不會影響性能,其實async不切換線程,本質還是在同一個線程執(zhí)行

類似協(xié)程機制,僅用來調度任務,不會切換線程,如果耗時任務全部丟在主線程async函數(shù)中,同樣會導致App卡死

多線程:

為了避免主線程卡頓問題,我們還是需要多線程

與Java共享內存模型不一樣,鴻蒙使用消息傳遞模型,線程間不共享內存

并發(fā)模型的差異:

共享內存模型:

image.png

線程間內存共享,訪問共享內存時需要先獲取鎖

消息傳遞模型/Actor模型:

image.png

同一個進程的線程間不共享內存,避免了鎖競爭,但會增加內存消耗,鴻蒙限制最多同時運行的Worker子線程數(shù)量為64個,代碼上也會導致主線程的全局變量和SDK無法在子線程共享使用,需要重新初始化

image.png
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容