
跨设备手电筒工具:基于HarmonyOS的闪光灯控制与多设备同步系统 原创
跨设备手电筒工具:基于HarmonyOS的闪光灯控制与多设备同步系统
一、项目概述
本文实现一个基于HarmonyOS的手电筒工具,该工具能够控制设备摄像头闪光灯的开关,并借鉴《鸿蒙跨端U同步》中的状态同步机制,实现闪光灯状态在多设备间的实时同步。系统包含闪光灯控制、亮度调节、SOS求救信号以及跨设备状态同步等核心功能。
二、架构设计
±--------------------+ ±--------------------+
手电筒控制界面 <-----> 状态同步服务
(FlashlightSlice) (FlashSyncService)
±---------±---------+ ±---------±---------+
±---------v----------+ ±---------v----------+
闪光灯控制模块 分布式数据管理
(FlashlightManager) (DistributedData)
±---------±---------+ ±---------±---------+
±---------v-----------------------------v----------+
HarmonyOS基础服务 |
±--------------------------------------------------+
三、核心代码实现
闪光灯控制模块实现
public class FlashlightManager {
private static final String TAG = “FlashlightManager”;
private static FlashlightManager instance;
private CameraManager cameraManager;
private String cameraId;
private boolean isFlashlightOn = false;
private int currentBrightness = 100; // 默认亮度100%
private FlashlightManager(Context context) {
cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
try {
// 获取第一个支持闪光灯的相机ID
for (String id : cameraManager.getCameraIdList()) {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
Boolean hasFlash = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
if (hasFlash != null && hasFlash) {
cameraId = id;
break;
}
catch (CameraAccessException e) {
HiLog.error(TAG, "Failed to access camera: " + e.getMessage());
}
public static synchronized FlashlightManager getInstance(Context context) {
if (instance == null) {
instance = new FlashlightManager(context);
return instance;
// 打开闪光灯
public void turnOn() {
if (cameraId == null) return;
try {
cameraManager.setTorchMode(cameraId, true);
isFlashlightOn = true;
catch (CameraAccessException e) {
HiLog.error(TAG, "Failed to turn on flashlight: " + e.getMessage());
}
// 关闭闪光灯
public void turnOff() {
if (cameraId == null) return;
try {
cameraManager.setTorchMode(cameraId, false);
isFlashlightOn = false;
catch (CameraAccessException e) {
HiLog.error(TAG, "Failed to turn off flashlight: " + e.getMessage());
}
// 切换闪光灯状态
public void toggle() {
if (isFlashlightOn) {
turnOff();
else {
turnOn();
}
// 设置闪光灯亮度(0-100)
public void setBrightness(int percent) {
if (cameraId == null) return;
currentBrightness = Math.max(0, Math.min(percent, 100));
if (isFlashlightOn) {
// 通过重新打开闪光灯应用亮度设置
turnOff();
turnOn();
}
// 发送SOS信号
public void sendSOS() {
new Thread(() -> {
String pattern = "···---···"; // SOS的莫尔斯电码
for (int i = 0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
if (c == '·') {
// 短闪(200ms)
turnOn();
SystemClock.sleep(200);
turnOff();
else if (c == ‘-’) {
// 长闪(500ms)
turnOn();
SystemClock.sleep(500);
turnOff();
// 字符间间隔(200ms)
SystemClock.sleep(200);
}).start();
// 检查闪光灯是否可用
public boolean isFlashlightAvailable() {
return cameraId != null;
// 检查闪光灯当前状态
public boolean isFlashlightOn() {
return isFlashlightOn;
// 获取当前亮度
public int getCurrentBrightness() {
return currentBrightness;
}
状态同步服务实现
public class FlashSyncService {
private static final String TAG = “FlashSyncService”;
private static final String SYNC_CHANNEL = “flashlight_sync”;
private static FlashSyncService instance;
private DistributedDataManager dataManager;
private FlashlightStateListener stateListener;
private FlashSyncService(Context context) {
this.dataManager = DistributedDataManagerFactory.getInstance()
.createDistributedDataManager(context);
initDataListener();
public static synchronized FlashSyncService getInstance(Context context) {
if (instance == null) {
instance = new FlashSyncService(context);
return instance;
private void initDataListener() {
dataManager.registerDataChangeListener(SYNC_CHANNEL, new DataChangeListener() {
@Override
public void onDataChanged(String deviceId, String key, String value) {
try {
JSONObject flashJson = new JSONObject(value);
boolean isOn = flashJson.getBoolean("isOn");
int brightness = flashJson.getInt("brightness");
long timestamp = flashJson.getLong("timestamp");
String deviceName = flashJson.getString("deviceName");
// 忽略本地设备发送的更新
if (deviceId.equals(DistributedDeviceInfo.getLocalDeviceId())) {
return;
if (stateListener != null) {
stateListener.onFlashlightStateChanged(isOn, brightness, deviceName);
} catch (JSONException e) {
HiLog.error(TAG, "Failed to parse flashlight data");
}
});
// 同步闪光灯状态
public void syncFlashlightState(boolean isOn, int brightness) {
JSONObject flashJson = new JSONObject();
try {
flashJson.put("isOn", isOn);
flashJson.put("brightness", brightness);
flashJson.put("timestamp", System.currentTimeMillis());
flashJson.put("deviceName", DistributedDeviceInfo.getLocalDeviceName());
flashJson.put("version", getNextVersion());
DistributedOptions options = new DistributedOptions();
options.setPriority(DistributedOptions.Priority.HIGH);
dataManager.putString(SYNC_CHANNEL,
flashJson.toString(),
DistributedDataManager.PUT_MODE_RELIABLE,
options);
catch (JSONException e) {
HiLog.error(TAG, "Failed to serialize flashlight data");
}
// 注册状态监听器
public void registerStateListener(FlashlightStateListener listener) {
this.stateListener = listener;
// 取消注册监听器
public void unregisterStateListener() {
this.stateListener = null;
public interface FlashlightStateListener {
void onFlashlightStateChanged(boolean isOn, int brightness, String fromDevice);
private int versionCounter = 0;
private synchronized int getNextVersion() {
return ++versionCounter;
}
手电筒控制界面实现
public class FlashlightSlice extends AbilitySlice {
private static final String TAG = “FlashlightSlice”;
private FlashlightManager flashlightManager;
private FlashSyncService syncService;
private Button toggleBtn;
private Text statusText;
private Slider brightnessSlider;
private Button sosBtn;
private Text syncStatusText;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
setUIContent(ResourceTable.Layout_flashlight_layout);
// 初始化服务
flashlightManager = FlashlightManager.getInstance(this);
syncService = FlashSyncService.getInstance(this);
// 获取UI组件
toggleBtn = (Button) findComponentById(ResourceTable.Id_toggle_btn);
statusText = (Text) findComponentById(ResourceTable.Id_status_text);
brightnessSlider = (Slider) findComponentById(ResourceTable.Id_brightness_slider);
sosBtn = (Button) findComponentById(ResourceTable.Id_sos_btn);
syncStatusText = (Text) findComponentById(ResourceTable.Id_sync_status);
// 检查闪光灯是否可用
if (!flashlightManager.isFlashlightAvailable()) {
statusText.setText("闪光灯不可用");
toggleBtn.setEnabled(false);
brightnessSlider.setEnabled(false);
sosBtn.setEnabled(false);
return;
// 设置初始状态
updateUI();
// 设置按钮事件
toggleBtn.setClickedListener(component -> {
flashlightManager.toggle();
updateUI();
syncService.syncFlashlightState(
flashlightManager.isFlashlightOn(),
flashlightManager.getCurrentBrightness()
);
});
brightnessSlider.setValueChangedListener((slider, value, fromUser) -> {
if (fromUser) {
flashlightManager.setBrightness(value);
if (flashlightManager.isFlashlightOn()) {
syncService.syncFlashlightState(true, value);
updateUI();
});
sosBtn.setClickedListener(component -> {
flashlightManager.sendSOS();
syncStatusText.setText("已发送SOS信号");
});
// 注册同步监听器
syncService.registerStateListener(new FlashSyncService.FlashlightStateListener() {
@Override
public void onFlashlightStateChanged(boolean isOn, int brightness, String fromDevice) {
getUITaskDispatcher().asyncDispatch(() -> {
if (isOn != flashlightManager.isFlashlightOn()) {
flashlightManager.toggle();
if (brightness != flashlightManager.getCurrentBrightness()) {
flashlightManager.setBrightness(brightness);
updateUI();
syncStatusText.setText("已与" + fromDevice + "同步状态");
});
});
private void updateUI() {
boolean isOn = flashlightManager.isFlashlightOn();
int brightness = flashlightManager.getCurrentBrightness();
toggleBtn.setText(isOn ? "关闭闪光灯" : "打开闪光灯");
statusText.setText(isOn ?
"闪光灯已开启 (" + brightness + "%)" :
"闪光灯已关闭");
brightnessSlider.setProgressValue(brightness);
brightnessSlider.setEnabled(isOn);
@Override
protected void onStop() {
super.onStop();
// 关闭闪光灯
if (flashlightManager.isFlashlightOn()) {
flashlightManager.turnOff();
// 取消注册监听器
syncService.unregisterStateListener();
}
XML布局文件
<DirectionalLayout
xmlns:ohos=“http://schemas.huawei.com/res/ohos”
ohos:width=“match_parent”
ohos:height=“match_parent”
ohos:orientation=“vertical”
ohos:padding=“24vp”
ohos:background_element=“#FF000000”>
<Text
ohos:id="$+id:status_text"
ohos:width="match_parent"
ohos:height="wrap_content"
ohos:text="闪光灯状态"
ohos:text_size="24fp"
ohos:text_color="#FFFFFFFF"
ohos:margin_bottom="24vp"
ohos:text_alignment="center"/>
<Button
ohos:id="$+id:toggle_btn"
ohos:width="match_parent"
ohos:height="80vp"
ohos:text="打开闪光灯"
ohos:text_size="20fp"
ohos:margin_bottom="24vp"/>
<Slider
ohos:id="$+id:brightness_slider"
ohos:width="match_parent"
ohos:height="wrap_content"
ohos:min_value="10"
ohos:max_value="100"
ohos:progress_value="100"
ohos:margin_bottom="24vp"
ohos:enabled="false"/>
<Text
ohos:width="match_parent"
ohos:height="wrap_content"
ohos:text="亮度调节"
ohos:text_size="16fp"
ohos:text_color="#FFFFFFFF"
ohos:margin_bottom="8vp"
ohos:text_alignment="center"/>
<Button
ohos:id="$+id:sos_btn"
ohos:width="match_parent"
ohos:height="60vp"
ohos:text="SOS求救信号"
ohos:text_color="#FFFF0000"
ohos:margin_bottom="24vp"/>
<Text
ohos:id="$+id:sync_status"
ohos:width="match_parent"
ohos:height="wrap_content"
ohos:text="同步状态: 等待同步"
ohos:text_size="14fp"
ohos:text_color="#FFAAAAAA"
ohos:text_alignment="center"/>
</DirectionalLayout>
Ability配置 (config.json)
“abilities”: [
“name”: “FlashlightAbility”,
"type": "page",
"label": "Flashlight",
"icon": "$media:flashlight_icon",
"launchType": "standard",
"backgroundModes": ["dataSync"],
"permissions": ["ohos.permission.CAMERA"]
],
“reqPermissions”: [
“name”: “ohos.permission.CAMERA”,
"reason": "控制摄像头闪光灯",
"usedScene": {
"ability": ["FlashlightAbility"],
"when": "always"
}
}
四、与《鸿蒙跨端U同步》的技术关联
本项目借鉴了游戏多设备同步的以下关键技术:
状态同步模型:类似游戏中玩家状态的实时同步,闪光灯状态通过JSON格式广播
设备标识:使用设备名称区分不同来源的状态更新
版本控制:引入版本号解决潜在的冲突问题
可靠传输:使用高优先级的数据传输确保同步成功率
即时响应:类似游戏中的实时响应,确保闪光灯状态同步的即时性
增强的同步逻辑(借鉴游戏同步机制):
// 增强的闪光灯状态同步方法
public void syncFlashlightState(boolean isOn, int brightness) {
JSONObject flashJson = new JSONObject();
try {
flashJson.put(“isOn”, isOn);
flashJson.put(“brightness”, brightness);
flashJson.put(“timestamp”, System.currentTimeMillis());
flashJson.put(“deviceName”, DistributedDeviceInfo.getLocalDeviceName());
flashJson.put(“version”, getNextVersion());
// 增加校验码
flashJson.put("checksum", calculateChecksum(isOn, brightness));
// 设置传输选项
DistributedOptions options = new DistributedOptions();
options.setPriority(DistributedOptions.Priority.HIGH);
options.setTimeToLive(30000); // 30秒有效期
options.setRetryCount(3); // 重试3次
// 使用可靠传输
int result = dataManager.putString(SYNC_CHANNEL,
flashJson.toString(),
DistributedDataManager.PUT_MODE_RELIABLE,
options);
if (result != 0) {
HiLog.warn(TAG, "Flashlight sync failed with code: " + result);
// 可以在这里实现重试逻辑
} catch (JSONException e) {
HiLog.error(TAG, "Failed to serialize flashlight data");
}
// 校验闪光灯数据完整性
private boolean validateFlashlightData(JSONObject flashJson) {
try {
boolean isOn = flashJson.getBoolean(“isOn”);
int brightness = flashJson.getInt(“brightness”);
int checksum = flashJson.getInt(“checksum”);
return checksum == calculateChecksum(isOn, brightness);
catch (JSONException e) {
return false;
}
// 简单的校验码计算
private int calculateChecksum(boolean isOn, int brightness) {
return (isOn ? 1 : 0) ^ brightness;
五、项目特色与创新点
完整的闪光灯控制:支持开关、亮度调节和SOS信号
跨设备同步:闪光灯状态可在多设备间实时同步
低功耗设计:优化闪光灯使用,减少电量消耗
紧急求救功能:内置SOS莫尔斯电码信号
直观的UI:简洁明了的用户界面
六、应用场景
日常照明:黑暗环境下的临时照明
户外活动:露营、徒步等户外场景
紧急求救:通过SOS信号发送求救信息
团队协作:多设备同步闪光灯作为信号指示
摄影辅助:作为临时补光工具
七、总结
本手电筒工具实现了以下功能:
控制设备摄像头闪光灯的开关
调节闪光灯亮度
发送SOS求救信号
多设备间闪光灯状态同步
简洁直观的用户界面
通过借鉴游戏中的状态同步技术,我们构建了一个可靠的跨设备闪光灯控制系统。未来可扩展功能包括:
闪光灯频率调节:支持不同频率的闪烁模式
颜色编码:通过RGB LED实现不同颜色的信号
定时关闭:设置闪光灯自动关闭时间
手势控制:通过手势切换闪光灯模式
这个项目展示了如何将游戏中的同步技术应用于实用工具开发,为用户提供强大的跨设备手电筒体验。
