《HarmonyOS 7 Flutter 三方插件鸿蒙化开发手记》04:权限、Ability 生命周期与异步回调为什么总是最后出问题【鸿蒙心迹】
第一次用插件完全正常,退到后台再回来,直接崩了。代码明明没改啊?

前面三篇插件已经能用了。但线上一跑就出问题:第一次用完全正常,退后台再回来,直接崩了。或者权限弹窗还没点完,页面已经关了。
代码明明没改,为什么会出问题?
这就是生命周期的问题。代码都对,时机错了。
一、先想清楚:对象什么时候出生,什么时候死亡
先把最基础的问题想明白。
插件里的对象:Context、Channel、监听器、异步任务,都有生命周期。什么时候创建?什么时候销毁?
| 阶段 | 对象状态 |
|---|---|
| Ability 创建 | 插件初始化,Channel 建立 |
| 页面运行 | 正常调用,回调处理 |
| 页面退后台 | 对象还在吗? |
| 页面销毁 | 对象销毁,资源释放 |
二、最经典的坑:Context 失效
很多人在插件初始化的时候,把 Context 缓存起来。然后在异步回调里用这个 Context。
问题是:用户退到后台,或者页面销毁了,Context 已经失效了。这时候异步回调回来,拿着一个已经死了的 Context 去调 API,直接崩。
| 操作 | 问题 |
|---|---|
| 缓存 Context | 页面销毁后 Context 失效 |
| 异步回调用旧 Context | 调用失败,崩 |
| 权限请求还没结束插件就销毁 | 回调找不到接收者 |
三、权限请求为什么会出问题
权限请求是异步的。用户点了同意/拒绝,才回调。
但这时候页面可能已经关了。插件可能已经 detach 了。回调回来,找不到接收者。
这段代码解决什么问题: 权限请求生命周期。
文件: ohos/src/main/ets/PermissionPlugin.ets
用途: 权限请求处理
接入位置: 插件实现
import { abilityAccessCtrl } from '@kit.AbilityKit';
class PermissionPlugin {
private context: UIAbilityContext;
async requestPermission(perm: string): Promise<boolean> {
// 检查 context 还有没有效
if (!this.context) {
throw new Error('Context already destroyed');
}
let atManager = abilityAccessCtrl.createAtManager();
let result = await atManager.requestPermissionsFromUser(
this.context, [perm]
);
// 回调回来,context 可能已经没了
return result.authResults[0] === 0;
}
dispose() {
// 页面销毁,清理 context
this.context = null;
}
}

四、Engine 重建后 Channel 为什么还在
Flutter Engine 重建的时候,插件会重新 attach。这时候如果还保留旧的 Channel 引用,就出问题了。
| 阶段 | Channel 状态 |
|---|---|
| 第一次 attach | Channel 建立 |
| Engine 重建 | 旧 Channel 失效 |
| 第二次 attach | 应该重新建立 Channel |
但很多人没处理第二次 attach,还在用旧 Channel。
五、几个容易踩的坑
第一个坑:缓存已经失效的 Context。页面销毁后 Context 失效,回调回来崩。
第二个坑:权限请求还没结束插件就被销毁。回调找不到接收者。
第三个坑:Engine 重建后保留旧 Channel。用的是失效的 Channel。
第四个坑:回调回来时页面已经不存在。UI 更新报错。
第五个坑:监听器重复注册。多次注册,多次回调。
第六个坑:插件 detach 后 Native 资源继续运行。资源没释放,泄漏。

这次做生命周期最大的体会是:代码都对,不代表没问题。时机错了,一样崩。
真正做的时候,最容易忽略的不是怎么写功能,而是怎么管好对象的生死。什么时候创建、什么时候销毁、什么时候失效,每一步都要想清楚。
更多推荐


所有评论(0)