收藏
0
0
分享
举报
·2026/7/13 04:32发布·43次阅读

我的应用在生产环境中启动即崩溃。我花了几个小时检查 9000 行

独立开发商业增长付费用户

我最近把自己独立开发的一款汇款费用比较应用发布到了 Google Play。

这是一个使用 React Native 和 Expo 开发的项目。
我没有专业编程背景,整个产品基本由我一个人完成。

应用正式上线后,我满怀期待地打开生产版本,准备庆祝一下。

结果却是:

点击图标 → 出现启动画面 → 应用立即关闭。

每一次都是这样。

没有错误提示,没有红色报错页面,也没有任何机会进入应用首页。

我第一反应当然是:

一定是我的 App.js 出问题了。

因为我的 App.js 已经接近 9000 行。

我知道这不是一个值得推荐的项目结构,但作为一个没有编程背景、边做边学的独立开发者,我不断增加功能,最后一个文件几乎变成了整个产品。

于是,我开始逐行排查。


第一个怀疑对象:AdMob Banner 初始化

应用崩溃发生在我加入 AdMob 底部横幅广告之后,所以我首先怀疑广告初始化代码。

我把初始化放进 useEffect,并加入错误捕获:

useEffect(() => {
  MobileAds()
    .initialize()
    .catch((error) => {
      console.log('MobileAds initialization failed:', error);
    });
}, []);

理论上,即使广告初始化失败,应用本身也不应该退出。

但重新构建后,应用仍然在启动时立即崩溃。


第二个怀疑对象:变量定义顺序

继续检查代码时,我发现一个叫 CRYPTO_CODES 的常量,在文件中第一次出现的位置,比它实际声明的位置早了大约 546 行。

我当时以为终于找到了原因:

ReferenceError:
Cannot access 'CRYPTO_CODES' before initialization

因为 JavaScript 中使用 const 声明的变量,在初始化前访问会触发 Temporal Dead Zone 错误。

而且如果这个错误发生在模块最上层,应用确实可能在加载时直接崩溃。

但仔细检查后发现,那次引用位于一个函数内部。

函数实际执行时,CRYPTO_CODES 早已完成初始化。

所以这只是一次误报。


我甚至写了一个 Node 测试环境

到这个阶段,我已经不想继续靠猜测了。

我写了一个 Node 测试环境,模拟 React Native 应用在生产模式下加载:

__DEV__ = false

为了运行这个测试,我需要模拟和替换:

React Native

Expo

Supabase

原生模块

图片资源

所有 require('./assets/...')

AdMob 相关模块

我的目标只有一个:

当生产版本加载 App.js 顶层代码时,它到底会不会崩溃?

测试结果是:

JavaScript 语法正常

模块顶层代码正常加载

没有变量初始化顺序错误

App 组件第一次渲染正常

生产模式下也没有出现 JavaScript 异常

也就是说,我那个看起来非常危险的 9000 行 App.js,至少从程序执行角度来看,并不是这次启动崩溃的直接原因。


真正的线索来自 adb logcat

真正改变调查方向的是 Android 崩溃日志。

日志中出现了:

Invalid application ID
MobileAdsInitProvider.attachInfo

这说明错误发生在 Google Mobile Ads SDK 的原生初始化阶段。

而这个阶段发生在 React Native 的 JavaScript 开始执行之前。

所以我写在 JavaScript 中的:

MobileAds()
  .initialize()
  .catch(...)

根本没有机会运行。

JavaScript 的 try/catch 或 Promise .catch(),只能捕获 JavaScript Runtime 启动之后发生的错误。

如果 Android 在加载原生 Provider 时就已经崩溃,JavaScript 层无法拦截它。


问题不在 App.js,而在 app.json

最终的问题出现在 app.json 生成的 Android 原生 AdMob 配置中。

生产版本的 AndroidManifest 没有正确包含有效的 AdMob App ID,导致 Google Mobile Ads SDK 在应用启动阶段直接失败。

正确的 Expo 插件配置应该类似这样:

{
  "plugins": [
    [
      "react-native-google-mobile-ads",
      {
        "androidAppId": "ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy"
      }
    ]
  ]
}

这里还有一个非常容易混淆的地方。

AdMob 的 App ID 和 Banner Ad Unit ID 格式不同:

App ID:
ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy

Banner Ad Unit ID:
ca-app-pub-xxxxxxxxxxxxxxxx/zzzzzzzzzz

App ID 使用 ~

广告单元 ID 使用 /

如果把两者混用,或者 App ID 没有正确写入 AndroidManifest,应用可能构建成功、上传成功,但在用户打开时立即崩溃。

我在整理配置时,也明确加入了 Android 广告 ID 权限:

{
  "android": {
    "permissions": [
      "com.google.android.gms.permission.AD_ID"
    ]
  }
}

不过需要说明的是,这个权限本身并不是此次 Invalid application ID 崩溃的直接原因。

真正导致启动崩溃的是:

有效的 AdMob App ID 没有正确反映到最终生成的 Android 原生配置中。


这次学到的几个教训

1. 启动后立即崩溃,不一定是 JavaScript 问题

如果应用连第一个页面都没显示就直接退出,应该优先检查:

AndroidManifest

原生 SDK 初始化

Expo Plugin 配置

原生模块版本

Release Build 配置

不要像我一样,一开始就盯着几千行 React 组件逐行排查。

2. adb logcat 比猜测有效得多

只要一条正确的原生崩溃堆栈,就可能节省几个小时。

这次如果我一开始就看到:

MobileAdsInitProvider
Invalid application ID

我根本不会花那么长时间检查 App.js。

3. 构建成功不等于应用可以正常运行

AAB 可以:

成功构建

成功上传 Google Play

成功通过部分检查

最终仍然在用户设备上启动即崩溃

构建系统只能证明文件生成成功,不能证明所有原生配置都有效。

4. JavaScript 无法捕获所有错误

React Native 开发中,应用虽然主要使用 JavaScript 编写,但依然依赖很多原生模块。

如果错误发生在 JavaScript Runtime 启动之前,JavaScript 中再完善的错误处理也没有作用。

5. 最大的文件不一定是最大的嫌疑人

我一直认为,9000 行的 App.js 一定是问题源头。

结果真正的问题,是 App.js 外面的一个原生配置项。

有时候,错误的大小和你排查它所花费的时间完全不成比例。


我现在正在重新构建并发布修复后的版本。

希望下一张截图,是应用终于正常打开,而不是另一份崩溃日志。

这款应用叫 TransferIQ。它用于比较外汇、跨境汇款和加密货币兑换路线。感兴趣的话,可以在 Google Play 商店搜索 TransferIQ。

你们有没有遇到过这种情况:

花了几个小时检查错误的文件,最后发现真正的问题在完全不同的地方?

讨论 (0)

    目录

    关于作者

    推广

    这家伙很懒,什么都没留下

    Solo 独立开发者社区support@solo.xin
    关于社区隐私政策用户协议商务合作友情链接订阅更新(RSS)投稿赞助

    © 2023 SOLO · 为独立开发者而生