惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
G
Google Developers Blog
J
Java Code Geeks
The GitHub Blog
The GitHub Blog
F
Full Disclosure
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Vercel News
Vercel News
酷 壳 – CoolShell
酷 壳 – CoolShell
Recent Announcements
Recent Announcements
Help Net Security
Help Net Security
The Hacker News
The Hacker News
IT之家
IT之家
Y
Y Combinator Blog
Martin Fowler
Martin Fowler
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
V
Visual Studio Blog
博客园 - 聂微东
Hacker News: Ask HN
Hacker News: Ask HN
H
Hacker News: Front Page
Know Your Adversary
Know Your Adversary
Security Latest
Security Latest
Security Archives - TechRepublic
Security Archives - TechRepublic
Simon Willison's Weblog
Simon Willison's Weblog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Troy Hunt's Blog
Last Week in AI
Last Week in AI
Schneier on Security
Schneier on Security
N
News and Events Feed by Topic
博客园 - 【当耐特】
有赞技术团队
有赞技术团队
AWS News Blog
AWS News Blog
Blog — PlanetScale
Blog — PlanetScale
博客园_首页
Google DeepMind News
Google DeepMind News
Cloudbric
Cloudbric
N
News | PayPal Newsroom
A
About on SuperTechFans
S
Schneier on Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Hugging Face - Blog
Hugging Face - Blog
M
MIT News - Artificial intelligence
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
雷峰网
雷峰网
T
The Exploit Database - CXSecurity.com
罗磊的独立博客
K
Kaspersky official blog
The Cloudflare Blog
I
Intezer

吐槽大王部落格

在 X (Twitter) 上找 IT 工作靠谱吗? 如何在上游分支被反复重写时优雅地 Rebase 我的 2025 我在经营自己的社交帐号,但可能「玩脱了」 我依然没有学会怎么在网上提出技术问题 我是怎么差点被恶意 npm 包攻击的 我对 Vue Router「动态路由」的一点暴论 【更正版】简单实现类型安全的、能触发 CustomEvent 的 EventTarget 在 IE11 使用 CSS Grid 实现多列卡片列表布局 自建云游戏服务的尝试(2024 更新) 在 Termux 编译和使用 bwm-ng(需要 root) 如何在已有的 Vue CLI 项目使用 esbuild 我的 2021 npm、镜像源与 package-lock.json 新的桌面工作站:HP Z2 G5 Tower 我的 2020 在 Intel 版 MacBook Pro 以 EFI 的形式安装 Windows 10 为什么大家都在安利这一样东西? 新的家庭服务器:MicroServer Gen10 我的 2019 在树莓派(Raspbian)上安装最新稳定版 nginx 驾照 宿舍用小型 UPS 电源与新台灯 拆解我爸 2010 年装的廉价 PC 我的 2018 2018 厦门面基记 我(曾经)设想过的外置智能大脑 没用的无懈可击 tcdw 与 ECMAScript 评论框 如何提出计算机相关问题(更新) 提升古诗词背诵及脸部记忆能力 一袋凉皮引发的『血案』 我写的一言轮播脚本(2018 更新) 土豆评论 我的 2017 我与 Linux 把 WSL 的发行版换成 Debian 时撞的坑 为什么公共 block 列表有时是个很糟的主意 故事:试图不关闭 SIP 在 macOS Sierra 上使用 proxychains-ng Xperia XZ Premium 在 DOSBox 使用 MIDI (MT-32、GM) 音源 昔日的折腾党 Android 手机间的文件传输 胡扯:为什么需要 https 黑历史:在 Flash 里手撕 HTML 与 JSON 从你的 Typecho 博客优雅的导出 Markdown 文件 如何维修电脑 Orange Pi One:百元家庭服务器的软件部分 关于香橙派 Orange Pi One 的碎碎念 在 FC 游戏实现内购 友情链接清理 夏天来了 如何给 tcdw 套上女装 选择 vps-hosting.ca 的廉价 VPS 是个糟糕的主意 Disqus + PJAX 后的小错误修复 域名是建站第一要素 网站搬家喽 重低音 Google Adsense FIR Filter Fader 移植 CMS 程序模板的一般方法 切换到 Ghost 关于以前用的昵称的正式说明 通过甲 JS 加载乙 JS 避免模板烂大街的最佳方法就是自己写一个 强行修改 Android 桌面小部件大小的方法,需 root tcdw 那糟糕的宾馆之旅 用 Flash 实现读取 QQ 空间背景音乐列表 屠城 QZone 播放器 0.1 正式版(求围观)
不用 eas-cli 编译 React Native (Expo) 应用的 Android 版本
tcdw · 2024-08-30 · via 吐槽大王部落格

为什么不用 eas-cli

  • 它需要你注册 Expo 帐号,并且建立一个新的 Project 在上面
  • 它每次运行都要连接 Expo 的云服务
  • 它不让你在 Windows 上跑。气抖冷!!

(当然如果以上方面对你不是问题的话,eas 其实挺适合懒人在自己的电脑/基础设施上跑的,特别是你不在内地的电脑上进行开发时)

需求

  • 配好 JDK 和 Android SDK,并且指定好它们的 PATHJAVA_HOMEANDROID_HOME详见
  • 项目启用 Prebuild

配置 Keystore

为了让编译出来的应用使用我们自己的 Keystore,我们需要修改 android/app/build.gradle 文件,在 signingConfigs 加入自己的 Keystore 信息,并且在 buildTypesrelease 中设置 signingConfig signingConfigs.release

由于我们的项目使用 Expo Managed Workflow,为了确保 Native 部分不会出现不同步的问题,这里通过编写项目级插件的方式实现修改:

// plugins/withAndroidSignature.js

const { withAppBuildGradle } = require("@expo/config-plugins");
const fs = require("fs");
const path = require("path");

module.exports = function withAndroidSignature(config) {
  return withAppBuildGradle(config, config => {
    if (config.modResults.language === "groovy") {
      config.modResults.contents = setAndroidSignature(config.modResults.contents);
    } else {
      throw new Error("如果不是 groovy,则无法在 app/build.gradle 中设置 signingConfigs");
    }
    return config;
  });
};

function setAndroidSignature(appBuildGradle) {
  if (!fs.existsSync(path.resolve(__dirname, "../credentials.json"))) {
    console.warn("警告:没有设置正式版本的 Android Keystore 文件,因为 credentials.json 不存在。");
    return appBuildGradle;
  }
  const info = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../credentials.json"), { encoding: "utf8" }));

  // 使用正则表达式插入签名信息
  let output = appBuildGradle.replace(
    /(signingConfigs\s*\{)/,
    `$1
        release {
            storeFile file(${JSON.stringify(path.resolve(__dirname, "../credentials/android-release.keystore"))})
            storePassword ${JSON.stringify(info.android.keystore.keystorePassword)}
            keyAlias ${JSON.stringify(info.android.keystore.keyAlias)}
            keyPassword ${JSON.stringify(info.android.keystore.keyPassword)}
        }`,
  );

  // 使用正则表达式替换 signingConfig
  output = output.replace(
    /(release\s*\{)[^}]*?signingConfig\s+signingConfigs\.debug/s,
    `$1
            signingConfig signingConfigs.release
`,
  );

  return output;
}

为了方便维护,我们将 Keystore 的本体放在项目根目录下 credentials/android-release.keystore ,而 Keystore 的信息放在 credentials.json 里。

(这里的 credentials.json 文件格式与 Expo 文档中的 Use local credentials 一致)

{
  "android": {
    "keystore": {
      "keystorePath": "credentials/android-release.keystore",
      "keystorePassword": "your keystore password",
      "keyAlias": "your key alias",
      "keyPassword": "your key password"
    }
  },
  "ios": {
    "provisioningProfilePath": "ios/certs/profile.mobileprovision",
    "distributionCertificate": {
      "path": "ios/certs/dist-cert.p12",
      "password": "password"
    }
  }
}

然后,在 app.json 中指定这个项目级插件:

{
  "expo": {
    "plugins": ["./plugins/withAndroidSignature"]
  }
}

编译

首先,我们需要执行一次 npx expo prebuild,让它生成 android 文件夹。这个文件夹包含了我们项目的 Native 部分。

打包给 Google Play 商店用的 .aab 文件:

cd android
./gradlew bundleRelease

你的编译产物会出现在项目根目录下 android/app/build/outputs/bundle/release/app-release.aab

而如果是给别人直接安装的 .apk 文件:

cd android
./gradlew assembleRelease

你的编译产物会出现在项目根目录下 android/app/build/outputs/apk/release/app-release.apk

如果在 Windows 下编译,需要把 ./gradlew 改成 ./gradlew.bat

还有一些东西……

为了简化上述的操作,我推荐在 package.json 中设置好相应的脚本。

{
  "scripts": {
    "prepare": "pnpm run clean && expo prebuild",
    "build:android": "cd android && ./gradlew assembleRelease",
    "clean": "node -e \"const opt = { recursive: true, force: true }; fs.rmSync('./ios', opt); fs.rmSync('./android', opt)\""
  }
}

另外,不要忘了把这些东西加到你的 .gitignore 里:

/credentials/*.keystore
/credentials.json

/ios
/android

这篇博文受到了上面那个「eas-cli 不能在 Windows 跑」的 Issue 中 这篇回复 的启发,我对于其中的操作流程进行了一些改进,让它更适合 Expo Managed Workflow。