- 切换到 expo-router 文件路由,删除 App.tsx - 新增 5 个 Expo 原生插件:ppocr (OCR), accessibility (账单抓取), notification-listener, screenshot-monitor, sms-receiver - 实现核心领域逻辑:billPipeline (账单流水), dedup (去重), transferRecognizer (转账识别), ruleEngine + categories (双轨制分类), budgets, creditCards, recurring, sync, ocrProcessor - 增强 ledger.ts:支持 balance assertion, option, pad/note 指令, posting 级 metadata, cost/price 解析 - 新增完整 UI:tabs (首页/报表/设置), 交易详情, 预算, 日历热力图, 分类管理, 信用卡, 定期交易, 规则管理 - 实现 Zustand 状态管理:ledgerStore, importStore, settingsStore, metadataStore, automationStore + 持久化 - 新增 AI 功能:chatAssistant, monthlySummary, voiceInput - 实现多端同步:gitSync, webdavSync, icloudSync - 新增主题系统 (tokens/presets) 和 i18n (zh/en) - 添加 30+ 单元测试覆盖核心逻辑
530 lines
16 KiB
Markdown
530 lines
16 KiB
Markdown
# Android 编译与真机运行完整指南
|
|
|
|
> 本文档记录了从零开始搭建 Android 编译环境、修复 Config Plugin / Kotlin / 依赖冲突、到最终在真机上成功运行 debug APK 的完整流程。
|
|
>
|
|
> 适用环境:Windows 11 + Expo SDK 54 + React Native 0.81 + PP-OCRv5 (ONNX Runtime)。
|
|
|
|
---
|
|
|
|
## 目录
|
|
|
|
1. [环境准备](#1-环境准备)
|
|
2. [安装 Android SDK + NDK](#2-安装-android-sdk--ndk)
|
|
3. [ONNX 模型获取](#3-onnx-模型获取)
|
|
4. [expo prebuild](#4-expo-prebuild)
|
|
5. [Gradle 编译](#5-gradle-编译)
|
|
6. [安装到真机](#6-安装到真机)
|
|
7. [Metro + 运行](#7-metro--运行)
|
|
8. [常见问题与解决方案](#8-常见问题与解决方案)
|
|
9. [已修复的 Bug 清单](#9-已修复的-bug-清单)
|
|
|
|
---
|
|
|
|
## 1. 环境准备
|
|
|
|
### JDK
|
|
|
|
React Native 0.81 + Gradle 8.x 需要 **JDK 17+**。本项目使用 **JDK 21**。
|
|
|
|
```bash
|
|
# 验证
|
|
java -version
|
|
# 应输出: java version "21.0.x"
|
|
|
|
# 确认 JAVA_HOME 指向正确的 JDK
|
|
echo $JAVA_HOME
|
|
# 应输出: C:\Program Files\Java\jdk-21
|
|
```
|
|
|
|
> **注意**:如果 `JAVA_HOME` 指向了别的用户的残留目录(如 `D:\Users\yxp\JDK`),需要通过「系统属性 → 环境变量 → 系统变量」修正为实际 JDK 路径。
|
|
|
|
### Node.js
|
|
|
|
```bash
|
|
node -v # 需要 v20+
|
|
npm -v
|
|
```
|
|
|
|
---
|
|
|
|
## 2. 安装 Android SDK + NDK
|
|
|
|
### 方式 A:Android Studio(推荐)
|
|
|
|
1. 下载 Android Studio:https://developer.android.com/studio
|
|
2. 安装时勾选 Android SDK / SDK Platform / Android Virtual Device
|
|
3. 首次启动自动下载 SDK 到默认位置 `C:\Users\<用户>\AppData\Local\Android\Sdk`
|
|
|
|
### 方式 B:命令行(轻量)
|
|
|
|
如果已装 Android Studio 但缺少 cmdline-tools:
|
|
|
|
```bash
|
|
SDK="$HOME/AppData/Local/Android/Sdk"
|
|
TMP="/tmp"
|
|
|
|
# 下载 cmdline-tools(官方,约 130MB)
|
|
curl -L -o "$TMP/cmdline-tools.zip" \
|
|
"https://dl.google.com/android/repository/commandlinetools-win-11076708_latest.zip"
|
|
|
|
# 解压到标准位置
|
|
mkdir -p "$SDK/cmdline-tools"
|
|
unzip -q "$TMP/cmdline-tools.zip" -d "$TMP/"
|
|
cp -r "$TMP/cmdline-tools" "$SDK/cmdline-tools/latest"
|
|
```
|
|
|
|
### 必装组件
|
|
|
|
```bash
|
|
export JAVA_HOME="C:/Program Files/Java/jdk-21"
|
|
export ANDROID_HOME="C:/Users/<用户>/AppData/Local/Android/Sdk"
|
|
SDKMGR="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager.bat"
|
|
|
|
# 接受所有 license
|
|
yes | "$SDKMGR" --licenses
|
|
|
|
# 安装必需组件
|
|
yes | "$SDKMGR" \
|
|
"platform-tools" \
|
|
"platforms;android-35" \
|
|
"build-tools;35.0.0"
|
|
```
|
|
|
|
| 组件 | 用途 |
|
|
|------|------|
|
|
| `platform-tools` | adb(设备连接) |
|
|
| `platforms;android-35` | 编译目标 SDK |
|
|
| `build-tools;35.0.0` | aapt/dex/打包工具 |
|
|
| `cmdline-tools;latest` | sdkmanager(expo prebuild 需要) |
|
|
|
|
### NDK(React Native 编译 C++ 需要)
|
|
|
|
React Native 0.81 要求 **NDK 27.1.12297006**(~745MB)。
|
|
|
|
#### 国内推荐:腾讯云镜像下载
|
|
|
|
Google 官方源在国内经常断连导致 zip 损坏。**强烈推荐用腾讯云镜像**:
|
|
|
|
```bash
|
|
SDK="$HOME/AppData/Local/Android/Sdk"
|
|
TMP="/tmp"
|
|
|
|
# 从腾讯云镜像下载(55MB/s 稳定,约 13 秒)
|
|
curl -L -o "$TMP/ndk.zip" \
|
|
"https://mirrors.cloud.tencent.com/AndroidSDK/android-ndk-r27b-windows.zip"
|
|
|
|
# 解压
|
|
mkdir -p "$TMP/ndk-extract"
|
|
unzip -q "$TMP/ndk.zip" -d "$TMP/ndk-extract"
|
|
|
|
# 安装到标准位置
|
|
mv "$TMP/ndk-extract/android-ndk-r27b" "$SDK/ndk/27.1.12297006"
|
|
```
|
|
|
|
#### expo-sqlite 的 NDK 版本冲突
|
|
|
|
`expo-sqlite` 可能要求 NDK `27.0.12077973`(不同于 RN 要求的 `27.1.12297006`)。
|
|
解决方式:用 Windows 目录联接(junction)让两个版本指向同一份 NDK:
|
|
|
|
```powershell
|
|
# 在 PowerShell 中执行
|
|
cmd /c mklink /J `
|
|
"C:\Users\<用户>\AppData\Local\Android\Sdk\ndk\27.0.12077973" `
|
|
"C:\Users\<用户>\AppData\Local\Android\Sdk\ndk\27.1.12297006"
|
|
```
|
|
|
|
> 两个版本号 ABI/API 兼容,联接不会导致编译问题。
|
|
|
|
#### NDK 完整性验证
|
|
|
|
NDK 解压后必须验证 arm64 目标库完整,否则 CMake 会报 "clang broken":
|
|
|
|
```bash
|
|
SDK="$HOME/AppData/Local/Android/Sdk"
|
|
NDK="$SDK/ndk/27.1.12297006"
|
|
|
|
# 关键文件必须存在
|
|
ls "$NDK/sysroot/usr/lib/aarch64-linux-android/24/crtbegin_dynamic.o" \
|
|
"$NDK/sysroot/usr/lib/aarch64-linux-android/libc.a" \
|
|
"$NDK/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/18/lib/linux/libclang_rt.builtins-aarch64-android.a"
|
|
```
|
|
|
|
> ⚠️ 如果 C 盘空间不足(NDK 解压后约 2.3GB),解压会中途截断,导致 `libc.a` 等文件缺失。确保至少有 **5GB 可用空间**。
|
|
|
|
---
|
|
|
|
## 3. ONNX 模型获取
|
|
|
|
本项目使用 PP-OCRv5 + ONNX Runtime(替代 NCNN,见 plan.md 决策 7)。
|
|
|
|
### 下载社区 ONNX 模型
|
|
|
|
```bash
|
|
cd plugins/ppocr/assets
|
|
mkdir -p . && cd .
|
|
|
|
# det 模型(4.8 MB)
|
|
curl -L -o ppocrv5_det.onnx \
|
|
"https://huggingface.co/ilaylow/PP_OCRv5_mobile_onnx/resolve/main/ppocrv5_det.onnx"
|
|
|
|
# rec 模型(16.6 MB)
|
|
curl -L -o ppocrv5_rec.onnx \
|
|
"https://huggingface.co/ilaylow/PP_OCRv5_mobile_onnx/resolve/main/ppocrv5_rec.onnx"
|
|
|
|
# CJK 字典(26 KB,PaddleOCR 标准字典)
|
|
curl -L -o ppocr_keys_v1.txt \
|
|
"https://raw.githubusercontent.com/PaddlePaddle/PaddleOCR/release/2.6/ppocr/utils/ppocr_keys_v1.txt"
|
|
```
|
|
|
|
### 验证模型 I/O 规格
|
|
|
|
```python
|
|
import onnx
|
|
for name in ['det', 'rec']:
|
|
m = onnx.load(f'plugins/ppocr/assets/ppocrv5_{name}.onnx')
|
|
print(f'{name}:')
|
|
for i in m.graph.input:
|
|
shape = [d.dim_value or d.dim_param for d in i.type.tensor_type.shape.dim]
|
|
print(f' input {i.name}: {shape}')
|
|
for o in m.graph.output:
|
|
shape = [d.dim_value or d.dim_param for d in o.type.tensor_type.shape.dim]
|
|
print(f' output {o.name}: {shape}')
|
|
```
|
|
|
|
预期输出:
|
|
- det: 输入 `x: [N, 3, H, W]`(动态),输出 `[N, 1, H, W]`
|
|
- rec: 输入 `x: [N, 3, 48, W]`(高固定 48),输出 `[N, T, 18385]`
|
|
|
|
---
|
|
|
|
## 4. expo prebuild
|
|
|
|
`expo prebuild` 把 Config Plugin 注入到原生工程(生成 `android/` 目录)。
|
|
|
|
```bash
|
|
export JAVA_HOME="C:/Program Files/Java/jdk-21"
|
|
export ANDROID_HOME="C:/Users/<用户>/AppData/Local/Android/Sdk"
|
|
|
|
npx expo prebuild --platform android --no-install
|
|
```
|
|
|
|
### 验证注入结果
|
|
|
|
```bash
|
|
# Kotlin 源码(应有 9 个 .kt 文件)
|
|
find android/app/src/main/java/com/beancount -name "*.kt" | sort
|
|
|
|
# ONNX 模型 + 字典
|
|
ls android/app/src/main/assets/
|
|
|
|
# MainApplication 里 OcrPackage 注册
|
|
grep "OcrPackage" android/app/src/main/java/com/example/beanmobile/MainApplication.kt
|
|
|
|
# onnxruntime gradle 依赖
|
|
grep "onnxruntime" android/app/build.gradle
|
|
|
|
# AndroidManifest 服务注册
|
|
grep "Billing" android/app/src/main/AndroidManifest.xml
|
|
```
|
|
|
|
### android/ 目录锁定问题(Windows + ZCode)
|
|
|
|
如果 `android/` 目录被进程锁定导致 prebuild 无法 `rmdir`,在临时副本项目中 prebuild 再 robocopy 回来:
|
|
|
|
```bash
|
|
# 1. 创建临时副本
|
|
TMP="/tmp/bm-prebuild"
|
|
mkdir -p "$TMP"
|
|
cp app.json package.json package-lock.json tsconfig.json "$TMP/"
|
|
cp -r app/ src/ plugins/ "$TMP/"
|
|
|
|
# node_modules 用 junction 共享
|
|
powershell -NoProfile -Command "cmd /c mklink /J '$TMP/node_modules' '$(pwd)/node_modules'"
|
|
|
|
# 2. 在临时项目 prebuild
|
|
cd "$TMP"
|
|
npx expo prebuild --platform android --no-install --clean
|
|
|
|
# 3. robocopy 回原项目(能处理锁定)
|
|
powershell -NoProfile -Command "robocopy '$TMP/android' '$(pwd -W || pwd)/android' /E /PURGE"
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Gradle 编译
|
|
|
|
### 前提:onnxruntime 版本修正
|
|
|
|
Config Plugin 注入的 `app/build.gradle` 中 onnxruntime 版本可能不匹配,每次 prebuild 后需检查:
|
|
|
|
```bash
|
|
# 确认是 1.20.0(不是 1.20.1,后者不存在)
|
|
grep "onnxruntime" android/app/build.gradle
|
|
# 如果是 1.20.1,修正:
|
|
sed -i 's/onnxruntime-android:1.20.1/onnxruntime-android:1.20.0/' android/app/build.gradle
|
|
```
|
|
|
|
### 编译 Kotlin(快速验证,不打包)
|
|
|
|
```bash
|
|
cd android
|
|
export JAVA_HOME="C:/Program Files/Java/jdk-21"
|
|
export ANDROID_HOME="C:/Users/<用户>/AppData/Local/Android/Sdk"
|
|
|
|
./gradlew :app:compileDebugKotlin --no-daemon
|
|
```
|
|
|
|
### 完整打包 APK
|
|
|
|
```bash
|
|
./gradlew :app:assembleDebug --no-daemon
|
|
```
|
|
|
|
APK 输出位置:`android/app/build/outputs/apk/debug/app-debug.apk`(~250MB)
|
|
|
|
### Gradle 缓存清理(遇到诡异问题时)
|
|
|
|
```bash
|
|
# 删 build 缓存
|
|
rm -rf app/build build app/.cxx
|
|
|
|
# 彻底重来(连 Gradle daemon 也杀)
|
|
powershell -NoProfile -Command "Get-Process -Name 'java' -ErrorAction SilentlyContinue | Stop-Process -Force"
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 安装到真机
|
|
|
|
### 开启 USB 调试
|
|
|
|
1. 手机「设置 → 关于手机」→ 连续点击「MIUI 版本」7 次 → 开启开发者选项
|
|
2. 「设置 → 更多设置 → 开发者选项」→ 打开:
|
|
- ✅ USB 调试
|
|
- ✅ **USB 安装**(红米/小米必须开,否则 `adb install` 被拒为 `INSTALL_FAILED_USER_RESTRICTED`)
|
|
3. USB 连接电脑(文件传输模式)
|
|
|
|
### adb install
|
|
|
|
```bash
|
|
export ANDROID_HOME="C:/Users/<用户>/AppData/Local/Android/Sdk"
|
|
ADB="$ANDROID_HOME/platform-tools/adb"
|
|
|
|
# 确认设备连接
|
|
"$ADB" devices
|
|
# 应显示 device 状态
|
|
|
|
# 安装
|
|
"$ADB" install -r android/app/build/outputs/apk/debug/app-debug.apk
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Metro + 运行
|
|
|
|
Debug APK 需要从 Metro server 加载 JS bundle(不内嵌 JS)。
|
|
|
|
### 启动 Metro
|
|
|
|
```bash
|
|
npx expo start --dev-client --port 8081
|
|
```
|
|
|
|
### adb reverse(USB 连接,手机和电脑不在同一 WiFi 时必需)
|
|
|
|
```bash
|
|
"$ADB" reverse tcp:8081 tcp:8081
|
|
```
|
|
|
|
### 启动 app
|
|
|
|
```bash
|
|
# 命令行启动
|
|
"$ADB" shell am start -n com.example.beanmobile/.MainActivity
|
|
|
|
# 或直接在手机桌面点击「Bean Mobile」图标
|
|
```
|
|
|
|
### 验证
|
|
|
|
```bash
|
|
# 进程存活?
|
|
"$ADB" shell pidof com.example.beanmobile
|
|
|
|
# JS 日志
|
|
"$ADB" logcat -d | grep "ReactNativeJS"
|
|
|
|
# 截图
|
|
"$ADB" shell screencap -p /sdcard/screen.png
|
|
"$ADB" pull /sdcard/screen.png /tmp/app-screen.png
|
|
```
|
|
|
|
---
|
|
|
|
## 8. 常见问题与解决方案
|
|
|
|
### `withAndroidMainApplication is not a function`
|
|
|
|
**原因**:SDK 54 的 `@expo/config-plugins` 没有 `withAndroidMainApplication` / `withAndroidGradle`。
|
|
|
|
**解决**:改用 `withMainApplication` + `withAppBuildGradle` + `withDangerousMod`。
|
|
|
|
### `Cannot read properties of undefined (reading '0')` in AndroidManifest
|
|
|
|
**原因**:`manifest.application` 在 prebuild 初始模板里可能不存在。
|
|
|
|
**解决**:操作前加防御性检查:
|
|
```javascript
|
|
if (!Array.isArray(manifest.application) || manifest.application.length === 0) {
|
|
manifest.application = [{ $: {} }];
|
|
}
|
|
```
|
|
|
|
### `modResults` 结构搞错导致 manifest 重复 `<root>` 标签
|
|
|
|
**原因**:`withAndroidManifest` 的 `modResults` 结构是 `{ manifest: { ... } }`,不是直接就是 manifest。
|
|
|
|
**解决**:所有权限和 application 操作通过 `modConfig.modResults.manifest`,不是 `modConfig.modResults` 本身。
|
|
|
|
### MainApplication 里 import OcrPackage 没注入
|
|
|
|
**原因**:Kotlin 的 package 声明**没有分号**(`package com.example.beanmobile`,不是 `package com.example.beanmobile;`),正则 `^(package [\w.]+;)` 匹配不到。
|
|
|
|
**解决**:正则去掉分号要求:`^(package\s+[\w.]+;?\s*)$`。
|
|
|
|
### Config Plugin 返回 undefined 导致整个 config 丢失
|
|
|
|
**原因**:插件函数最后漏了 `return config;`,导致 `withAndroidManifest` 的结果没返回,config 变成 undefined,后续所有插件崩溃。
|
|
|
|
**解决**:每个插件函数末尾必须有 `return config;`。
|
|
|
|
### `NDK at ...27.0.12077973 did not have a source.properties file`
|
|
|
|
**原因**:expo-sqlite 要求 NDK 27.0,但只装了 27.1;Gradle 自动下载 27.0 失败留下空目录。
|
|
|
|
**解决**:用 `mklink /J` 创建目录联接(见第 2 节)。
|
|
|
|
### NDK `clang broken` / `cannot open crtbegin_dynamic.o`
|
|
|
|
**原因**:NDK 解压时磁盘空间不足,arm64 目标库被截断。
|
|
|
|
**解决**:确保至少 5GB 可用空间,重新从腾讯云镜像下载并解压。
|
|
|
|
### `onnxruntime-android:1.20.1` not found
|
|
|
|
**原因**:Maven Central 上最新稳定版是 `1.20.0`,`1.20.1` 不存在。
|
|
|
|
**解决**:`sed -i 's/1.20.1/1.20.0/' android/app/build.gradle`
|
|
|
|
### `NoSuchMethodError: getDirectConverter` (expo-font 崩溃)
|
|
|
|
**原因**:`@expo/vector-icons@15.x` 拉进了 `expo-font@57.0.0`(SDK 55 版本),与 `expo-modules-core@3.0.x`(SDK 54)不兼容。
|
|
|
|
**解决**:在 `package.json` 加 npm override 强制降级:
|
|
```json
|
|
"overrides": {
|
|
"expo-font": "~14.0.12"
|
|
}
|
|
```
|
|
|
|
### `Unable to resolve "expo-linking"`
|
|
|
|
**原因**:expo-router 的 peer dependencies 没有全部安装。
|
|
|
|
**解决**:补全所有 peer deps:
|
|
```bash
|
|
npm install --legacy-peer-deps \
|
|
@expo/metro-runtime expo-constants expo-linking \
|
|
react-dom react-native-gesture-handler react-native-reanimated \
|
|
@react-navigation/drawer
|
|
```
|
|
|
|
### `INSTALL_FAILED_USER_RESTRICTED` (红米/小米)
|
|
|
|
**原因**:MIUI 的「USB 安装」开关默认关闭。
|
|
|
|
**解决**:开发者选项里打开「USB 安装」。
|
|
|
|
### `EBUSY: resource busy or locked, rmdir android/`
|
|
|
|
**原因**:ZCode 的子进程(NodeBabyLinkService)持有 `android/` 目录句柄,`rmdir` 失败。
|
|
|
|
**解决**:在临时副本项目里 prebuild,再用 `robocopy /E /PURGE` 复制回原项目(见第 4 节)。
|
|
|
|
### `attribute android:canTakeScreenshots not found`
|
|
|
|
**原因**:`canTakeScreenshots` 是 API 30+ 属性,部分 compileSdk 配置下 AAPT 不识别。
|
|
|
|
**解决**:从 `accessibility_service_config.xml` 删除该属性(非必需,截图功能在 Kotlin 代码里实现)。
|
|
|
|
---
|
|
|
|
## 9. 已修复的 Bug 清单
|
|
|
|
以下是真机编译过程中暴露并修复的全部 bug(共 26 个):
|
|
|
|
### Config Plugin Bug(7 个)
|
|
|
|
| 文件 | Bug | 修复 |
|
|
|------|-----|------|
|
|
| `plugins/ppocr/app.plugin.js` | `withAndroidMainApplication` 不存在 | 改用 `withMainApplication` + `withAppBuildGradle` + `withDangerousMod` |
|
|
| `plugins/ppocr/app.plugin.js` | `withDangerousMod` 回调里 `modConfig.platformProjectRoot` undefined | 改为 `modConfig.modRequest.platformProjectRoot` |
|
|
| `plugins/*/app.plugin.js`(3 个) | `manifest.application[0]` 崩溃(prebuild 初始 manifest 无 application 节点) | 加 `Array.isArray` 防御 |
|
|
| `plugins/ppocr/app.plugin.js` | MainApplication import 没注入(Kotlin package 无分号) | 正则去掉分号要求 |
|
|
| `plugins/accessibility/app.plugin.js` | `return config` 丢失导致整个 config 变 undefined | 补回 `return config` |
|
|
|
|
### Kotlin 编译 Bug(11 个)
|
|
|
|
| 文件 | Bug | 修复 |
|
|
|------|-----|------|
|
|
| `FloatingTip.kt:80` | `val layoutParams` 不可重赋值 + 类型不匹配 | 拆出 apply 块 + 用 LinearLayout.LayoutParams |
|
|
| `OcrTileService.kt:50` | `startActivityAndCollapse(intentSender)` 参数不匹配 | 改传 PendingIntent |
|
|
| `OcrModule.kt:179` | `putDouble(confidence)` Float 不匹配 | `.toDouble()` |
|
|
| `OcrModule.kt:215,235` | `longArrayOf(1,3,...)` Int vs Long | 显式 `.toLong()` + `L` 后缀 |
|
|
| `OcrModule.kt:221,242` | `detOutputs.forEach{it.close()}` 未解析 | 改用 `detOutputs.close()`(OrtSession.Result.close) |
|
|
| `OcrModule.kt:333` | det 输出维度多了 channel 维,`prob[y][x]` 是 FloatArray 不是 Float | `[0][0]` 剥掉 N+C 两维,函数签名改 `Array<FloatArray>` |
|
|
|
|
### 资源/环境 Bug(8 个)
|
|
|
|
| 问题 | 修复 |
|
|
|------|------|
|
|
| cmdline-tools 缺失 | 从 Google 官方下载安装 |
|
|
| platform 35 缺失 | sdkmanager 安装 |
|
|
| NDK 27.0 vs 27.1 版本冲突 | `mklink /J` 目录联接 |
|
|
| NDK 下载损坏(Google 源断连) | 腾讯云镜像替代 |
|
|
| `accessibility_service_config.xml` canTakeScreenshots 属性 | 删除(非必需) |
|
|
| `onnxruntime:1.20.1` 不存在 | 改用 1.20.0 |
|
|
| expo-font 57.0 vs 14.0 版本冲突 | npm overrides 强制降级 |
|
|
| expo-router peer deps 缺失 | 补全 8 个依赖包 |
|
|
|
|
---
|
|
|
|
## 附录:完整一键流程
|
|
|
|
```bash
|
|
# === 环境变量(每次新终端都要设) ===
|
|
export JAVA_HOME="C:/Program Files/Java/jdk-21"
|
|
export ANDROID_HOME="C:/Users/$USER/AppData/Local/Android/Sdk"
|
|
|
|
# === 1. 依赖 ===
|
|
npm install --legacy-peer-deps
|
|
|
|
# === 2. prebuild ===
|
|
npx expo prebuild --platform android --no-install
|
|
sed -i 's/onnxruntime-android:1.20.1/onnxruntime-android:1.20.0/' android/app/build.gradle
|
|
|
|
# === 3. 编译 ===
|
|
cd android
|
|
./gradlew :app:assembleDebug --no-daemon
|
|
|
|
# === 4. 安装 ===
|
|
cd ..
|
|
"$ANDROID_HOME/platform-tools/adb" install -r android/app/build/outputs/apk/debug/app-debug.apk
|
|
|
|
# === 5. Metro + 运行 ===
|
|
npx expo start --dev-client --port 8081 &
|
|
sleep 10
|
|
"$ANDROID_HOME/platform-tools/adb" reverse tcp:8081 tcp:8081
|
|
"$ANDROID_HOME/platform-tools/adb" shell am start -n com.example.beanmobile/.MainActivity
|
|
```
|