HarmonyOS 鸿蒙Next中怎样统一处理应用错误
HarmonyOS 鸿蒙Next中怎样统一处理应用错误 应用需要统一的错误处理,怎样实现?
- 捕获并记录错误
- 友好的错误提示
- 生产环境日志收集
- 调试日志控制
3 回复
解决方案
1. 日志工具类
import { hilog } from '@kit.PerformanceAnalysisKit';
/**
* 日志工具类
*/
export class Logger {
private static readonly DOMAIN = 0x0001; // 日志域
private static readonly TAG = 'GlanceHome'; // 日志标签
private static isDebug: boolean = true; // 调试模式
/**
* 调试日志
*/
static debug(tag: string, message: string, ...args: Object[]): void {
if (this.isDebug) {
hilog.debug(this.DOMAIN, `${this.TAG}_${tag}`, message, args);
}
}
/**
* 信息日志
*/
static info(tag: string, message: string, ...args: Object[]): void {
hilog.info(this.DOMAIN, `${this.TAG}_${tag}`, message, args);
}
/**
* 警告日志
*/
static warn(tag: string, message: string, ...args: Object[]): void {
hilog.warn(this.DOMAIN, `${this.TAG}_${tag}`, message, args);
}
/**
* 错误日志
*/
static error(tag: string, message: string, error?: Error): void {
const errorMsg = error ? `${message}: ${error.message}` : message;
hilog.error(this.DOMAIN, `${this.TAG}_${tag}`, errorMsg);
// ✅ 生产环境上报错误
if (!this.isDebug) {
this.reportError(tag, errorMsg, error);
}
}
/**
* 上报错误到服务器
*/
private static async reportError(tag: string, message: string, error?: Error): Promise<void> {
try {
// TODO: 发送到错误收集服务
console.info('上报错误:', tag, message);
} catch (err) {
console.error('上报失败:', err);
}
}
/**
* 设置调试模式
*/
static setDebugMode(isDebug: boolean): void {
this.isDebug = isDebug;
}
}
// ✅ 使用
Logger.debug('Database', '查询物品', { id: 123 });
Logger.info('UI', '页面加载完成');
Logger.warn('Network', '网络请求慢');
Logger.error('Database', '查询失败', error);
2. 错误处理基类
/**
* 应用错误基类
*/
export class AppError extends Error {
code: string;
constructor(code: string, message: string) {
super(message);
this.code = code;
this.name = 'AppError';
}
}
/**
* 数据库错误
*/
export class DatabaseError extends AppError {
constructor(message: string) {
super('DB_ERROR', message);
this.name = 'DatabaseError';
}
}
/**
* 网络错误
*/
export class NetworkError extends AppError {
constructor(message: string) {
super('NET_ERROR', message);
this.name = 'NetworkError';
}
}
/**
* 业务错误
*/
export class BusinessError extends AppError {
constructor(code: string, message: string) {
super(code, message);
this.name = 'BusinessError';
}
}
3. 统一错误处理
/**
* 错误处理器
*/
export class ErrorHandler {
/**
* 处理错误
*/
static handle(error: Error | AppError, context: string = ''): void {
Logger.error(context || 'Global', '发生错误', error);
// ✅ 根据错误类型显示不同提示
let message = '操作失败,请稍后重试';
if (error instanceof DatabaseError) {
message = '数据保存失败';
} else if (error instanceof NetworkError) {
message = '网络连接失败';
} else if (error instanceof BusinessError) {
message = error.message; // 业务错误直接显示
}
promptAction.showToast({ message });
}
/**
* 安全执行(自动捕获错误)
*/
static async safeExecute<T>(
fn: () => Promise<T>,
context: string,
fallback?: T
): Promise<T | undefined> {
try {
return await fn();
} catch (error) {
this.handle(error as Error, context);
return fallback;
}
}
}
// ✅ 使用
async function loadData(): Promise<void> {
await ErrorHandler.safeExecute(
async () => {
const items = await ItemDao.findAll();
this.items = items;
},
'ItemList',
[] // 失败时返回空数组
);
}
4. 实战案例
/**
* 带完整错误处理的Service
*/
export class ItemService {
private static instance: ItemService;
private itemDao = ItemDao.getInstance();
static getInstance(): ItemService {
if (!ItemService.instance) {
ItemService.instance = new ItemService();
}
return ItemService.instance;
}
/**
* 添加物品
*/
async addItem(item: Item): Promise<number> {
const TAG = 'ItemService';
try {
Logger.debug(TAG, '开始添加物品', item);
// ✅ 业务校验
this.validateItem(item);
// ✅ 检查重复
const exists = await this.itemDao.existsByName(item.name);
if (exists) {
throw new BusinessError('DUPLICATE_NAME', '物品名称已存在');
}
// ✅ 保存到数据库
const itemId = await this.itemDao.insert(item);
Logger.info(TAG, '添加物品成功', { id: itemId });
return itemId;
} catch (error) {
// ✅ 转换数据库错误
if (error instanceof Error && error.message.includes('SQLITE')) {
throw new DatabaseError('数据保存失败');
}
// ✅ 记录并抛出
Logger.error(TAG, '添加物品失败', error as Error);
throw error;
}
}
/**
* 业务校验
*/
private validateItem(item: Item): void {
if (!item.name || item.name.trim() === '') {
throw new BusinessError('INVALID_NAME', '物品名称不能为空');
}
if (item.name.length > 50) {
throw new BusinessError('NAME_TOO_LONG', '物品名称不能超过50字');
}
}
/**
* 查询所有物品(安全)
*/
async getAllItems(): Promise<Item[]> {
return await ErrorHandler.safeExecute(
async () => {
return await this.itemDao.findAll();
},
'ItemService.getAllItems',
[] // 失败返回空数组
) || [];
}
}
5. 页面级错误处理
@Component
struct ItemListPage {
@State items: Item[] = [];
@State isLoading: boolean = false;
@State errorMessage: string = '';
async aboutToAppear(): Promise<void> {
await this.loadData();
}
async loadData(): Promise<void> {
this.isLoading = true;
this.errorMessage = '';
try {
Logger.debug('ItemListPage', '开始加载数据');
const items = await ItemService.getInstance().getAllItems();
if (items.length === 0) {
this.errorMessage = '暂无数据';
} else {
this.items = items;
Logger.info('ItemListPage', `加载成功: ${items.length}条`);
}
} catch (error) {
// ✅ 页面级错误处理
ErrorHandler.handle(error as Error, 'ItemListPage');
this.errorMessage = '加载失败';
} finally {
this.isLoading = false;
}
}
build() {
Column() {
if (this.isLoading) {
LoadingProgress();
} else if (this.errorMessage) {
this.buildErrorState();
} else {
this.buildList();
}
}
}
@Builder
buildErrorState() {
Column({ space: 16 }) {
Text(this.errorMessage)
.fontSize(14)
.fontColor('#999999');
Button('重新加载')
.onClick(() => {
this.loadData();
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
@Builder
buildList() {
List() {
ForEach(this.items, (item: Item) => {
ListItem() {
Text(item.name);
}
})
}
}
}
关键要点
1. 日志级别
Logger.debug() // 调试信息,生产环境不输出
Logger.info() // 重要信息
Logger.warn() // 警告信息
Logger.error() // 错误信息,需上报
2. 错误分类
// ✅ 系统错误
DatabaseError // 数据库错误
NetworkError // 网络错误
// ✅ 业务错误
BusinessError // 业务逻辑错误,可直接提示用户
3. 错误处理原则
// ✅ 低层抛出,高层捕获
// Dao层
async findById(id: number): Promise<Item> {
// 发生错误直接抛出
throw new DatabaseError('查询失败');
}
// Service层
async getItem(id: number): Promise<Item> {
try {
return await itemDao.findById(id);
} catch (err) {
// 记录日志并转换错误
Logger.error('ItemService', '获取物品失败', err);
throw err;
}
}
// UI层
async loadItem(): Promise<void> {
try {
this.item = await itemService.getItem(this.id);
} catch (err) {
// 最终捕获并提示用户
ErrorHandler.handle(err);
}
}
最佳实践
✅ 统一使用Logger记录日志 ✅ 自定义错误类型 ✅ 错误信息对用户友好 ✅ 生产环境上报错误 ✅ 使用safeExecute简化代码
完善的错误处理提升应用稳定性!
更多关于HarmonyOS 鸿蒙Next中怎样统一处理应用错误的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,统一处理应用错误主要通过全局异常处理器实现。开发者可在Application的onCreate方法中调用ErrorManager.registerErrorHandler()注册自定义错误处理器。该处理器需实现ErrorHandler接口,重写onError方法以捕获和处理未捕获的异常。
在HarmonyOS Next中,统一处理应用错误可以通过构建一个集中的错误管理机制来实现。以下是针对你提出的几个方面的具体实现思路:
1. 捕获并记录错误
- 全局异常捕获:在应用的入口文件(通常是
EntryAbility)中,使用errorManager或通过window.onerror(在Web兼容环境中)来设置全局未捕获异常处理器。这可以捕获到大多数未被局部try-catch处理的运行时错误。 - 异步错误处理:对于Promise异步操作,可以使用
Promise.catch()进行捕获,或者考虑使用async/await配合try-catch。 - 自定义错误边界:对于UI渲染错误,可以设计一个顶层的“错误边界”组件。这个组件使用生命周期方法(如
aboutToAppear)或@State装饰器来捕获子组件树的JavaScript异常,防止整个应用崩溃,并显示降级UI。 - 日志记录:在捕获到错误后,立即调用统一的日志记录函数。记录的信息应包括错误对象、时间戳、设备信息、用户操作路径等上下文信息。
2. 友好的错误提示
- 用户提示组件:设计一个全局的、可复用的错误提示组件(例如,一个Toast或Dialog弹窗)。当业务逻辑中发生可预知的错误(如网络请求失败、表单验证失败)时,通过状态管理或事件总线触发该组件显示。
- 错误分类与映射:建立一个错误码或错误类型到友好提示信息的映射表。根据捕获的错误类型,显示对应的、对用户有指导意义的文字,避免直接展示技术性错误堆栈。
- 降级UI:对于因错误导致无法渲染的组件,在错误边界中显示预设的友好占位界面,引导用户进行刷新或其他操作。
3. 生产环境日志收集
- 日志分级与过滤:实现日志分级(如Debug, Info, Warn, Error)。在生产环境中,通常只上报
Warn和Error级别的日志,并可能过滤掉一些敏感的或过于频繁的信息。 - 持久化与上报:将重要的错误日志持久化存储到本地(例如使用
Preferences或RDB)。可以设置一个日志上报服务,在适当的时机(如应用启动、网络恢复、达到一定数量)将本地缓存的日志批量上传到你的服务器。 - 关键信息附加:上报的日志应附带有助于诊断的上下文,如应用版本、HarmonyOS版本、设备型号、错误发生前的用户操作序列等。
4. 调试日志控制
- 环境感知:通过编译变量或配置文件区分开发、测试和生产环境。
- 条件输出:在开发环境下,可以将所有级别的日志(尤其是Debug信息)输出到控制台(
console.log),便于调试。在生产环境下,则完全禁用console.log或将其重定向到无操作函数,仅保留必要的错误记录和上报逻辑。 - 动态调试开关(可选):可以考虑实现一个通过特定操作(如连续点击版本号)激活的“调试模式”,在该模式下临时开启更详细的日志输出,方便在测试或生产环境复现问题时进行现场诊断。
架构建议
可以创建一个独立的 ErrorService 或 Logger 模块来封装以上所有功能。该模块提供统一的API供应用其他部分调用,例如 logError(error, extraInfo)、showUserToast(message) 等。在应用初始化时配置该模块,根据当前环境设置不同的行为策略。这样能够确保错误处理逻辑的一致性和可维护性。

