在鸿蒙应用开发中,业务逻辑处理常常面临一个典型困境:如何优雅地处理操作结果和错误反馈。传统方式要么抛出异常,要么返回可空值,但这两种方法都存在明显缺陷。异常会中断程序正常流程,而可空值无法承载详细的错误信息。error_or 库的出现,为这个问题提供了优雅的解决方案。
error_or 是一个受 .NET 社区启发的 Dart 库,它引入了 ErrorOr<TValue> 类型,将操作结果明确区分为成功(包含值)或失败(包含一个或多个错误)。这种设计特别适合鸿蒙应用开发,因为它能:
error_or 的核心是联合类型(Union Type)概念。一个 ErrorOr<T> 实例只能是以下两种状态之一:
这种设计强制开发者显式处理所有可能的结果,避免了意外异常导致的程序崩溃。在鸿蒙开发中,这种显式处理尤为重要,因为鸿蒙应用往往需要跨设备、跨场景运行,稳定性至关重要。
传统方式通常有以下几种:
error_or 综合了这些方式的优点,同时避免了它们的缺点。它提供了类型安全的错误处理,同时保持了代码的简洁性。
在鸿蒙生态中,error_or 特别适合以下场景:
在鸿蒙项目中使用 error_or 非常简单,只需要在 pubspec.yaml 中添加依赖:
yaml复制dependencies:
error_or: ^1.0.0
然后运行 flutter pub get 即可。由于 error_or 是纯 Dart 实现,不需要任何原生平台支持,因此在鸿蒙环境中可以无缝使用。
下面是一个简单的使用示例,展示如何在鸿蒙应用中处理用户登录:
dart复制import 'package:error_or/error_or.dart';
ErrorOr<User> login(String username, String password) {
final errors = <Error>[];
if (username.isEmpty) {
errors.add(Error.validation(description: "用户名不能为空"));
}
if (password.length < 6) {
errors.add(Error.validation(description: "密码长度不能少于6位"));
}
if (errors.isNotEmpty) {
return errors;
}
// 模拟登录成功
return User(name: username, token: "mock_token");
}
void onLoginPressed() {
final result = login(usernameController.text, passwordController.text);
result.match(
(user) {
// 登录成功,跳转到主页
Navigator.push(context, MaterialPageRoute(builder: (_) => HomePage(user)));
},
(errors) {
// 显示所有错误信息
for (final error in errors) {
showDialog(context: context, builder: (_) => AlertDialog(
title: Text("错误"),
content: Text(error.description),
));
}
}
);
}
error_or 提供了多种创建 ErrorOr 实例的方式:
dart复制// 成功结果
final success = ErrorOr<String>.from("成功数据");
// 单个错误
final singleError = ErrorOr<int>.error(Error.validation(description: "验证失败"));
// 多个错误
final multipleErrors = ErrorOr<double>.errors([
Error.unexpected(description: "意外错误1"),
Error.unexpected(description: "意外错误2")
]);
// 从可空值转换
String? nullableValue = "非空值";
final fromNullable = nullableValue.toErrorOr();
error_or 提供了多种方式检查和提取值:
dart复制final result = someOperation();
// 检查状态
if (result.isValue) {
print("操作成功");
}
if (result.isError) {
print("操作失败");
}
// 直接获取值(不安全,仅在确定成功时使用)
final value = result.value;
// 安全获取值
final safeValue = result.valueOrNull;
// 获取错误列表
final errors = result.errors;
error_or 的核心功能是模式匹配,它提供了 match 方法:
dart复制result.match(
(value) {
// 处理成功情况
updateUIWithValue(value);
},
(errors) {
// 处理失败情况
showErrors(errors);
}
);
此外,还提供了一系列转换方法:
dart复制// 映射值
final mapped = result.map((value) => value.length);
// 扁平映射
final flatMapped = result.flatMap((value) => anotherOperation(value));
// 错误恢复
final recovered = result.recover((errors) => defaultValue);
在鸿蒙应用中,表单验证是常见需求。error_or 可以优雅地处理多字段验证:
dart复制ErrorOr<RegistrationData> validateRegistrationForm({
required String username,
required String email,
required String password,
}) {
final errors = <Error>[];
if (username.isEmpty) {
errors.add(Error.validation(field: "username", description: "用户名不能为空"));
} else if (username.length < 3) {
errors.add(Error.validation(field: "username", description: "用户名至少3个字符"));
}
if (!EmailValidator.validate(email)) {
errors.add(Error.validation(field: "email", description: "邮箱格式不正确"));
}
if (password.length < 8) {
errors.add(Error.validation(field: "password", description: "密码至少8个字符"));
}
return errors.isEmpty
? RegistrationData(username, email, password)
: errors;
}
鸿蒙的分布式特性使得跨设备操作成为可能。error_or 可以统一处理分布式操作中的各种错误:
dart复制ErrorOr<File> transferFileToRemoteDevice(File file, Device target) async {
final errors = <Error>[];
if (!await checkNetworkConnection()) {
errors.add(Error.unavailable(description: "网络不可用"));
}
if (!await target.isOnline()) {
errors.add(Error.unavailable(description: "目标设备离线"));
}
if (await target.getFreeSpace() < file.lengthSync()) {
errors.add(Error.failure(description: "目标设备存储空间不足"));
}
if (errors.isNotEmpty) {
return errors;
}
try {
final transferredFile = await DistributedFileManager.transfer(file, target);
return transferredFile;
} catch (e) {
return Error.unexpected(description: "文件传输失败: ${e.toString()}");
}
}
处理网络API调用时,error_or 可以统一封装各种错误情况:
dart复制ErrorOr<User> fetchUserProfile(int userId) async {
try {
final response = await http.get(Uri.parse("$apiUrl/users/$userId"));
if (response.statusCode == 200) {
return User.fromJson(jsonDecode(response.body));
}
if (response.statusCode == 404) {
return Error.notFound(description: "用户不存在");
}
if (response.statusCode == 401) {
return Error.unauthorized(description: "认证失败");
}
return Error.unexpected(
description: "服务器错误: ${response.statusCode}"
);
} on SocketException {
return Error.unavailable(description: "网络连接失败");
} on FormatException {
return Error.unexpected(description: "响应数据格式错误");
} catch (e) {
return Error.unexpected(description: "未知错误: ${e.toString()}");
}
}
虽然 error_or 提供了内置错误类型,但在鸿蒙开发中,我们经常需要自定义错误:
dart复制class OhosError extends Error {
final int ohosErrorCode;
final String ohosErrorMessage;
OhosError(this.ohosErrorCode, this.ohosErrorMessage);
@override
String get description => "鸿蒙错误[$ohosErrorCode]: $ohosErrorMessage";
}
ErrorOr<void> checkOhosPermission(String permission) {
final status = OhosPermissionManager.checkPermission(permission);
if (status != PermissionStatus.granted) {
return OhosError(
status.code,
"缺少必要权限: $permission"
);
}
return null; // 表示成功
}
在鸿蒙应用中,建议建立一个统一的错误处理层:
dart复制extension ErrorOrUiExtension<T> on ErrorOr<T> {
void handleWithOhosUi(BuildContext context) {
match(
(value) => OhosToast.showSuccess(context, "操作成功"),
(errors) {
for (final error in errors) {
OhosDialog.showError(
context,
title: "操作失败",
message: error.description
);
}
}
);
}
}
// 使用示例
fetchUserProfile(userId).handleWithOhosUi(context);
当需要组合多个可能失败的操作时,可以使用以下模式:
dart复制ErrorOr<UserProfile> loadCompleteUserProfile(int userId) {
return fetchUserProfile(userId).flatMap((profile) {
return fetchUserPreferences(profile.id).map((preferences) {
return UserProfile.complete(profile, preferences);
});
});
}
error_or 的实现非常轻量,主要开销在于:
在实际应用中,这些开销通常可以忽略不计。但对于性能敏感的代码路径,可以考虑以下优化:
鸿蒙原生开发通常使用以下错误处理方式:
error_or 提供了一种跨语言的统一错误处理模式,特别适合 Flutter for OpenHarmony 的跨平台特性。它比异常更轻量,比错误码更安全,比简单的返回值更强大。
测试 error_or 代码时,可以遵循以下模式:
dart复制void main() {
test('success case', () {
final result = ErrorOr<int>.from(42);
expect(result.isValue, true);
expect(result.value, 42);
});
test('error case', () {
final result = ErrorOr<int>.error(Error.validation());
expect(result.isError, true);
expect(result.errors.length, 1);
});
test('operation that may fail', () {
final successResult = operationThatMayFail(validInput);
expect(successResult.isValue, true);
final errorResult = operationThatMayFail(invalidInput);
expect(errorResult.isError, true);
});
}
toString() 方法查看 ErrorOr 状态dart复制extension ErrorOrDebugExtension<T> on ErrorOr<T> {
void debugPrint() {
match(
(value) => print("✅ Success: $value"),
(errors) => print("❌ Errors: ${errors.join(", ")}")
);
}
}
在鸿蒙Ability中使用 error_or:
dart复制class MyAbility extends Ability {
ErrorOr<void> onRemoteRequest(int code, MessageParcel data) {
try {
final request = parseRequest(data);
return handleRequest(request);
} catch (e) {
return Error.unexpected(description: "请求处理失败: ${e.toString()}");
}
}
// ...
}
处理分布式数据操作:
dart复制ErrorOr<List<Contact>> syncContactsAcrossDevices(List<Device> devices) {
final results = <ErrorOr<List<Contact>>>[];
for (final device in devices) {
results.add(fetchContactsFromDevice(device));
}
return ErrorOr.collect(results).map((contactLists) {
return contactLists.expand((list) => list).toList();
});
}
在实际鸿蒙项目中采用 error_or 后,我们获得了以下经验:
一个特别有用的实践是为常见错误类型创建扩展方法:
dart复制extension ErrorOrFormValidation on ErrorOr<void> {
void showAsFormErrors(BuildContext context) {
if (isError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Column(
children: errors.map((e) => Text(e.description)).toList(),
),
),
);
}
}
}
另一个实用技巧是结合鸿蒙的原子化服务概念,为每个服务操作定义清晰的错误类型:
dart复制abstract class OhosServiceError extends Error {
String get serviceName;
@override
String get description => "[$serviceName] ${super.description}";
}
class CalendarServiceError extends OhosServiceError {
@override
final String serviceName = "Calendar";
CalendarServiceError(String message) : super(description: message);
}