HarmonyOS开发:系统开发实战——完整系统应用开发
HarmonyOS开发:系统开发实战——完整系统应用开发
📌 核心要点:从签名到特权API,从系统UI到系统服务,从权限管理到日志调试——把前面9篇的知识串起来,手把手带你做一个完整的系统应用。
背景与动机
前面9篇讲了系统签名、系统UI、系统服务、系统配置、预装应用、权限提升、系统广播、系统窗口、系统调试——每篇都是一块拼图。但拼图散着放不叫画,你得把它们拼起来。
这篇就来做这件事。我们从头到尾开发一个完整的系统应用——“系统工具箱”。它包含:
- 系统签名和特权权限
- 系统UI定制(沉浸式状态栏)
- 系统属性读写
- 系统级事件监听(开机启动)
- 系统弹窗
- 权限管理
- 日志采集与诊断
这不是一个玩具Demo,而是一个可以真正跑在设备上的系统应用框架。你理解了这个框架,做任何系统应用都能快速上手。
核心原理
系统应用的整体架构
flowchart TD
A[系统工具箱 App] --> B[UI层]
A --> C[服务层]
A --> D[基础层]
B --> B1[主页面 - 功能入口]
B --> B2[系统信息页]
B --> B3[权限管理页]
B --> B4[配置管理页]
B --> B5[日志诊断页]
C --> C1[SystemEventService]
C --> C2[ConfigService]
C --> C3[PermissionService]
C --> C4[LogService]
D --> D1[Logger - 日志封装]
D --> D2[WindowManager - 窗口管理]
D --> D3[SystemParamManager - 参数管理]
D --> D4[DaemonManager - 守护进程]
classDef ui fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
classDef service fill:#e3f2fd,stroke:#2196f3,color:#0d47a1
classDef base fill:#fff3e0,stroke:#ff9800,color:#e65100
class B,B1,B2,B3,B4,B5 ui
class C,C1,C2,C3,C4 service
class D,D1,D2,D3,D4 base
系统应用的开发流程
flowchart LR
A[1. 项目创建] --> B[2. 签名配置]
B --> C[3. 权限声明]
C --> D[4. 基础层开发]
D --> E[5. 服务层开发]
E --> F[6. UI层开发]
F --> G[7. 系统签名打包]
G --> H[8. 部署测试]
classDef step fill:#e3f2fd,stroke:#2196f3,color:#0d47a1
classDef key fill:#fff3e0,stroke:#ff9800,color:#e65100
class A,C,D,E,F step
class B,G,H key
代码实战
第一步:项目配置
module.json5完整配置
{
"app": {
"bundleName": "com.hm.systemtoolbox",
"vendor": "hm",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name",
"systemApp": true,
"targetAPIVersion": 12,
"apiReleaseType": "Release",
"debug": false
},
"module": {
"name": "SystemToolBox",
"type": "entry",
"deviceTypes": ["default"],
"systemApp": true,
"installationFree": false,
"distro": {
"deliveryWithInstall": true,
"moduleName": "SystemToolBox",
"moduleType": "entry"
},
"requestPermissions": [
{
"name": "ohos.permission.SET_TIME",
"reason": "$string:perm_set_time",
"usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
},
{
"name": "ohos.permission.GET_BUNDLE_INFO_PRIVILEGED",
"reason": "$string:perm_bundle_info",
"usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
},
{
"name": "ohos.permission.INSTALL_BUNDLE",
"reason": "$string:perm_install",
"usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
},
{
"name": "ohos.permission.CONNECTIVITY_INTERNAL",
"reason": "$string:perm_network",
"usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
}
],
"abilities": [
{
"name": "MainAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:mainability_description",
"icon": "$media:icon",
"label": "$string:app_name",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
}
],
"extensionAbilities": [
{
"name": "BootReceiver",
"type": "staticSubscriber",
"label": "开机启动",
"exported": true,
"metadata": [
{
"name": "ohos.extension.staticSubscriber",
"resource": "$profile:boot_subscriber_config"
}
]
}
]
}
}
开机订阅配置
// resources/base/profile/boot_subscriber_config.json
{
"events": [
"usual.event.BOOT_COMPLETED",
"usual.event.SHUTDOWN",
"usual.event.SCREEN_ON",
"usual.event.SCREEN_OFF"
]
}
第二步:基础层开发
// common/Logger.ets - 日志封装
import hilog from '@ohos.hilog';
const DOMAIN = 0xD001;
class Logger {
private prefix: string;
constructor(prefix: string) {
this.prefix = prefix;
}
debug(msg: string, ...args: Object[]): void {
hilog.debug(DOMAIN, this.prefix, msg, args);
}
info(msg: string, ...args: Object[]): void {
hilog.info(DOMAIN, this.prefix, msg, args);
}
warn(msg: string, ...args: Object[]): void {
hilog.warn(DOMAIN, this.prefix, msg, args);
}
error(msg: string, ...args: Object[]): void {
hilog.error(DOMAIN, this.prefix, msg, args);
}
}
export function createLogger(prefix: string): Logger {
return new Logger(prefix);
}
// common/WindowHelper.ets - 窗口管理
import window from '@ohos.window';
import { createLogger } from './Logger';
const logger = createLogger('WindowHelper');
class WindowHelper {
private mainWindow: window.Window | null = null;
private statusBarHeight: number = 0;
private navBarHeight: number = 0;
// 初始化主窗口
async init(windowStage: window.WindowStage): Promise<void> {
this.mainWindow = await windowStage.getMainWindow();
await this.mainWindow.setWindowLayoutFullScreen(true);
await this.loadAvoidAreaSize();
logger.info('窗口初始化完成');
}
// 加载安全区域尺寸
private async loadAvoidAreaSize(): Promise<void> {
if (!this.mainWindow) return;
const systemAvoid = await this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
this.statusBarHeight = systemAvoid.topRect.height;
const navAvoid = await this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION);
this.navBarHeight = navAvoid.bottomRect.height;
}
// 设置状态栏样式
async setSystemBarStyle(bgColor: string, isLightContent: boolean): Promise<void> {
if (!this.mainWindow) return;
const props: window.SystemBarProperties = {
statusBarColor: bgColor,
statusBarContentColor: isLightContent ? '#ffffff' : '#000000',
navigationBarColor: bgColor,
navigationBarContentColor: isLightContent ? '#ffffff' : '#000000',
};
await this.mainWindow.setWindowSystemBarProperties(props);
}
getStatusBarHeight(): number {
return this.statusBarHeight;
}
getNavBarHeight(): number {
return this.navBarHeight;
}
}
export default new WindowHelper();
// common/SystemParamHelper.ets - 系统参数管理
import systemParameter from '@ohos.systemParameter';
import { createLogger } from './Logger';
const logger = createLogger('SystemParamHelper');
class SystemParamHelper {
get(key: string, defaultValue: string = ''): string {
try {
return systemParameter.getSync(key, defaultValue);
} catch (err) {
logger.warn(`读取参数失败: ${key}`);
return defaultValue;
}
}
set(key: string, value: string): boolean {
try {
systemParameter.setSync(key, value);
logger.info(`写入参数成功: ${key} = ${value}`);
return true;
} catch (err) {
logger.error(`写入参数失败: ${key}`);
return false;
}
}
}
export default new SystemParamHelper();
第三步:服务层开发
// service/AppService.ets - 应用核心服务
import { createLogger } from '../common/Logger';
import SystemParamHelper from '../common/SystemParamHelper';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import bundleManager from '@ohos.bundle.bundleManager';
const logger = createLogger('AppService');
// 系统信息
interface SystemInfo {
deviceModel: string;
deviceBrand: string;
sdkVersion: string;
bootMode: string;
isSystemApp: boolean;
}
// 权限状态
interface PermissionStatus {
permission: string;
granted: boolean;
level: string;
}
class AppService {
private systemInfo: SystemInfo | null = null;
private permissionStatuses: PermissionStatus[] = [];
// 初始化服务
async init(): Promise<void> {
logger.info('AppService 初始化开始');
this.systemInfo = await this.loadSystemInfo();
await this.loadPermissionStatuses();
logger.info('AppService 初始化完成');
}
// 加载系统信息
private async loadSystemInfo(): Promise<SystemInfo> {
const info: SystemInfo = {
deviceModel: SystemParamHelper.get('ro.product.model', '未知'),
deviceBrand: SystemParamHelper.get('ro.product.brand', '未知'),
sdkVersion: SystemParamHelper.get('ro.build.version.sdk', '未知'),
bootMode: SystemParamHelper.get('ohos.boot.mode', '未知'),
isSystemApp: false,
};
// 检查是否为系统应用
try {
const bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
);
info.isSystemApp = bundleInfo.appInfo.systemApp;
} catch (err) {
logger.warn('检查系统应用状态失败');
}
return info;
}
// 加载权限状态
private async loadPermissionStatuses(): Promise<void> {
const permissions: Array<{ name: string; level: string }> = [
{ name: 'ohos.permission.SET_TIME', level: 'system' },
{ name: 'ohos.permission.SET_TIME_ZONE', level: 'system' },
{ name: 'ohos.permission.INSTALL_BUNDLE', level: 'system' },
{ name: 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED', level: 'system' },
{ name: 'ohos.permission.UNINSTALL_BUNDLE', level: 'system_core' },
{ name: 'ohos.permission.WRITE_SYSTEM_SETTINGS', level: 'system_core' },
{ name: 'ohos.permission.FACTORY_RESET', level: 'system_core' },
];
const atManager = abilityAccessCtrl.createAtManager();
try {
const bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
);
const tokenID = bundleInfo.appInfo.accessTokenId;
this.permissionStatuses = await Promise.all(
permissions.map(async (p) => {
let granted = false;
try {
const result = await atManager.checkAccessToken(tokenID, p.name as Permissions);
granted = result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch (e) {
// 权限检查失败视为未授权
}
return { permission: p.name, granted, level: p.level };
})
);
} catch (err) {
logger.error('加载权限状态失败');
}
}
// 获取系统信息
getSystemInfo(): SystemInfo {
return this.systemInfo || {
deviceModel: '未知',
deviceBrand: '未知',
sdkVersion: '未知',
bootMode: '未知',
isSystemApp: false,
};
}
// 获取权限状态
getPermissionStatuses(): PermissionStatus[] {
return this.permissionStatuses;
}
// 获取权限通过率
getPermissionPassRate(): { system: number; core: number } {
const sysPerms = this.permissionStatuses.filter(p => p.level === 'system');
const corePerms = this.permissionStatuses.filter(p => p.level === 'system_core');
const sysPass = sysPerms.filter(p => p.granted).length;
const corePass = corePerms.filter(p => p.granted).length;
return {
system: sysPerms.length > 0 ? Math.round(sysPass / sysPerms.length * 100) : 0,
core: corePerms.length > 0 ? Math.round(corePass / corePerms.length * 100) : 0,
};
}
}
export default new AppService();
第四步:UI层开发——主页面
// pages/Index.ets - 系统工具箱主页面
import AppService from '../service/AppService';
import WindowHelper from '../common/WindowHelper';
import router from '@ohos.router';
@Entry
@Component
struct IndexPage {
@State statusBarHeight: number = 0;
@State isSystemApp: boolean = false;
@State deviceModel: string = '';
async aboutToAppear() {
// 初始化服务
await AppService.init();
// 设置状态栏
await WindowHelper.setSystemBarStyle('#1976d2', true);
this.statusBarHeight = WindowHelper.getStatusBarHeight();
// 加载数据
const sysInfo = AppService.getSystemInfo();
this.isSystemApp = sysInfo.isSystemApp;
this.deviceModel = sysInfo.deviceModel;
}
build() {
Column() {
// 状态栏占位
Row().width('100%').height(this.statusBarHeight)
// 标题栏
Row() {
Text('系统工具箱')
.fontSize(22)
.fontWeight(FontWeight.Bold)
.fontColor('#ffffff')
// 系统签名状态
Row() {
Circle({ width: 8, height: 8 })
.fill(this.isSystemApp ? '#4caf50' : '#f44336')
Text(this.isSystemApp ? '系统签名' : '普通签名')
.fontSize(11)
.fontColor('#ffffff')
.margin({ left: 4 })
}
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.backgroundColor('#ffffff20')
.borderRadius(10)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding({ left: 20, right: 20, top: 12, bottom: 12 })
.backgroundColor('#1976d2')
// 功能入口
Scroll() {
Column() {
// 系统信息卡片
this.FeatureCard(
'📱',
'系统信息',
`设备: ${this.deviceModel}`,
() => router.pushUrl({ url: 'pages/SystemInfoPage' })
)
// 权限管理
this.FeatureCard(
'🔑',
'权限管理',
'查看和审计系统权限状态',
() => router.pushUrl({ url: 'pages/PermissionPage' })
)
// 配置管理
this.FeatureCard(
'⚙️',
'配置管理',
'读写系统参数和属性',
() => router.pushUrl({ url: 'pages/ConfigPage' })
)
// 日志诊断
this.FeatureCard(
'📋',
'日志诊断',
'HiLog采集与hdc命令参考',
() => router.pushUrl({ url: 'pages/LogPage' })
)
// 系统弹窗测试
this.FeatureCard(
'🔔',
'系统弹窗',
'测试系统级弹窗和悬浮窗',
() => router.pushUrl({ url: 'pages/DialogPage' })
)
// 权限通过率概览
this.PermissionOverview()
}
.padding(16)
}
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5')
}
@Builder
FeatureCard(icon: string, title: string, desc: string, action: () => void) {
Row() {
Text(icon)
.fontSize(32)
.margin({ right: 16 })
Column() {
Text(title)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Text(desc)
.fontSize(13)
.fontColor('#999999')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('›')
.fontSize(24)
.fontColor('#cccccc')
}
.width('100%')
.padding(16)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ bottom: 12 })
.onClick(action)
}
@Builder
PermissionOverview() {
Column() {
Text('权限概览')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 12 })
Row() {
Column() {
Text('system权限')
.fontSize(12)
.fontColor('#999999')
Text(`${AppService.getPermissionPassRate().system}%`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#ff9800')
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Center)
Column() {
Text('system_core权限')
.fontSize(12)
.fontColor('#999999')
Text(`${AppService.getPermissionPassRate().core}%`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#f44336')
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Center)
}
}
.width('100%')
.padding(16)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ top: 4 })
}
}
第五步:Ability入口
// entryability/EntryAbility.ets
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import WindowHelper from '../common/WindowHelper';
import { createLogger } from '../common/Logger';
const logger = createLogger('EntryAbility');
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam): void {
logger.info('Ability onCreate');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
logger.info('Ability onWindowStageCreate');
// 初始化窗口
WindowHelper.init(windowStage).then(() => {
logger.info('窗口初始化完成');
});
// 加载主页面
windowStage.loadContent('pages/Index', (err, data) => {
if (err) {
logger.error(`加载页面失败: ${JSON.stringify(err)}`);
return;
}
logger.info('页面加载完成');
});
}
onForeground(): void {
logger.info('Ability onForeground');
}
onBackground(): void {
logger.info('Ability onBackground');
}
onDestroy(): void {
logger.info('Ability onDestroy');
}
}
第六步:开机启动处理
// staticsubscriber/BootSubscriber.ets - 开机事件处理
import StaticSubscriberExtensionAbility from '@ohos.application.StaticSubscriberExtensionAbility';
import { createLogger } from '../common/Logger';
const logger = createLogger('BootSubscriber');
export default class BootSubscriber extends StaticSubscriberExtensionAbility {
onReceiveEvent(event: string): void {
logger.info(`收到系统事件: ${event}`);
switch (event) {
case 'usual.event.BOOT_COMPLETED':
this.onBootCompleted();
break;
case 'usual.event.SHUTDOWN':
this.onShutdown();
break;
case 'usual.event.SCREEN_ON':
this.onScreenOn();
break;
case 'usual.event.SCREEN_OFF':
this.onScreenOff();
break;
default:
logger.warn(`未处理的事件: ${event}`);
}
}
private onBootCompleted(): void {
logger.info('开机完成,启动系统工具箱服务');
// 初始化系统服务、注册守护进程等
}
private onShutdown(): void {
logger.info('系统关机,保存状态');
// 保存应用状态、清理资源
}
private onScreenOn(): void {
logger.info('屏幕亮起');
}
private onScreenOff(): void {
logger.info('屏幕熄灭');
}
}
踩坑与注意事项
坑一:系统签名应用的开发调试流程
系统签名应用的开发调试流程跟普通应用不一样。普通应用直接在DevEco Studio里点Run就行,系统签名应用需要额外步骤:
- 开发阶段:用调试证书开发,特权API调用会失败,但UI和基础功能可以调试
- 签名阶段:用
hapsigner对HAP做系统签名 - 部署阶段:用
hdc install安装签名后的HAP - 验证阶段:在设备上验证特权API是否正常
建议写一个自动化脚本,把签名和部署串起来:
# auto_deploy.sh - 自动签名部署脚本
#!/bin/bash
# 1. 编译
hvigorw assembleHap
# 2. 系统签名
java -jar hapsigner.jar sign-app \
-keyAlias "platform" \
-keyPwd "$PLATFORM_KEY_PWD" \
-keystoreFile "./certs/platform.p12" \
-keystorePwd "$PLATFORM_KS_PWD" \
-appCertFile "./certs/app.cer" \
-profileFile "./certs/profile.p7b" \
-inFile "./build/outputs/entry-default-signed.hap" \
-outFile "./build/outputs/entry-system-signed.hap" \
-signAlg SHA256withECDSA \
-mode localSign
# 3. 部署
hdc install -r "./build/outputs/entry-system-signed.hap"
echo "部署完成!"
坑二:多页面共享状态管理
系统应用通常有多个页面,页面之间需要共享状态(比如系统信息、权限状态)。如果每个页面都自己去查,既浪费又容易不一致。
推荐用AppStorage或自定义的单例服务管理共享状态:
// 方式一:AppStorage(适合简单状态)
AppStorage.setOrCreate('isSystemApp', true);
const isSystemApp = AppStorage.get<boolean>('isSystemApp');
// 方式二:单例服务(适合复杂逻辑,推荐)
// 就像上面的AppService,所有页面通过AppService.xxx()访问
坑三:系统应用的权限降级测试
你的应用有系统签名,所有system权限都能通过。但你怎么测试"签名不够"的场景?
办法:临时用普通签名打包,测试权限校验逻辑。确保在签名不够的情况下,应用能优雅降级而不是直接崩溃。
// 权限校验 + 优雅降级
async function setSystemTime(timestamp: number): Promise<boolean> {
// 先检查权限
const hasPermission = await PermissionManager.checkPermission('ohos.permission.SET_TIME');
if (!hasPermission) {
logger.warn('没有设置时间的权限,降级处理');
// 降级:跳转到系统设置页面让用户手动设置
this.jumpToSystemSettings();
return false;
}
// 有权限,执行操作
await systemDateTime.setTime(timestamp);
return true;
}
坑四:系统应用的性能优化
系统应用是常驻内存的,性能问题比普通应用影响更大。几个优化点:
- 延迟初始化:非核心服务不要在
onCreate里初始化,等用到时再初始化 - 日志控制:Release包关闭Debug日志,减少I/O开销
- 内存监控:定期检查内存使用,发现泄漏及时处理
- 后台资源释放:切到后台时释放不必要的资源
// 延迟初始化示例
class LazyService {
private instance: HeavyService | null = null;
getService(): HeavyService {
if (!this.instance) {
this.instance = new HeavyService();
}
return this.instance;
}
}
坑五:系统签名密钥的管理
系统签名密钥是整个安全体系的根基。密钥泄露了,任何人都能做系统应用,安全体系直接崩塌。
密钥管理最佳实践:
- 密钥文件不要放在代码仓库里,用CI/CD的Secret管理
- 构建服务器做好访问控制,只有授权人员能触发签名
- 不同产品线用不同密钥,一个泄露不影响其他
- 定期轮换密钥(至少每年一次)
- 签名操作要有审计日志
HarmonyOS 6适配说明
HarmonyOS 6对系统应用开发有几个整体性的变化:
-
模块化架构:系统应用推荐使用模块化架构,每个功能模块是独立的HAP,按需加载。主HAP只包含入口和核心功能,其他功能模块在用户首次使用时下载。
-
系统应用沙箱:即使有系统签名,系统应用也运行在沙箱中。跨应用数据访问需要通过
DataShare接口,不再能直接读其他应用的文件。 -
权限声明强制校验:所有system及以上权限必须声明
reason和usedScene,否则安装拒绝。 -
系统应用热更新:新增系统应用的热更新机制,不需要系统OTA就能更新系统应用。通过
SystemAppUpdater接口实现。
适配建议:新项目采用模块化架构,权限声明严格按要求填写,利用热更新机制降低系统应用更新成本。
总结
系统应用开发是一个系统工程,不是写几行代码就完事的。从签名配置到权限管理,从服务层到UI层,从开机启动到守护进程,每个环节都要考虑到。
核心要点回顾:
- 签名先行:没有系统签名,一切特权API都是空谈
- 分层架构:基础层→服务层→UI层,职责清晰,维护方便
- 权限管理:声明要完整,校验要严格,降级要优雅
- 日志先行:开发阶段就把日志体系搭好,线上问题才好排查
- 守护保活:系统服务必须稳,被杀了能重启,重启要有退避
| 维度 | 评价 |
|---|---|
| 学习难度 | ⭐⭐⭐⭐⭐ 综合性强,需要掌握签名、权限、服务、UI等多个领域 |
| 使用频率 | ⭐⭐⭐⭐ 系统级开发必用,是所有系统应用的基础框架 |
| 重要程度 | ⭐⭐⭐⭐⭐ 系统应用开发的集大成,掌握它就掌握了系统开发的全貌 |
- 点赞
- 收藏
- 关注作者
评论(0)