《HarmonyOS 7 精准碰一碰跨设备协作开发实战》07:异常恢复、状态机与CrossDrop工程收尾【鸿蒙心迹】
前六篇我们一路做下来,功能都有了。但真用起来呢?传到一半App退了怎么办?窗口关了怎么办?URI失效了怎么办?这一篇把这些问题收口。

写到这里,CrossDrop已经有了:精准分享、触点识别、区域路由、多窗口、多文件、业务指令、多设备并发。
看起来挺完整。
但我自己测的时候,发现一堆问题:
传到一半,用户直接把App划掉了。
窗口关了,任务还在后台跑。
文件URI过期了,打开失败。
用户碰了两次,同一个任务传了两遍。
切到后台再回来,之前的任务状态全丢了。
这些问题不解决,Demo就是Demo,真用起来全是bug。
这一篇做工程收口。
一、TransferStateMachine:统一状态机
前面我们零散地定义了一些状态。这一篇统一起来。
一个任务从开始到结束,要经历这些状态:
IDLE → RECEIVED → VALIDATING → PROCESSING → COMPLETED
中间任何一步失败,都可能进入:
FAILED → RETRYING
用户取消,进入:
CANCELLED
关键是:状态迁移要合法。不能从COMPLETED再跳回PROCESSING,不能从IDLE直接跳到COMPLETED。
这段代码解决什么问题: 任务状态机。
文件: state/TransferStateMachine.ets
用途: 统一管理任务状态迁移
接入位置: 每个TransferTask持有
enum TransferState {
IDLE = 'idle',
RECEIVED = 'received', // 已接收
VALIDATING = 'validating', // 校验中
PROCESSING = 'processing', // 处理中
COMPLETED = 'completed', // 完成
FAILED = 'failed', // 失败
RETRYING = 'retrying', // 重试中
CANCELLED = 'cancelled' // 已取消
}
class TransferStateMachine {
private state: TransferState = TransferState.IDLE;
// 合法状态迁移表
private validTransitions: Record<TransferState, TransferState[]> = {
[TransferState.IDLE]: [TransferState.RECEIVED, TransferState.CANCELLED],
[TransferState.RECEIVED]: [TransferState.VALIDATING, TransferState.CANCELLED],
[TransferState.VALIDATING]: [TransferState.PROCESSING, TransferState.FAILED, TransferState.CANCELLED],
[TransferState.PROCESSING]: [TransferState.COMPLETED, TransferState.FAILED, TransferState.CANCELLED],
[TransferState.FAILED]: [TransferState.RETRYING, TransferState.CANCELLED],
[TransferState.RETRYING]: [TransferState.VALIDATING, TransferState.CANCELLED],
[TransferState.COMPLETED]: [],
[TransferState.CANCELLED]: []
};
canTransitionTo(newState: TransferState): boolean {
return this.validTransitions[this.state].includes(newState);
}
transitionTo(newState: TransferState): boolean {
if (this.canTransitionTo(newState)) {
this.state = newState;
return true;
}
console.warn('非法状态迁移:' + this.state + ' → ' + newState);
return false;
}
getState(): TransferState {
return this.state;
}
}
二、任务持久化:App退了也能恢复
最大的问题是:App退了,任务状态全丢了。
解决办法:把任务状态持久化到本地。
每次状态变化,都存到RDB或Preferences里。App重新启动时,读出来,恢复未完成的任务。
这段代码解决什么问题: 任务持久化。
文件: persist/TaskRepository.ets
用途: 任务状态本地持久化
接入位置: 任务状态变化时调用
class TaskRepository {
// 保存任务状态
async saveTask(task: TransferTask) {
let store = await relationalStore.getRdbStore(...);
await store.executeSql(
'INSERT OR REPLACE INTO tasks (id, uri, state, progress, retry_count) VALUES (?, ?, ?, ?, ?)',
[task.id, task.uri, task.stateMachine.getState(), task.progress, task.retryCount]
);
}
// 加载所有未完成任务
async loadPendingTasks(): Promise<TransferTask[]> {
let store = await relationalStore.getRdbStore(...);
let result = await store.querySql('SELECT * FROM tasks WHERE state NOT IN ("completed", "cancelled")');
let tasks: TransferTask[] = [];
while (result.goToNextRow()) {
let task = new TransferTask(result.getString(0), result.getString(1));
task.progress = result.getDouble(3);
task.retryCount = result.getLong(4);
tasks.push(task);
}
return tasks;
}
// App重启后恢复
async restoreOnStartup() {
let pending = await this.loadPendingTasks();
console.info('恢复未完成任务:' + pending.length + '个');
for (let task of pending) {
// 重新加入队列继续处理
taskQueue.addTask(task);
}
}
}
三、资源释放:该清的都清掉
最后一个问题:资源泄漏。
Listener没注销、Session没关闭、Buffer没释放——这些都会慢慢耗内存。
App退出、窗口关闭、任务完成时,都要清理:
- 注销所有状态监听
- 关闭所有DeviceSession
- 释放未使用的Buffer
- 取消还在跑的异步任务
四、整个连载回顾
七篇下来,我们做了一条完整链路:
01 能传——搭好CrossDrop工程骨架,实现第一条完整链路。
02 知道碰哪里——屏幕坐标到应用坐标的转换。
03 知道碰哪个窗口——多窗口识别与路由。
04 能传很多——多文件任务队列,并发控制。
05 能传业务指令——传输层和业务层解耦。
06 能同时传多设备——每台设备独立Session。
07 能稳定运行——状态机、持久化、资源释放。
从"碰一下传文件"到"碰哪传哪、多窗口、多文件、多设备、稳定可靠",这就是完整的跨设备协作工作台。


最终总结:
做跨设备分享,看起来就是"传个文件"这么简单。真做下来,从坐标转换、窗口识别、任务队列、业务协议、多设备并发,到最后状态机和持久化,每一层都有工程问题。
这也是做系统级能力的乐趣:不是调个API就完了,而是要把传输、路由、状态、资源整个链路想清楚。
更多推荐



所有评论(0)