第一次用插件完全正常,退到后台再回来,直接崩了。代码明明没改啊?

文章封面

前面三篇插件已经能用了。但线上一跑就出问题:第一次用完全正常,退后台再回来,直接崩了。或者权限弹窗还没点完,页面已经关了。

代码明明没改,为什么会出问题?

这就是生命周期的问题。代码都对,时机错了。

一、先想清楚:对象什么时候出生,什么时候死亡

先把最基础的问题想明白。

插件里的对象: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 状态
第一次 attachChannel 建立
Engine 重建旧 Channel 失效
第二次 attach应该重新建立 Channel

但很多人没处理第二次 attach,还在用旧 Channel。

五、几个容易踩的坑

第一个坑:缓存已经失效的 Context。页面销毁后 Context 失效,回调回来崩。

第二个坑:权限请求还没结束插件就被销毁。回调找不到接收者。

第三个坑:Engine 重建后保留旧 Channel。用的是失效的 Channel。

第四个坑:回调回来时页面已经不存在。UI 更新报错。

第五个坑:监听器重复注册。多次注册,多次回调。

第六个坑:插件 detach 后 Native 资源继续运行。资源没释放,泄漏。

测试效果图

这次做生命周期最大的体会是:代码都对,不代表没问题。时机错了,一样崩。

真正做的时候,最容易忽略的不是怎么写功能,而是怎么管好对象的生死。什么时候创建、什么时候销毁、什么时候失效,每一步都要想清楚。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐