鸿蒙Cordova开发踩坑记录:物理返回键的“劫持“

摘要:Android/鸿蒙用户有一个根深蒂固的习惯:按侧边/底部返回键回退页面。但在单页应用(SPA)中,如果不做特殊处理,按一次返回键就会直接关闭应用。本文介绍了如何"劫持"物理返回事件,实现"页面内回退"与"双击退出"的逻辑。

🔙 1. 用户的困惑

Web App 的路由通常是虚拟的(Hash 路由或 History API)。
当用户在游戏中进入了"设置"页面,按下手机侧边的返回手势时,他期望的是回到"游戏主页"。
但实际发生的是:应用直接退到了桌面

这是因为 ArkWeb 默认不消费系统的 Back 事件,除非我们显式告诉它。

🔗 2. 拦截链条

我们需要构建一个拦截链:
System Back Event -> ArkUI -> ArkWeb -> Check History -> Go Back OR Exit App.

2.1 启用 Web 访问历史

首先,确保 Web 组件允许访问浏览记录。

Web({ src: this.url, controller: this.controller })
  // 这一步很重要,虽然默认通常是开启的
  .domStorageAccess(true)

2.2 接管系统返回 (ArkTS)

EntryAbility.ets 或主页面中,重写 onBackPressed 接口。

// Index.ets
@Entry
@Component
struct Index {
  @State webController: WebviewController = new web_webview.WebviewController();

  // 关键:拦截系统返回键
  onBackPress() {
    // 1. 检查 Web 是否可以回退
    if (this.webController.accessBackward()) {
      this.webController.backward();
      return true; // 消费事件,不再传递给系统(即不退出应用)
    }

    // 2. 如果 Web 不能回退(在首页),执行"双击退出"逻辑
    if (this.shouldExit()) {
      return false; // 返回 false,交给系统处理(退出应用)
    } else {
      this.showToast("再按一次退出应用");
      this.lastBackTime = Date.now();
      return true; // 消费事件,暂不退出
    }
  }
  
  private lastBackTime: number = 0;
  
  private shouldExit(): boolean {
    return Date.now() - this.lastBackTime < 2000;
  }
}

🎭 3. 进阶:SPA 的特殊处理

对于 Vue/React 这种 SPA,webController.accessBackward() 往往不准确,因为 URL 可能没变(Hash 模式)或者 History Stack 被手动修改了。

更精准的做法是将返回逻辑下放给 JS 处理。

3.1 JS 监听器

Web 端提供一个 onBackPressed 钩子。

// 注册到全局
window.onNativeBack = function() {
    // 检查是否有弹窗打开
    if (document.querySelector('.modal.visible')) {
        closeModal();
        return true; // 已处理
    }
    
    // 检查路由
    if (window.location.hash !== '#/home') {
        window.history.back();
        return true; // 已处理
    }
    
    return false; // 没法处理,请 Native 退出应用
};

3.2 Native 调用

修改 onBackPress

onBackPress() {
    // 同步调用 JS 不太现实,通常只能异步
    // 但 onBackPress 需要立即返回 boolean
    // 这是一个经典的死锁问题
}

死锁破解
由于 onBackPress 必须同步返回结果,而 runJavaScript 是异步的。我们通常采用**“总是拦截”**策略。

  1. Native onBackPress 永远返回 true(拦截)。
  2. Native 发送事件给 JS:window.dispatchEvent(new Event('hardwareback'))
  3. JS 判断:
    • 如果能回退,JS 自己处理。
    • 如果不能回退(在首页),JS 调用 Native 的 app.exit() 接口。

这种"JS 主控"模式虽然复杂,但灵活性最高,能处理诸如"关闭弹窗"、"取消选中"等细粒度逻辑。

📝 4. 总结

对于简单的 H5 应用:
使用 ArkTS accessBackward() 检查 即可满足 90% 的需求。

对于复杂的 SPA 应用:
推荐 “JS 主控模式”,Native 只负责传递按键信号,由前端路由(Vue Router / React Router)来决定是后退路由、关闭 Modal 还是真正退出 App。这才是 Hybrid App 的最佳实践。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

淼学派对

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值