級開機(jī)自啟動與Launcher替換實(shí)戰(zhàn))
1. 項(xiàng)目概述這不是簡單的APP安裝而是對OpenHarmony系統(tǒng)內(nèi)核層的“外科手術(shù)”O(jiān)penHarmony不是安卓更不是Windows。當(dāng)你在手機(jī)上點(diǎn)開設(shè)置、勾選“開機(jī)自啟動”時(shí)背后是廠商預(yù)埋的權(quán)限管理模塊和AMSActivity Manager Service調(diào)度邏輯而當(dāng)你在OpenHarmony設(shè)備上想讓一個(gè)應(yīng)用“一上電就跑起來”你面對的是一套從LiteOS-M/LiteOS-A內(nèi)核、HDF驅(qū)動框架、Ability運(yùn)行時(shí)到UI框架全棧自研的分布式操作系統(tǒng)。它沒有/etc/rc.local不認(rèn)AndroidManifest.xml里的android:enabledtrue也沒有StartupManager這種現(xiàn)成API——所有“開機(jī)自啟動”和“Launcher替換”都必須穿透到系統(tǒng)服務(wù)層與啟動流程鏈中去動刀。我去年在一款基于OpenHarmony 3.2-Release的工業(yè)手持終端上落地這個(gè)需求客戶要的是設(shè)備通電后3秒內(nèi)完成系統(tǒng)初始化并直接進(jìn)入定制的工單掃描界面全程無原生Launcher界面閃現(xiàn)且該應(yīng)用具備最高優(yōu)先級——即使用戶誤觸返回鍵或Home鍵也必須強(qiáng)制拉回主業(yè)務(wù)界面。這已經(jīng)超出了“應(yīng)用開發(fā)”范疇進(jìn)入了系統(tǒng)級定制工程師的工作區(qū)。核心關(guān)鍵詞“OpenHarmony”“開機(jī)自啟動”“Launcher替換”不是并列關(guān)系而是存在強(qiáng)依賴鏈沒有Launcher替換開機(jī)自啟動就失去入口意義沒有對系統(tǒng)啟動流程的深度干預(yù)所謂“自啟動”只是個(gè)偽命題。網(wǎng)上搜到的“通過config.json配置ability.launchType”或“調(diào)用startAbility()”方案在真機(jī)實(shí)測中全部失效——因?yàn)槟切〢PI只在系統(tǒng)已進(jìn)入桌面態(tài)后才生效而我們要解決的是“系統(tǒng)還沒進(jìn)桌面怎么把桌面換成我的應(yīng)用”。適合誰來讀這篇如果你是剛從Android轉(zhuǎn)過來的開發(fā)者別急著寫Ability如果你是鴻蒙生態(tài)合作伙伴的FAE正被客戶追問“為什么我們的APP不能像安卓那樣開機(jī)就起”那你需要的不是文檔鏈接而是可驗(yàn)證、可復(fù)現(xiàn)、帶編譯日志和燒錄驗(yàn)證的完整路徑。本文不講理論架構(gòu)圖只講我在RK3566開發(fā)板OpenHarmony 3.2源碼樹上從零開始打patch、改配置、重簽名、燒錄驗(yàn)證的全過程。每一個(gè)步驟都有對應(yīng)代碼行號、編譯報(bào)錯(cuò)截圖分析、以及為什么必須這么做的底層依據(jù)。2. 系統(tǒng)啟動流程解構(gòu)從上電到首幀渲染每一環(huán)都得“簽收”2.1 OpenHarmony啟動鏈全景比安卓更扁平但每層都更“硬核”O(jiān)penHarmony的啟動流程不像安卓那樣分Bootloader→Kernel→Init→Zygote→SystemServer→Launcher多級接力它采用“輕量級內(nèi)核微內(nèi)核服務(wù)化”設(shè)計(jì)關(guān)鍵階段只有四層Boot階段U-Boot加載kernel鏡像與ramdisk掛載/system分區(qū)只讀和/data分區(qū)可寫Kernel階段LiteOS-A內(nèi)核初始化設(shè)備樹、調(diào)度器、內(nèi)存管理啟動第一個(gè)用戶態(tài)進(jìn)程initInit階段/system/bin/init讀取/etc/init.cfg按順序啟動system_service如hiview、samgr、core_service如bundle_manager、ability_manager、ui_service如window_manager、surface_flingerUI階段ui_service啟動后由launcher_service加載默認(rèn)Launcher應(yīng)用即com.ohos.launcher完成首幀渲染。提示OpenHarmony 3.2中l(wèi)auncher_service不是一個(gè)獨(dú)立進(jìn)程而是ui_service內(nèi)部的一個(gè)AbilityManagerService插件模塊其啟動時(shí)機(jī)由/system/profile/launcher_config.json控制而非Android的PackageManager掃描機(jī)制。這意味著想實(shí)現(xiàn)開機(jī)自啟動不能等launcher_service起來后再發(fā)Intent而必須在ui_service啟動前就讓系統(tǒng)知道“我的應(yīng)用才是真正的Launcher”。否則哪怕你的應(yīng)用設(shè)置了launchType: standard它也只能作為普通Ability被調(diào)度永遠(yuǎn)無法搶占首屏。2.2 Launcher替換的本質(zhì)不是換圖標(biāo)而是劫持“系統(tǒng)默認(rèn)首頁”的注冊權(quán)OpenHarmony的Launcher不是靠intent-filter聲明搶占而是通過Bundle Manager的默認(rèn)首頁注冊機(jī)制實(shí)現(xiàn)。系統(tǒng)在首次啟動時(shí)會掃描/system/app和/data/app下所有Bundle查找滿足以下條件的Abilitymodule.json5中abilities字段包含visible: true且exported: trueabilities中至少有一個(gè)Ability的name為MainAbility該Ability的metadata中包含ohos.app.launcher標(biāo)簽該Bundle的bundleName在/system/profile/default_launcher.json中被列為白名單。注意default_launcher.json是只讀文件位于/system/profile/內(nèi)容形如{ launcherBundleName: com.ohos.launcher, launcherAbilityName: MainAbility }所以“Launcher替換”真正的技術(shù)動作是修改default_launcher.json指向你的Bundle并確保你的Bundle在系統(tǒng)啟動早期就被Bundle Manager識別并加載。但這帶來新問題——/system分區(qū)是只讀的你不能直接adb push覆蓋而/data分區(qū)雖可寫但Bundle Manager在init階段只掃描/system/app/data/app中的Bundle要等bundle_manager服務(wù)完全啟動后才加載此時(shí)Launcher早已啟動完畢。解決方案只有一個(gè)將你的定制Launcher打包進(jìn)/system/app目錄并在編譯階段注入到system.img中。這要求你必須獲取OpenHarmony源碼修改構(gòu)建腳本重新編譯整個(gè)system鏡像。2.3 開機(jī)自啟動的兩種合法路徑系統(tǒng)級 vs 應(yīng)用級選錯(cuò)就白干網(wǎng)上流傳的“開機(jī)自啟動”方案基本分兩類但90%都踩坑錯(cuò)誤路徑應(yīng)用級在Ability的onStart()里調(diào)用startAbility()啟動其他Ability或監(jiān)聽COMMON_EVENT_BOOT_COMPLETED廣播。→ 實(shí)測結(jié)果OpenHarmony 3.2中該廣播根本不存在CommonEventManager未實(shí)現(xiàn)BOOT_COMPLETED事件startAbility()在非UI線程調(diào)用會拋IllegalStateException而在UI線程調(diào)用時(shí)系統(tǒng)尚未完成AbilityManagerService初始化直接崩潰。正確路徑系統(tǒng)級在ui_service啟動過程中通過AbilityManager的addSystemAbility()接口將你的應(yīng)用注冊為系統(tǒng)級Ability并設(shè)置啟動策略為START_ON_BOOT?!?這需要你修改//foundation/ability/ability_runtime/src/core/ability_manager_service.cpp源碼在AbilityManagerService::Init()函數(shù)末尾插入啟動邏輯且必須確保你的Bundle已通過BundleManager預(yù)加載。注意START_ON_BOOT不是公開枚舉值它是ability_runtime內(nèi)部定義的私有常量#define START_ON_BOOT 0x01只能通過源碼級patch實(shí)現(xiàn)。任何試圖用setStartMode(1)繞過編譯檢查的做法都會在link階段因符號未定義失敗。因此“開機(jī)自啟動”和“Launcher替換”本質(zhì)是同一枚硬幣的兩面前者解決“什么時(shí)候啟動”后者解決“啟動后顯示什么”。二者必須同步設(shè)計(jì)、同步編譯、同步燒錄缺一不可。3. 實(shí)操全流程從源碼修改到真機(jī)驗(yàn)證的7個(gè)關(guān)鍵步驟3.1 步驟一準(zhǔn)備開發(fā)環(huán)境與源碼樹避坑重點(diǎn)分支與工具鏈匹配我使用的環(huán)境組合經(jīng)實(shí)測穩(wěn)定Ubuntu 20.04 LTS必須18.04缺少Python3.922.04的GCC11與OH 3.2不兼容OpenHarmony 3.2-Release源碼tag:OpenHarmony-3.2-Release嚴(yán)禁用master分支其Launcher機(jī)制已重構(gòu)編譯工具鏈ohos-sdkv3.2.12.2從DevEco Studio 3.1.1中提取官網(wǎng)下載頁標(biāo)注“for 3.2-Release”燒錄工具h(yuǎn)bv1.4.3pip install ohos-build安裝版本錯(cuò)配會導(dǎo)致build.sh報(bào)No module named build提示hb set -rp設(shè)置源碼根目錄時(shí)必須確保.repo目錄存在且repo sync已完成。我曾因repo init時(shí)未指定-u https://gitee.com/openharmony/manifest.git -b OpenHarmony-3.2-Release導(dǎo)致同步了錯(cuò)誤分支編譯出的system.img無法啟動浪費(fèi)17小時(shí)排查。3.2 步驟二創(chuàng)建定制Launcher應(yīng)用關(guān)鍵BundleName與AbilityName必須嚴(yán)格匹配新建應(yīng)用命名為com.mycompany.industrial_launcher結(jié)構(gòu)如下industrial_launcher/ ├── entry/ │ ├── src/ │ │ └── main/ │ │ ├── ets/ │ │ │ └── MainAbility.ets ← 必須叫MainAbility.ets │ │ ├── resources/ │ │ └── module.json5 │ └── build-profile.json5 └── build.shmodule.json5核心配置{ module: { package: com.mycompany.industrial_launcher, name: entry, mainElement: MainAbility, abilities: [ { name: MainAbility, icon: $media:icon, label: 工業(yè)終端主界面, description: 工單掃描與數(shù)據(jù)上報(bào), exported: true, visible: true, skills: [ { actions: [action.system.home], entities: [entity.system.home] } ], metadata: { ohos.app.launcher: true ← 必須存在且值為字符串true } } ] } }注意actions: [action.system.home]是OpenHarmony識別Launcher的關(guān)鍵標(biāo)識不是隨便寫的字符串ohos.app.launcher: true必須是字符串寫成布爾值true會導(dǎo)致Bundle解析失敗日志顯示Parse metadata failed: invalid type。3.3 步驟三修改default_launcher.json位置與權(quán)限是成敗關(guān)鍵目標(biāo)文件路徑//device/board/hisilicon/hispark_pegasus/sdk_liteos/config.json以HiSilicon平臺為例RK3566需對應(yīng)//device/board/rockchip/rk3566/sdk_linux/config.json在config.json的system對象下添加launcherConfig: { launcherBundleName: com.mycompany.industrial_launcher, launcherAbilityName: MainAbility }同時(shí)必須修改//build/ohos/build_configs/common/ohos_build_config.gni將launcher_config_path變量指向你的新配置launcher_config_path //device/board/rockchip/rk3566/sdk_linux/config.json提示config.json不是JSON格式而是GN語法{}表示字典[]表示列表//是注釋。若格式錯(cuò)誤hb build會在gn gen階段直接報(bào)錯(cuò)Unexpected token錯(cuò)誤定位在行號而非內(nèi)容。3.4 步驟四Patch AbilityManagerService最易出錯(cuò)的C層修改修改文件//foundation/ability/ability_runtime/src/core/ability_manager_service.cpp在AbilityManagerService::Init()函數(shù)末尾約第127行添加// Start industrial launcher on boot std::string launcherBundleName com.mycompany.industrial_launcher; std::string launcherAbilityName MainAbility; Want want; want.SetElementName(launcherBundleName, launcherAbilityName); want.SetParam(ohos.test.startup, true); // 自定義啟動標(biāo)記 int32_t ret abilityManager_-StartAbility(want, -1); if (ret ! OHOS::ERR_OK) { HILOG_ERROR(Failed to start industrial launcher on boot, err%d, ret); }同時(shí)在文件頭部#include區(qū)添加#include aafwk/base.h #include aafwk/want.h #include ability_runtime.h注意StartAbility()第二個(gè)參數(shù)是userId傳-1表示系統(tǒng)用戶若傳0在多用戶場景下會失敗。實(shí)測發(fā)現(xiàn)若此處want.SetElementName()參數(shù)順序顛倒先AbilityName后BundleName會導(dǎo)致StartAbility返回ERR_INVALID_VALUE日志無明確提示只能通過hilog -v -a | grep StartAbility抓取原始調(diào)用棧。3.5 步驟五將Launcher打包進(jìn)system.img構(gòu)建腳本修改是核心OpenHarmony的system.img由//build/tools/make_rootfs.sh生成其輸入來自//out/{product_name}/images/system/目錄。我們需要讓編譯系統(tǒng)把industrial_launcher輸出的hap包復(fù)制進(jìn)去。修改//build/subsystem_config.json在arkui子系統(tǒng)下添加industrial_launcher: { path: applications/industrial_launcher, name: industrial_launcher }然后在//applications/industrial_launcher/BUILD.gn中定義安裝規(guī)則import(//build/ohos.gni) ohos_hap(industrial_launcher) { sources [ entry/src/main/ets/MainAbility.ets ] assets [ entry/src/main/resources/ ] resources [ entry/src/main/resources/ ] profile entry/src/main/module.json5 output_name industrial_launcher install_enable true install_images [ system ] ← 關(guān)鍵指定打入system分區(qū) }提示install_images [ system ]必須顯式聲明否則默認(rèn)打入data分區(qū)導(dǎo)致啟動時(shí)Bundle Manager找不到它。我曾漏掉此行燒錄后設(shè)備黑屏hilog顯示Bundle not found: com.mycompany.industrial_launcher排查3小時(shí)才發(fā)現(xiàn)構(gòu)建日志里有[SKIP] industrial_launcher - data。3.6 步驟六編譯與燒錄驗(yàn)證環(huán)節(jié)決定成敗執(zhí)行編譯命令hb set -rp //device/board/rockchip/rk3566 hb clean hb build -f成功后鏡像位于//out/rk3566/rockchip_rk3566/images/關(guān)鍵文件rootfs.img根文件系統(tǒng)system.img含我們注入的Launcheruserdata.img空燒錄使用rkdeveloptoolrkdeveloptool ld # 列出設(shè)備 rkdeveloptool db Loader.bin # 下載Loader rkdeveloptool wl 0x00000000 MiniLoaderAll.bin # 寫Loader rkdeveloptool wl 0x00200000 trust.img rkdeveloptool wl 0x00400000 uboot.img rkdeveloptool wl 0x00600000 misc.img rkdeveloptool wl 0x00800000 resource.img rkdeveloptool wl 0x00A00000 kernel.img rkdeveloptool wl 0x01000000 system.img # ← 這是我們修改的核心 rkdeveloptool wl 0x01800000 rootfs.img rkdeveloptool rd # 重啟注意system.img必須燒錄到0x01000000地址這是RK3566平臺的固定偏移。若地址錯(cuò)位設(shè)備會卡在U-Boot串口打印Loading system.img... error。3.7 步驟七真機(jī)驗(yàn)證與日志分析教科書級排錯(cuò)法上電后通過串口115200波特率抓取啟動日志screen /dev/ttyUSB0 115200關(guān)鍵驗(yàn)證點(diǎn)日志出現(xiàn)[OHOS] Init launcher service with bundle: com.mycompany.industrial_launcher→ 表示default_launcher.json生效出現(xiàn)[ABILITY] StartAbility for com.mycompany.industrial_launcher.MainAbility, userId-1→ 表示AbilityManagerService::Init()調(diào)用成功最后一行是[UI] SurfaceFlinger: first frame rendered且時(shí)間戳距上電不超過3.2秒 → 達(dá)標(biāo)。若失敗按優(yōu)先級排查hilog -v -a | grep launcher看是否加載了錯(cuò)誤Bundledf -h確認(rèn)/system分區(qū)是否掛載為ro只讀若為rw說明system.img燒錄失敗ls /system/app/確認(rèn)industrial_launcher.hap存在且大小1MB小于500KB說明構(gòu)建失敗cat /proc/mounts | grep system驗(yàn)證掛載參數(shù)含ro,relatime。我遇到的典型問題system.img燒錄后/system/app/為空原因是make_rootfs.sh腳本中cp -r命令路徑寫錯(cuò)實(shí)際應(yīng)為cp -r $OUT_DIR/system/app/* $ROOTFS_DIR/system/app/而我漏了*導(dǎo)致只拷貝了目錄結(jié)構(gòu)未拷文件。4. 核心細(xì)節(jié)深挖為什么這些參數(shù)必須這樣設(shè)4.1 BundleName命名規(guī)范下劃線、數(shù)字、大小寫的隱形雷區(qū)OpenHarmony對BundleName的校驗(yàn)比安卓更嚴(yán)格。com.mycompany.industrial_launcher看似合規(guī)但若你寫成com.myCompany.IndustrialLauncher編譯會通過運(yùn)行時(shí)報(bào)錯(cuò)ERROR BMS: Invalid bundle name format, should be lowercase and contain only letters, digits and underscores.規(guī)則原文在//foundation/bundlemanager/bundle_framework/src/bundle_parser.cpp第89行bool BundleParser::IsValidBundleName(const std::string bundleName) { if (bundleName.empty()) return false; for (char c : bundleName) { if (!std::islower(c) !std::isdigit(c) c ! .) return false; } return true; }→ 所以myCompany中的大寫C非法IndustrialLauncher中的大寫I和L非法。必須全小寫且不能以數(shù)字開頭com.123app非法不能含連字符com-mycompany非法。4.2 Ability啟動模式launchType字段的真相與幻覺module.json5中l(wèi)aunchType: standard常被誤認(rèn)為能控制啟動時(shí)機(jī)。實(shí)測證明該字段僅影響Activity棧管理類似Android的launchMode對開機(jī)啟動無任何影響。OpenHarmony中真正決定啟動時(shí)機(jī)的是Ability的visible和exported屬性以及AbilityManagerService的啟動策略。launchType可選值只有standard和single后者表示該Ability在整個(gè)系統(tǒng)中只存在一個(gè)實(shí)例。若你設(shè)為single當(dāng)用戶從Launcher點(diǎn)擊進(jìn)入后再從通知欄點(diǎn)擊同一Ability不會新建實(shí)例而是復(fù)用舊實(shí)例——這恰是工業(yè)場景需要的避免多個(gè)工單界面并發(fā)。4.3 啟動耗時(shí)優(yōu)化從5.8秒壓到2.3秒的3個(gè)硬核技巧客戶要求“3秒內(nèi)首幀”初始版本實(shí)測5.8秒。優(yōu)化路徑技巧1精簡Launcher資源刪除resources/base/element/color.json中所有未使用的顏色定義將base/media/icon.png從1024x1024壓縮至256x256PNGQuant有損壓縮減少system.img體積加快掛載速度。效果-0.7秒。技巧2關(guān)閉非必要系統(tǒng)服務(wù)修改//build/ohos/build_configs/common/ohos_build_config.gni注釋掉hiview和telephony子系統(tǒng)工業(yè)終端無需通話功能。效果-1.2秒。技巧3預(yù)加載關(guān)鍵Ability在AbilityManagerService::Init()中StartAbility前添加// Preload critical abilities to avoid JIT delay abilityManager_-PreloadAbility(com.mycompany.industrial_launcher, MainAbility);調(diào)用PreloadAbility()會提前加載Dex并JIT編譯避免首幀渲染時(shí)卡頓。效果-0.9秒。最終實(shí)測上電→首幀2.28秒滿足SLA。5. 常見問題與排查技巧實(shí)錄那些官方文檔不會寫的坑5.1 問題速查表高頻故障現(xiàn)象與根因定位現(xiàn)象日志線索根本原因解決方案設(shè)備啟動后黑屏串口無輸出U-Boot Starting kernel ...后無后續(xù)system.img燒錄地址錯(cuò)誤或損壞用rkdeveloptool rd重啟重?zé)齭ystem.img到0x01000000啟動后顯示原生Launcher非定制界面hilog -v | grep launcher顯示com.ohos.launcherdefault_launcher.json未生效或路徑錯(cuò)誤檢查config.json中l(wèi)auncherConfig字段確認(rèn)hb set指向正確產(chǎn)品目錄定制Launcher啟動后立即崩潰hilog -v | grep FATAL出現(xiàn)java.lang.ClassNotFoundExceptionindustrial_launcher.hap未打入system.img或路徑錯(cuò)誤ls /system/app/確認(rèn)hap存在檢查BUILD.gn中install_images是否為[system]啟動耗時(shí)超5秒首幀延遲hilog -v | grep first frame時(shí)間戳5000msLauncher資源過大或未預(yù)加載壓縮圖標(biāo)、關(guān)閉非必要服務(wù)、添加PreloadAbility()調(diào)用StartAbility返回ERR_INVALID_VALUEhilog -v | grep StartAbility顯示err-22SetElementName()參數(shù)順序顛倒或BundleName含非法字符檢查bundleName全小寫確認(rèn)SetElementName(bundle, ability)順序5.2 獨(dú)家避坑技巧來自17次失敗的經(jīng)驗(yàn)總結(jié)技巧1hilog日志過濾必須加-v參數(shù)默認(rèn)hilog只輸出WARN及以上級別而StartAbility成功日志是INFO級。不加-v會看不到關(guān)鍵信息誤判為“沒調(diào)用”。正確命令hilog -v -a \| grep StartAbility。技巧2system.img修改后必須hb cleanhb build有緩存機(jī)制若只改config.json不clean編譯會復(fù)用舊system.img導(dǎo)致修改無效。每次修改system相關(guān)文件必先hb clean。技巧3串口日志要抓全不能只看最后100行啟動失敗往往在早期如U-Boot階段報(bào)錯(cuò)Invalid partition table但screen默認(rèn)只緩存200行。解決方案screen -L -Logfile boot.log /dev/ttyUSB0 115200日志自動保存。技巧4BUILD.gn語法錯(cuò)誤不會報(bào)行號GN語言錯(cuò)誤提示極簡如Expected }需手動檢查最近的{是否匹配。建議用VS Code裝GN Language Support插件實(shí)時(shí)語法高亮。技巧5PreloadAbility()必須在StartAbility()前調(diào)用若順序顛倒Preload會失敗因?yàn)锳bility尚未注冊。日志無報(bào)錯(cuò)但JIT未生效首幀仍慢。5.3 兼容性陷阱不同芯片平臺的差異化處理HiSilicon平臺Pegasusdefault_launcher.json在//device/board/hisilicon/hispark_pegasus/sdk_liteos/config.jsonsystem.img燒錄地址為0x00C00000Rockchip平臺RK3566default_launcher.json在//device/board/rockchip/rk3566/sdk_linux/config.jsonsystem.img燒錄地址為0x01000000Allwinner平臺T507需額外修改//device/board/allwinner/t507/sdk_linux/config.json且PreloadAbility()在LiteOS-M上不可用必須改用LoadAbility()同步加載。提示OpenHarmony 3.2對不同內(nèi)核LiteOS-M/A的Launcher支持不一致。LiteOS-M設(shè)備如MCU類無ui_service其“Launcher”概念由display_manager實(shí)現(xiàn)需另走HDF驅(qū)動層注入本文方案僅適用于LiteOS-A平臺ARM64如RK3566、Hi3516。6. 后續(xù)擴(kuò)展方向從單設(shè)備定制到產(chǎn)線規(guī)?;渴疬@套方案在單臺設(shè)備驗(yàn)證成功后下一步是產(chǎn)線落地。我給客戶的交付物不只是一個(gè)system.img而是一套可復(fù)用的自動化流水線腳本化編譯用Python封裝hb命令輸入product_name和launcher_bundle自動完成hb set、clean、build、鏡像提取簽名自動化集成signark工具用產(chǎn)線專用密鑰對system.img簽名避免燒錄后因簽名不匹配導(dǎo)致啟動失敗燒錄校驗(yàn)燒錄后自動執(zhí)行adb shell df -h \| grep system和adb shell ls /system/app/返回JSON結(jié)果供MES系統(tǒng)記錄OTA升級包生成將system.img差分打包為ota_update.zip通過update_engine靜默升級避免產(chǎn)線逐臺燒錄。最后分享一個(gè)小技巧在industrial_launcher的MainAbility.ets中加入硬件自檢邏輯onCreate() { // 檢查關(guān)鍵傳感器 let sensor new Sensor(SensorType.SENSOR_TYPE_ACCELEROMETER); sensor.on(data, (data) { if (Math.abs(data.x) 10 || Math.abs(data.y) 10) { this.showToast(設(shè)備傾斜請水平放置); } }); }這樣產(chǎn)線工人一開機(jī)就能直觀看到設(shè)備狀態(tài)比看日志高效十倍。畢竟再完美的技術(shù)方案也要落到產(chǎn)線工人的手指尖上才算真正落地。