开发背景
在移动应用开发中,闪光灯(手电筒)是一个常见且实用的功能。随着 HarmonyOS Next 的推出,越来越多的开发者开始将应用迁移到鸿蒙平台。我们开发了 harmony-flashlight 插件。该插件基于 UTS 封装鸿蒙原生 Camera Kit API,为 uni-app 开发者提供了一套简洁易用的闪光灯控制接口。插件完全遵循 uni-app 的 API 设计规范,支持 success/fail/complete 回调模式,让开发者能够以熟悉的方式在鸿蒙平台上实现手电筒功能。
这个过程非常适合学习和了解如何开发鸿蒙 uts 插件,特地编写此文用于展示整个过程。
鸿蒙原生实现
HarmonyOS Next 通过 Camera Kit 提供了完整的相机功能支持,其中包括闪光灯(Torch)控制能力。要在鸿蒙平台实现闪光灯控制,需要理解以下核心概念和 API:
Camera Manager
Camera Manager 是鸿蒙相机功能的核心管理器,负责相机设备的枚举、创建和配置。获取 Camera Manager 实例是使用相机相关功能的前提:
camera.getCameraManager(context) 方法用于获取相机管理器实例,需要传入 UIAbility 的 Context 上下文对象。该实例在整个应用生命周期中应该被复用,避免重复创建造成资源浪费。
Torch Mode 控制
鸿蒙系统定义了 camera.TorchMode 枚举,包含两种模式:
TorchMode.ON:开启闪光灯(手电筒模式)TorchMode.OFF:关闭闪光灯
在实际使用前,必须进行两项检查:
- 通过
isTorchSupported()方法检查设备是否支持手电筒功能。部分低端设备或特殊硬件可能不具备闪光灯,此方法可避免在不支持的设备上调用相关 API 导致异常。 - 通过
isTorchModeSupported(mode)方法检查指定的 Torch Mode 是否被支持。虽然大多数设备同时支持 ON 和 OFF 两种模式,但规范的做法是在使用前进行验证。 - 通过
setTorchMode(mode)方法设置闪光灯模式。该方法会立即生效,改变设备闪光灯的状态。
权限配置
闪光灯控制依赖于相机权限。在 module.json5 配置文件中,必须声明 ohos.permission.CAMERA 权限。
错误处理
鸿蒙原生 API 在发生错误时会抛出 BusinessError 异常,主要错误码包括:
7400102:操作不被允许(如权限未授予、设备被占用)7400201:相机服务内部错误(如服务异常、硬件故障)
规范的错误处理需要捕获异常并根据错误码进行相应的提示和处理。
UTS 封装实现
UTS 是 uni-app 推出的面向跨平台原生开发的类型化语言。它允许开发者使用接近 TypeScript 的语法调用各平台的原生 API,同时保持类型安全。本插件的 UTS 封装主要包含三个核心文件:
类型定义层(interface.uts)
类型定义层定义了插件对外暴露的所有类型和接口,确保 API 调用的类型安全性。
主要定义包括:
FlashlightSuccess类型定义成功回调的返回结构,包含errMsg字符串字段TurnOnFlashlightOptions和TurnOffFlashlightOptions类型定义了可选的配置参数,包括success、fail和complete三个回调函数- 回调函数类型采用函数签名定义,如
FlashlightSuccessCallback = (res: FlashlightSuccess) => void TurnOnFlashlight和TurnOffFlashlight函数类型定义了接口的调用签名
这种类型定义方式完全符合 uni-app 的 API 设计规范,与 uni-app 内置 API 保持一致的调用体验。
错误处理层(unierror.uts)
错误处理层通过继承 UniError 基类,实现了标准化的错误对象。
FlashlightErrorCode 类型使用 TypeScript 联合类型定义了四个错误码:
13001:设备不支持闪光灯(硬件能力限制)13002:操作不允许(权限问题或设备占用)13003:相机服务错误(系统服务异常)13004:未知错误(兜底错误码)
FlashlightFailImpl 类的构造函数接收错误码,通过 switch 语句映射到对应的错误消息。所有错误对象的 errSubject 统一设置为 'harmony-flashlight',便于开发者定位错误来源。
这种错误处理机制实现了鸿蒙原生错误码到 uni-app 错误规范的转换,屏蔽了平台差异,让开发者能够以统一的方式处理错误。
核心实现层(index.uts)
核心实现层是插件的主体逻辑,负责调用鸿蒙原生 API 并处理业务流程。
使用模块级变量 cameraManager 存储相机管理器实例,通过 getCameraManager() 函数实现懒加载和复用。首次调用时,通过 camera.getCameraManager(UTSHarmony.getUIAbilityContext()) 创建实例,后续调用直接返回缓存实例。这种设计避免了重复创建管理器的开销,提升了性能。
turnOnFlashlight 和 turnOffFlashlight 函数均采用多层检测机制:
第一层检测:确认 Camera Manager 是否成功获取。如果获取失败,返回 13003(相机服务错误)。
第二层检测:调用 isTorchSupported() 检查设备硬件是否支持手电筒。不支持时返回 13001(设备不支持闪光灯)。
第三层检测:调用 isTorchModeSupported(mode) 检查目标模式是否被支持。虽然极少出现部分模式不支持的情况,但这一检测确保了代码的健壮性。
通过检测后,才调用 setTorchMode(mode) 执行实际操作。
使用 try-catch 捕获所有可能的异常。捕获到的异常被转换为 BusinessError 类型,通过检查 error.code 属性进行错误码映射:
- 鸿蒙原生码
7400102映射为插件码13002 - 鸿蒙原生码
7400201映射为插件码13003 - 其他未知错误统一映射为
13004
这种映射机制实现了平台错误到业务错误的转换,为开发者提供了更友好的错误信息。
标准的回调执行模式
操作成功时,构造 FlashlightSuccess 对象,依次执行 success 和 complete 回调。操作失败时,构造 FlashlightFailImpl 对象,依次执行 fail 和 complete 回调。所有回调执行前都会进行空值检查(options?.success?.(res)),确保代码的安全性。
这种回调模式完全遵循 uni-app 的异步 API 规范,让开发者能够以统一的方式处理成功和失败情况。
使用说明
安装配置
将 harmony-flashlight 插件文件夹复制到项目的 uni_modules 目录下。插件采用 uni_modules 规范,HBuilderX 会自动识别并完成依赖安装。
基础用法
在 Vue 组件中导入所需的函数:
import { turnOnFlashlight, turnOffFlashlight } from "@/uni_modules/harmony-flashlight"
打开闪光灯示例:
turnOnFlashlight({
success: (res) => {
console.log('打开成功', res.errMsg)
uni.showToast({ title: '手电筒已打开', icon: 'success' })
},
fail: (err) => {
console.error('打开失败', err.errCode, err.errMsg)
uni.showToast({ title: err.errMsg, icon: 'none' })
},
complete: () => {
console.log('操作完成')
}
})
关闭闪光灯示例:
turnOffFlashlight({
success: (res) => {
console.log('关闭成功', res.errMsg)
uni.showToast({ title: '手电筒已关闭', icon: 'success' })
},
fail: (err) => {
console.error('关闭失败', err.errCode, err.errMsg)
uni.showToast({ title: err.errMsg, icon: 'none' })
}
})
完整应用示例
以下是一个完整的手电筒开关组件实现:
<script setup lang="ts">
import { turnOnFlashlight, turnOffFlashlight } from "@/uni_modules/harmony-flashlight"
import { ref } from 'vue'
const isFlashlightOn = ref(false)
const toggleFlashlight = () => {
if (isFlashlightOn.value) {
turnOffFlashlight({
success: () => {
isFlashlightOn.value = false
uni.showToast({ title: '手电筒已关闭', icon: 'success' })
},
fail: (err) => {
uni.showToast({ title: err.errMsg, icon: 'none' })
}
})
} else {
turnOnFlashlight({
success: () => {
isFlashlightOn.value = true
uni.showToast({ title: '手电筒已打开', icon: 'success' })
},
fail: (err) => {
uni.showToast({ title: err.errMsg, icon: 'none' })
}
})
}
}
</script>
<template>
<view class="container">
<button @click="toggleFlashlight" class="flashlight-btn">
{{ isFlashlightOn ? '关闭手电筒' : '打开手电筒' }}
</button>
</view>
</template>
错误处理建议
在生产环境中,建议根据错误码进行差异化处理:
turnOnFlashlight({
fail: (err) => {
if (err.errCode === 13001) {
uni.showModal({
title: '提示',
content: '您的设备不支持闪光灯功能',
showCancel: false
})
} else if (err.errCode === 13002) {
uni.showModal({
title: '权限提示',
content: '需要相机权限才能使用闪光灯,请在系统设置中授权',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
// 跳转到系统设置页面
}
}
})
} else {
uni.showToast({ title: '操作失败:' + err.errMsg, icon: 'none' })
}
}
})
注意事项
-
权限处理:首次使用时,系统会弹出相机权限授权对话框,用户拒绝授权会导致功能无法使用。建议在适当时机向用户说明权限用途。
-
生命周期管理:应用切换到后台或退出时,闪光灯会自动关闭。如需保持闪光灯状态,建议在
onShow生命周期中恢复。
如果有问题可留言评论。
0 个评论
要回复文章请先登录或注册