@flun/node-mobile-app 4.0.3 → 4.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Android /346/236/204/345/273/272/346/214/207/345/215/227.md" +347 -348
- package/CHANGELOG.md +9 -19
- package/bin/cli.js +5 -5
- package/mobileAppConfig.js +18 -18
- package/package.json +1 -1
- package/scripts/copy-files.js +1 -1
- package/src/commands/build.js +1 -1
- package/src/commands/test.js +21 -15
- package/src/config.js +8 -11
- package/src/copy.js +24 -28
- package/src/fingerprint.js +5 -5
- package/src/generate.js +7 -10
- package/src/install.js +13 -13
- package/src/optimize-node-modules.js +10 -8
- package/src/patcher.js +21 -21
- package/src/platform-ios.js +7 -7
- package/src/utils.js +13 -13
- package/template/App.tsx +83 -87
- package/template/index.js +1 -1
- package/template/metro.config.js +1 -3
- package//350/207/252/345/256/232/344/271/211libnode/346/214/207/345/215/227.md +444 -444
|
@@ -1,445 +1,445 @@
|
|
|
1
|
-
# 自定义 libnode.so 指南(Android)
|
|
2
|
-
|
|
3
|
-
本文档讲两件事:
|
|
4
|
-
|
|
5
|
-
1.
|
|
6
|
-
2. **自己编译**(进阶)—— 用 WSL + NDK 从源码编译带 full-icu 的 v22+
|
|
7
|
-
|
|
8
|
-
**使用 `@flun/node-mobile-app` 时不需要看这份文档就能打包**——CLI 会自动处理 ABI
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## 一、自定义 libnode.so
|
|
13
|
-
|
|
14
|
-
`@flun/node-mobile-app`
|
|
15
|
-
|
|
16
|
-
| 限制 | 影响
|
|
17
|
-
| ------------------------------ |
|
|
18
|
-
| `--with-intl=none`(不带 ICU) | 不能用 `\p{...}` / `\P{...}` Unicode 正则
|
|
19
|
-
| 无法用 Express 5 | `path-to-regexp@8` 内部用 `\p{ID_Start}
|
|
20
|
-
| 停更 | 官方不再更新
|
|
21
|
-
|
|
22
|
-
**要跑 Express 5 / 用新特性 / 用完整 Unicode
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 二、用预编译产物(推荐)
|
|
27
|
-
|
|
28
|
-
>
|
|
29
|
-
>
|
|
30
|
-
>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
### 2.1 下载地址
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
| 平台 | 源码仓库 | Release 页面 |
|
|
38
|
-
| ------------- | ------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
39
|
-
| GitHub | [OpenFlun/nodejs-mobile](https://github.com/OpenFlun/nodejs-mobile) | [Releases](https://github.com/OpenFlun/nodejs-mobile/releases) |
|
|
40
|
-
| Gitee(国内) | [OpenFlun/nodejs-mobile](https://gitee.com/OpenFlun/nodejs-mobile) | [Releases](https://gitee.com/OpenFlun/nodejs-mobile/releases) |
|
|
41
|
-
|
|
42
|
-
**文件名说明**:发布名为 `android-libnode.zip`(带 `android-`
|
|
43
|
-
|
|
44
|
-
### 2.2 支持矩阵
|
|
45
|
-
|
|
46
|
-
| 架构 | v18 | v22 |
|
|
47
|
-
| ---------------------------- | ------ | -------- |
|
|
48
|
-
| **arm64-v8a**(真机) | ✓ 官方 | ✓ 预编译 |
|
|
49
|
-
| **x86_64**(模拟器) | ✓ 官方 | ✓ 预编译 |
|
|
50
|
-
| **armeabi-v7a**(32 位 ARM) | ✓ 官方 | ✗ |
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- 双架构(推荐):`android-libnode.zip`(约 88 MB
|
|
54
|
-
- 单架构 zip
|
|
55
|
-
|
|
56
|
-
**armeabi-v7a 为什么放弃**:V8 v22 在 x64 主机上无法交叉编译 32 位 ARM 目标(详见 5.1
|
|
57
|
-
|
|
58
|
-
### 2.3 下载与替换
|
|
59
|
-
|
|
60
|
-
**解压后的结构**:
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
libnode/
|
|
64
|
-
├── bin/
|
|
65
|
-
│ ├── arm64-v8a/libnode.so # 真机(约 113 MB)
|
|
66
|
-
│ └── x86_64/libnode.so # 模拟器(约 118 MB)
|
|
67
|
-
└── include/
|
|
68
|
-
└── node/ # 官方 v22 headers(含 ABI 适配)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
**替换到你的项目**:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
# 进入插件的 android 目录
|
|
75
|
-
cd <你的项目>/node_modules/@flun/nodejs-mobile-react-native/android
|
|
76
|
-
|
|
77
|
-
#
|
|
78
|
-
cp -r libnode libnode.bak
|
|
79
|
-
|
|
80
|
-
# 整体替换
|
|
81
|
-
rm -rf libnode
|
|
82
|
-
unzip /path/to/android-libnode.zip -d /tmp
|
|
83
|
-
mv /tmp/libnode ./libnode
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**Windows 下用 PowerShell**:
|
|
87
|
-
|
|
88
|
-
```powershell
|
|
89
|
-
$plugin = '<你的项目>\node_modules\@flun\nodejs-mobile-react-native\android'
|
|
90
|
-
|
|
91
|
-
# 备份
|
|
92
|
-
Copy-Item -Recurse "$plugin\libnode" "$plugin\libnode.bak
|
|
93
|
-
|
|
94
|
-
# 替换
|
|
95
|
-
Remove-Item -Recurse -Force "$plugin\libnode"
|
|
96
|
-
Expand-Archive -Path 'D:\path\to\android-libnode.zip' -DestinationPath "$env:TEMP\libnode-new" -Force
|
|
97
|
-
Move-Item "$env:TEMP\libnode-new\libnode" "$plugin\libnode"
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### 2.4 调整 ABI 配置
|
|
101
|
-
|
|
102
|
-
**如果下载的 `android-libnode.zip` 只含一个架构**(比如只编了 arm64-v8a
|
|
103
|
-
|
|
104
|
-
```js
|
|
105
|
-
android: {
|
|
106
|
-
abiFilters: ['arm64-v8a'], // 只放实际存在的架构
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
**如果双架构**(arm64-v8a + x86_64
|
|
111
|
-
|
|
112
|
-
```js
|
|
113
|
-
android: {
|
|
114
|
-
abiFilters: ['arm64-v8a', 'x86_64'],
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### 2.5 跑 CLI(自动完成所有适配)
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
npx node-mobile-app test # 开发调试
|
|
122
|
-
# 或
|
|
123
|
-
npx node-mobile-app build # 打包 APK
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**CLI 会自动**:
|
|
127
|
-
|
|
128
|
-
| CLI 动作 | 说明 |
|
|
129
|
-
| --------------------------------------------------------------- | ---------------------------- |
|
|
130
|
-
| 检测 `libnode.so` 变化 | 指纹比对 size / mtime |
|
|
131
|
-
| 清 `.cxx` / `android/build` / `app/build` | 避免旧架构的 CMake 中间产物 |
|
|
132
|
-
| Patch `app/build.gradle` 的 `ndk.abiFilters` | 用 `mobileAppConfig.js` 的值 |
|
|
133
|
-
| Patch `android/gradle.properties` 的 `reactNativeArchitectures` | 同步 ABI 列表 |
|
|
134
|
-
| Patch 插件 `build.gradle` 的 `abiFilters` | 让插件只编指定架构 |
|
|
135
|
-
| 重编原生模块(自动检测 `binding.gyp`)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
### 2.6 重编原生模块(仅当项目含原生模块)
|
|
140
|
-
|
|
141
|
-
Node
|
|
142
|
-
|
|
143
|
-
**默认自动处理**:CLI 会扫描 `nodejs-project/node_modules` 下的 `binding.gyp`
|
|
144
|
-
|
|
145
|
-
**手动强制重编**(特殊场景:换过 `libnode.so`
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
# Linux / macOS
|
|
149
|
-
NODE_MOBILE_FORCE_REBUILD=1 npx node-mobile-app test
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
```powershell
|
|
153
|
-
# Windows
|
|
154
|
-
$env:NODE_MOBILE_FORCE_REBUILD=1; npx node-mobile-app test
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
**没有原生模块(纯 JS
|
|
158
|
-
|
|
159
|
-
**切换架构(arm64 ↔ x86_64
|
|
160
|
-
|
|
161
|
-
**Windows 限制**:Windows 上 nodejs-mobile
|
|
162
|
-
|
|
163
|
-
### 2.7 验证
|
|
164
|
-
|
|
165
|
-
**看 Node 版本**:
|
|
166
|
-
|
|
167
|
-
```powershell
|
|
168
|
-
$adb = "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe"
|
|
169
|
-
& $adb logcat -c
|
|
170
|
-
& $adb shell am force-stop <你的 appId>
|
|
171
|
-
& $adb shell am start -n <你的 appId>/.MainActivity
|
|
172
|
-
Start-Sleep -Seconds 5
|
|
173
|
-
& $adb logcat -d | Select-String 'NODEJS-MOBILE'
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
**预期**:
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
D nativeloader: Load ... libnodejs-mobile-react-native-native-lib.so ... : ok
|
|
180
|
-
I NODEJS-MOBILE: Node.js
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
**验证 full-icu**:临时在 `server.js` 顶部加:
|
|
184
|
-
|
|
185
|
-
```js
|
|
186
|
-
try {
|
|
187
|
-
/^[$_\p{ID_Start}]$/u;
|
|
188
|
-
console.log('=== ICU check: \\p{ID_Start} OK');
|
|
189
|
-
} catch (e) {
|
|
190
|
-
console.log('=== ICU check failed:', e.message);
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
抓日志:
|
|
195
|
-
|
|
196
|
-
```powershell
|
|
197
|
-
& $adb logcat -c
|
|
198
|
-
& $adb shell am start -n <你的 appId>/.MainActivity
|
|
199
|
-
Start-Sleep -Seconds 5
|
|
200
|
-
& $adb logcat -d | Select-String 'ICU check'
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
预期:`I NODEJS-MOBILE: === ICU check: \p{ID_Start} OK`
|
|
204
|
-
|
|
205
|
-
若显示 `ICU check failed` → `libnode.so` 不带 full-icu →
|
|
206
|
-
|
|
207
|
-
---
|
|
208
|
-
|
|
209
|
-
## 三、自己编译 libnode.so
|
|
210
|
-
|
|
211
|
-
**环境要求**:WSL(或 Linux
|
|
212
|
-
|
|
213
|
-
### 3.1 准备源码与 NDK
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
# 克隆源码(国内推荐 Gitee)
|
|
217
|
-
git clone https://gitee.com/OpenFlun/nodejs-mobile.git
|
|
218
|
-
cd nodejs-mobile
|
|
219
|
-
|
|
220
|
-
# 下载 Android NDK r27
|
|
221
|
-
cd /mnt/d/nodejs-mobile-build
|
|
222
|
-
wget https://dl.google.com/android/repository/android-ndk-r27-linux.zip
|
|
223
|
-
unzip android-ndk-r27-linux.zip
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### 3.2 关键:启用 full-icu
|
|
227
|
-
|
|
228
|
-
**默认 `--with-intl=none
|
|
229
|
-
|
|
230
|
-
```bash
|
|
231
|
-
cd /mnt/d/nodejs-mobile-build/nodejs-mobile
|
|
232
|
-
|
|
233
|
-
# 改前确认
|
|
234
|
-
grep with-intl android_configure.py
|
|
235
|
-
|
|
236
|
-
# 改成 full-icu
|
|
237
|
-
sed -i 's/--with-intl=none/--with-intl=full-icu/' android_configure.py
|
|
238
|
-
|
|
239
|
-
# 改后确认
|
|
240
|
-
grep with-intl android_configure.py
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### 3.3 编译单架构
|
|
244
|
-
|
|
245
|
-
```bash
|
|
246
|
-
NDK=/mnt/d/nodejs-mobile-build/android-ndk-r27
|
|
247
|
-
SRC=/mnt/d/nodejs-mobile-build/nodejs-mobile
|
|
248
|
-
STRIP=$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip
|
|
249
|
-
|
|
250
|
-
cd $SRC
|
|
251
|
-
rm -rf out config.gypi config.mk config.status
|
|
252
|
-
|
|
253
|
-
# 目标: arm64 或 x86_64
|
|
254
|
-
./android-configure $NDK 30 arm64
|
|
255
|
-
make -j4
|
|
256
|
-
|
|
257
|
-
#
|
|
258
|
-
cp out/Release/libnode.so out/Release/libnode.so.bak
|
|
259
|
-
$STRIP out/Release/libnode.so
|
|
260
|
-
|
|
261
|
-
# 验证符号表未变
|
|
262
|
-
diff <(nm -D out/Release/libnode.so.bak | awk '{print $2, $3}' | sort) \
|
|
263
|
-
<(nm -D out/Release/libnode.so | awk '{print $2, $3}' | sort) \
|
|
264
|
-
&& echo "符号表一致"
|
|
265
|
-
|
|
266
|
-
# 验证 full-icu
|
|
267
|
-
strings out/Release/libnode.so | grep -c "icudt78l"
|
|
268
|
-
# 应输出几千(约 4300
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
### 3.4 多架构编译(推荐用脚本)
|
|
272
|
-
|
|
273
|
-
**一键脚本** `/mnt/d/nodejs-mobile-build/build-all-archs.sh`:
|
|
274
|
-
|
|
275
|
-
```bash
|
|
276
|
-
#!/bin/bash
|
|
277
|
-
set -e
|
|
278
|
-
NDK=/mnt/d/nodejs-mobile-build/android-ndk-r27
|
|
279
|
-
SRC=/mnt/d/nodejs-mobile-build/nodejs-mobile
|
|
280
|
-
TMP=/mnt/d/nodejs-mobile-build/libnode-tmp
|
|
281
|
-
OUT=$SRC/out_android
|
|
282
|
-
STRIP=$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip
|
|
283
|
-
HEADERS=/mnt/d/nodejs-mobile-build/node-
|
|
284
|
-
|
|
285
|
-
# 编译前检查 full-icu
|
|
286
|
-
if ! grep -q "with-intl=full-icu" $SRC/android_configure.py; then
|
|
287
|
-
echo "❌ android_configure.py 未启用 full-icu"
|
|
288
|
-
exit 1
|
|
289
|
-
fi
|
|
290
|
-
echo "✅ full-icu 已启用"
|
|
291
|
-
|
|
292
|
-
mkdir -p $TMP $OUT
|
|
293
|
-
|
|
294
|
-
#
|
|
295
|
-
for ARCH in arm64 x86_64; do
|
|
296
|
-
echo "=== 编译 $ARCH ==="
|
|
297
|
-
cd $SRC
|
|
298
|
-
rm -rf out config.gypi config.mk config.status
|
|
299
|
-
./android-configure $NDK 30 $ARCH
|
|
300
|
-
make -j4
|
|
301
|
-
$STRIP out/Release/libnode.so
|
|
302
|
-
DIR_NAME=$([ "$ARCH" = "arm64" ] && echo "arm64-v8a" || echo "$ARCH")
|
|
303
|
-
mkdir -p $TMP/$DIR_NAME
|
|
304
|
-
cp out/Release/libnode.so $TMP/$DIR_NAME/libnode.so
|
|
305
|
-
echo "--- ICU 符号数: $(strings $TMP/$DIR_NAME/libnode.so | grep -c 'icudt78l') ---"
|
|
306
|
-
done
|
|
307
|
-
|
|
308
|
-
# 打包 android-libnode.zip
|
|
309
|
-
cd /mnt/d/nodejs-mobile-build
|
|
310
|
-
rm -rf libnode-package
|
|
311
|
-
mkdir -p libnode-package/libnode/bin/arm64-v8a
|
|
312
|
-
mkdir -p libnode-package/libnode/bin/x86_64
|
|
313
|
-
mkdir -p libnode-package/libnode/include
|
|
314
|
-
cp -r $HEADERS libnode-package/libnode/include/node
|
|
315
|
-
cp $TMP/arm64-v8a/libnode.so libnode-package/libnode/bin/arm64-v8a/
|
|
316
|
-
cp $TMP/x86_64/libnode.so libnode-package/libnode/bin/x86_64/
|
|
317
|
-
cd libnode-package && zip -r android-libnode.zip libnode
|
|
318
|
-
mv android-libnode.zip $OUT/android-libnode.zip
|
|
319
|
-
|
|
320
|
-
echo "=== 产物:$OUT/android-libnode.zip ==="
|
|
321
|
-
ls -lh $OUT
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
执行:
|
|
325
|
-
|
|
326
|
-
```bash
|
|
327
|
-
chmod +x /mnt/d/nodejs-mobile-build/build-all-archs.sh
|
|
328
|
-
/mnt/d/nodejs-mobile-build/build-all-archs.sh
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
### 3.5 多架构编译注意事项
|
|
332
|
-
|
|
333
|
-
1. **每次切换架构必须清 `out
|
|
334
|
-
2. **切换架构会清空 `out`**:所以脚本编完一个架构立即把 `.so` 复制到 `libnode-tmp/<arch>/`
|
|
335
|
-
3. **每次切架构前确认 `--with-intl=full-icu` 还在**:`android_configure.py`
|
|
336
|
-
4. **验证两个架构都带 ICU**:`strings libnode.so | grep -c "icudt78l"` 应输出几千(约 4300
|
|
337
|
-
|
|
338
|
-
---
|
|
339
|
-
|
|
340
|
-
## 四、产物体积与 Express 版本
|
|
341
|
-
|
|
342
|
-
### 4.1 体积对照
|
|
343
|
-
|
|
344
|
-
**官方数据(strip 后)**:
|
|
345
|
-
|
|
346
|
-
| 版本
|
|
347
|
-
|
|
|
348
|
-
| v18
|
|
349
|
-
| v22
|
|
350
|
-
|
|
351
|
-
**zip 后**:`android-libnode.zip`(含两架构 `.so` + headers)约 88 MB
|
|
352
|
-
|
|
353
|
-
体积差异约 50 MB
|
|
354
|
-
|
|
355
|
-
### 4.2 full-icu 与 Express 版本
|
|
356
|
-
|
|
357
|
-
| libnode.so | Node.js | ICU | Express |
|
|
358
|
-
| --------------------- | ------- | --- | --------------------------- |
|
|
359
|
-
| 官方 v18 | v18 | 无 | **必须 4.x**(如 `4.22.2`) |
|
|
360
|
-
| 自编译 v22 + full-icu | v22 | 有 | 可用 5.x(如 `5.2.1`) |
|
|
361
|
-
|
|
362
|
-
**Express 版本选错的典型崩溃**:
|
|
363
|
-
|
|
364
|
-
- `number 116 is not a function` → v18 无 ICU 加载 `path-to-regexp@8`
|
|
365
|
-
- `Invalid regular expression: /^[$_\p{ID_Start}]$/u` → 同上
|
|
366
|
-
|
|
367
|
-
**Express 5 路由语法迁移**:
|
|
368
|
-
|
|
369
|
-
| Express 4 | Express 5 |
|
|
370
|
-
| --------------------------- | --------------------------------- |
|
|
371
|
-
| `app.get('*', handler)` | `app.get('/{*splat}', handler)` |
|
|
372
|
-
| `app.get('/:id?', handler)` | `app.get('/user{/:id}', handler)` |
|
|
373
|
-
| `app.del('/x', handler)` | `app.delete('/x', handler)` |
|
|
374
|
-
| `res.json(obj, 200)` | `res.status(200).json(obj)` |
|
|
375
|
-
| `res.send(200, body)` | `res.status(200).send(body)` |
|
|
376
|
-
|
|
377
|
-
**不要用 `@alloc/path-to-regexp` 替换原版**:它的 `exports` 只有 `import`
|
|
378
|
-
|
|
379
|
-
---
|
|
380
|
-
|
|
381
|
-
## 五、常见问题
|
|
382
|
-
|
|
383
|
-
### 5.1 armeabi-v7a 为什么放弃
|
|
384
|
-
|
|
385
|
-
**结论**:v22+ **放弃 armeabi-v7a(32 位 ARM
|
|
386
|
-
|
|
387
|
-
**原因**:
|
|
388
|
-
|
|
389
|
-
1. **V8 官方限制**:x64 主机上交叉编译 32 位 arm
|
|
390
|
-
|
|
391
|
-
```
|
|
392
|
-
#error Target architecture arm is only supported on arm and ia32 host
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
2. **实际意义有限**:Android 从 2019 年起要求新应用提供 64
|
|
398
|
-
|
|
399
|
-
**替代**:需 32 位 ARM
|
|
400
|
-
|
|
401
|
-
### 5.2 x86_64 的说明
|
|
402
|
-
|
|
403
|
-
x86_64 在 x64
|
|
404
|
-
|
|
405
|
-
**用途**:Android Studio / 命令行的 x86_64 架构 AVD
|
|
406
|
-
|
|
407
|
-
**用模拟器时**:
|
|
408
|
-
- `mobileAppConfig.js` 的 `abiFilters` 含 `x86_64`
|
|
409
|
-
- `libnode/bin/x86_64/libnode.so` 存在
|
|
410
|
-
- 模拟器镜像必须是 x86_64 架构(不是 arm64)
|
|
411
|
-
|
|
412
|
-
### 5.3 自编译相关坑
|
|
413
|
-
|
|
414
|
-
| 问题
|
|
415
|
-
|
|
|
416
|
-
| `number 116 is not a function`
|
|
417
|
-
| `\p{...}` 正则报错
|
|
418
|
-
| `No "exports" main defined`
|
|
419
|
-
| `Cannot find module 'rn-bridge'`
|
|
420
|
-
| 启动即崩:`pthread_mutex_lock called on a destroyed mutex` + `Fatal signal 6 (SIGABRT)`;`require('rn-bridge')` 返回的对象无 `channel` 属性 | v22 loader 补丁按 `rn-bridge/index.js`
|
|
421
|
-
| JNI 编译报 `undefined symbol: v8::Exception::Error`
|
|
422
|
-
| `NODE_MODULE_VERSION` 不匹配
|
|
423
|
-
| Windows 下 `Unsupported operating system for nodejs-mobile native builds`
|
|
424
|
-
| Gradle 9 报 `Could not find method exec()`
|
|
425
|
-
|
|
426
|
-
---
|
|
427
|
-
|
|
428
|
-
## 六、注意事项
|
|
429
|
-
|
|
430
|
-
1. **插件本身可能不兼容**:`nodejs-mobile-react-native` 的 C++ 胶水代码为特定 Node.js
|
|
431
|
-
2. **依赖库版本**:部分 npm 包限制 Node.js
|
|
432
|
-
3. **非官方产物**:自编译的 `libnode.so`
|
|
433
|
-
4. **多架构建议**:优先保证 `arm64-v8a
|
|
434
|
-
5. **full-icu 是 Express 5 的硬前提**:不要图省事用 `--with-intl=none`
|
|
435
|
-
6. **`android-libnode.zip` 是唯一发布物**:与官方 nodejs-mobile 结构一致(`bin/` + `include
|
|
436
|
-
7. **CLI 会自动处理缓存**:替换 `libnode.so` 后跑 `test` / `build
|
|
437
|
-
|
|
438
|
-
---
|
|
439
|
-
|
|
440
|
-
## 相关文档
|
|
441
|
-
|
|
442
|
-
- [README.md](./README.md) —— 快速开始、配置字段、命令
|
|
443
|
-
- [Android 构建指南.md](./Android 构建指南.md) —— 环境准备、libnode.so 对照、Express 版本、调试
|
|
444
|
-
- **预编译产物下载**:[GitHub Releases](https://github.com/OpenFlun/nodejs-mobile/releases) / [Gitee Releases](https://gitee.com/OpenFlun/nodejs-mobile/releases)
|
|
1
|
+
# 自定义 libnode.so 指南(Android)
|
|
2
|
+
|
|
3
|
+
本文档讲两件事:
|
|
4
|
+
|
|
5
|
+
1. **用预编译产物**(推荐,90% 用户)—— 从仓库下载 `android-libnode.zip`,替换,跑 CLI,完事
|
|
6
|
+
2. **自己编译**(进阶)—— 用 WSL + NDK 从源码编译带 full-icu 的 v22+
|
|
7
|
+
|
|
8
|
+
**使用 `@flun/node-mobile-app` 时不需要看这份文档就能打包**——CLI 会自动处理 ABI 配置、缓存清理、原生模块编译;本文档只在**要换 libnode.so** 时才需要;
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 一、自定义 libnode.so
|
|
13
|
+
|
|
14
|
+
`@flun/node-mobile-app` 的 Node.js 运行时由 `@flun/nodejs-mobile-react-native` 提供的 `libnode.so` 决定;下载版本以该包 `package.json` 的 `prebuiltAssets.version` 字段为准,运行时版本与之一致;不同版本的产物在 ICU 支持上有差异:
|
|
15
|
+
|
|
16
|
+
| 限制 | 影响 |
|
|
17
|
+
| ------------------------------ | ------------------------------------------------------- |
|
|
18
|
+
| `--with-intl=none`(不带 ICU) | 不能用 `\p{...}` / `\P{...}` Unicode 正则 |
|
|
19
|
+
| 无法用 Express 5 | `path-to-regexp@8` 内部用 `\p{ID_Start}`,v18 加载时崩溃 |
|
|
20
|
+
| 停更 | 官方不再更新 |
|
|
21
|
+
|
|
22
|
+
**要跑 Express 5 / 用新特性 / 用完整 Unicode 正则**,需替换为 **v22+ full-icu**;
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 二、用预编译产物(推荐)
|
|
27
|
+
|
|
28
|
+
> **注意**:从 `@flun/nodejs-mobile-react-native` 1.0.1 起,Android 的 `libnode.so` 由 postinstall 脚本从 Release 自动下载;版本以该包 `package.json` 的 `prebuiltAssets.version` 字段为准;
|
|
29
|
+
> **本节适用于**:自动下载的产物为 v22 以下(不带 full-icu),且需跑 Express 5 或使用 Unicode 正则,要换成 v22+ 自编译版本;
|
|
30
|
+
> 若产物为 v22 及以上(带 full-icu),你**不需要**看本节--装包后 `libnode.so` 已就位;
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### 2.1 下载地址
|
|
34
|
+
|
|
35
|
+
**下载版本**:见 Releases 页面,按需选择;版本号以页面中的 tag 为准,不在文档中固定;
|
|
36
|
+
|
|
37
|
+
| 平台 | 源码仓库 | Release 页面 |
|
|
38
|
+
| ------------- | ------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
39
|
+
| GitHub | [OpenFlun/nodejs-mobile](https://github.com/OpenFlun/nodejs-mobile) | [Releases](https://github.com/OpenFlun/nodejs-mobile/releases) |
|
|
40
|
+
| Gitee(国内) | [OpenFlun/nodejs-mobile](https://gitee.com/OpenFlun/nodejs-mobile) | [Releases](https://gitee.com/OpenFlun/nodejs-mobile/releases) |
|
|
41
|
+
|
|
42
|
+
**文件名说明**:发布名为 `android-libnode.zip`(带 `android-` 前缀),为未来 iOS 等平台扩展预留接口(如 `ios-libnode.zip`);**内容仍是标准的 `libnode/` 目录**(`bin/` + `include/`);
|
|
43
|
+
|
|
44
|
+
### 2.2 支持矩阵
|
|
45
|
+
|
|
46
|
+
| 架构 | v18 | v22 |
|
|
47
|
+
| ---------------------------- | ------ | -------- |
|
|
48
|
+
| **arm64-v8a**(真机) | ✓ 官方 | ✓ 预编译 |
|
|
49
|
+
| **x86_64**(模拟器) | ✓ 官方 | ✓ 预编译 |
|
|
50
|
+
| **armeabi-v7a**(32 位 ARM) | ✓ 官方 | ✗ |
|
|
51
|
+
|
|
52
|
+
**下载入口**:
|
|
53
|
+
- 双架构(推荐):`android-libnode.zip`(约 88 MB,具体以实际产物为准)
|
|
54
|
+
- 单架构 zip 已废弃,不再发布
|
|
55
|
+
|
|
56
|
+
**armeabi-v7a 为什么放弃**:V8 v22 在 x64 主机上无法交叉编译 32 位 ARM 目标(详见 5.1);
|
|
57
|
+
|
|
58
|
+
### 2.3 下载与替换
|
|
59
|
+
|
|
60
|
+
**解压后的结构**:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
libnode/
|
|
64
|
+
├── bin/
|
|
65
|
+
│ ├── arm64-v8a/libnode.so # 真机(约 113 MB)
|
|
66
|
+
│ └── x86_64/libnode.so # 模拟器(约 118 MB)
|
|
67
|
+
└── include/
|
|
68
|
+
└── node/ # 官方 v22 headers(含 ABI 适配)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**替换到你的项目**:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# 进入插件的 android 目录
|
|
75
|
+
cd <你的项目>/node_modules/@flun/nodejs-mobile-react-native/android
|
|
76
|
+
|
|
77
|
+
# 备份原产物(可选,方便回退)
|
|
78
|
+
cp -r libnode libnode.bak
|
|
79
|
+
|
|
80
|
+
# 整体替换
|
|
81
|
+
rm -rf libnode
|
|
82
|
+
unzip /path/to/android-libnode.zip -d /tmp
|
|
83
|
+
mv /tmp/libnode ./libnode
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Windows 下用 PowerShell**:
|
|
87
|
+
|
|
88
|
+
```powershell
|
|
89
|
+
$plugin = '<你的项目>\node_modules\@flun\nodejs-mobile-react-native\android'
|
|
90
|
+
|
|
91
|
+
# 备份
|
|
92
|
+
Copy-Item -Recurse "$plugin\libnode" "$plugin\libnode.bak"
|
|
93
|
+
|
|
94
|
+
# 替换
|
|
95
|
+
Remove-Item -Recurse -Force "$plugin\libnode"
|
|
96
|
+
Expand-Archive -Path 'D:\path\to\android-libnode.zip' -DestinationPath "$env:TEMP\libnode-new" -Force
|
|
97
|
+
Move-Item "$env:TEMP\libnode-new\libnode" "$plugin\libnode"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 2.4 调整 ABI 配置
|
|
101
|
+
|
|
102
|
+
**如果下载的 `android-libnode.zip` 只含一个架构**(比如只编了 arm64-v8a),在 `mobileAppConfig.js` 里同步:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
android: {
|
|
106
|
+
abiFilters: ['arm64-v8a'], // 只放实际存在的架构
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**如果双架构**(arm64-v8a + x86_64,当前预编译产物即为双架构):
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
android: {
|
|
114
|
+
abiFilters: ['arm64-v8a', 'x86_64'],
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 2.5 跑 CLI(自动完成所有适配)
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npx node-mobile-app test # 开发调试
|
|
122
|
+
# 或
|
|
123
|
+
npx node-mobile-app build # 打包 APK
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**CLI 会自动**:
|
|
127
|
+
|
|
128
|
+
| CLI 动作 | 说明 |
|
|
129
|
+
| --------------------------------------------------------------- | ---------------------------- |
|
|
130
|
+
| 检测 `libnode.so` 变化 | 指纹比对 size / mtime |
|
|
131
|
+
| 清 `.cxx` / `android/build` / `app/build` | 避免旧架构的 CMake 中间产物 |
|
|
132
|
+
| Patch `app/build.gradle` 的 `ndk.abiFilters` | 用 `mobileAppConfig.js` 的值 |
|
|
133
|
+
| Patch `android/gradle.properties` 的 `reactNativeArchitectures` | 同步 ABI 列表 |
|
|
134
|
+
| Patch 插件 `build.gradle` 的 `abiFilters` | 让插件只编指定架构 |
|
|
135
|
+
| 重编原生模块(自动检测 `binding.gyp`) | 见 2.6 |
|
|
136
|
+
|
|
137
|
+
**你不需要手动清任何缓存;**
|
|
138
|
+
|
|
139
|
+
### 2.6 重编原生模块(仅当项目含原生模块)
|
|
140
|
+
|
|
141
|
+
不同 Node 主版本的 `NODE_MODULE_VERSION` 不同(如 v18 为 108,v22 为 127);**如果项目里装了带 C++ 代码的 npm 包**(如 `bcrypt`、`sqlite3`、`sharp`),替换为不同主版本的 `libnode.so` 后,原生模块需重编才能兼容;
|
|
142
|
+
|
|
143
|
+
**默认自动处理**:CLI 会扫描 `nodejs-project/node_modules` 下的 `binding.gyp` 判断是否有原生模块,自动决定是否重编,**无需配置**;
|
|
144
|
+
|
|
145
|
+
**手动强制重编**(特殊场景:换过 `libnode.so` 但依赖没变,检测机制未触发):
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Linux / macOS
|
|
149
|
+
NODE_MOBILE_FORCE_REBUILD=1 npx node-mobile-app test
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```powershell
|
|
153
|
+
# Windows
|
|
154
|
+
$env:NODE_MOBILE_FORCE_REBUILD=1; npx node-mobile-app test
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**没有原生模块(纯 JS 依赖)**:无需关心,检测会自动跳过;
|
|
158
|
+
|
|
159
|
+
**切换架构(arm64 ↔ x86_64)**:不需要重编;JS 依赖跨架构,只有少数原生模块分架构;
|
|
160
|
+
|
|
161
|
+
**Windows 限制**:Windows 上 nodejs-mobile 无法编译原生模块(上游限制);此功能仅 Linux / macOS 可用;
|
|
162
|
+
|
|
163
|
+
### 2.7 验证
|
|
164
|
+
|
|
165
|
+
**看 Node 版本**:
|
|
166
|
+
|
|
167
|
+
```powershell
|
|
168
|
+
$adb = "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe"
|
|
169
|
+
& $adb logcat -c
|
|
170
|
+
& $adb shell am force-stop <你的 appId>
|
|
171
|
+
& $adb shell am start -n <你的 appId>/.MainActivity
|
|
172
|
+
Start-Sleep -Seconds 5
|
|
173
|
+
& $adb logcat -d | Select-String 'NODEJS-MOBILE'
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**预期**:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
D nativeloader: Load ... libnodejs-mobile-react-native-native-lib.so ... : ok
|
|
180
|
+
<I NODEJS-MOBILE: Node.js vX.Y.Z>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**验证 full-icu**:临时在 `server.js` 顶部加:
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
try {
|
|
187
|
+
/^[$_\p{ID_Start}]$/u;
|
|
188
|
+
console.log('=== ICU check: \\p{ID_Start} OK');
|
|
189
|
+
} catch (e) {
|
|
190
|
+
console.log('=== ICU check failed:', e.message);
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
抓日志:
|
|
195
|
+
|
|
196
|
+
```powershell
|
|
197
|
+
& $adb logcat -c
|
|
198
|
+
& $adb shell am start -n <你的 appId>/.MainActivity
|
|
199
|
+
Start-Sleep -Seconds 5
|
|
200
|
+
& $adb logcat -d | Select-String 'ICU check'
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
预期:`I NODEJS-MOBILE: === ICU check: \p{ID_Start} OK`
|
|
204
|
+
|
|
205
|
+
若显示 `ICU check failed` → `libnode.so` 不带 full-icu → 见第三章自己编,或回退到 Express 4;
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 三、自己编译 libnode.so
|
|
210
|
+
|
|
211
|
+
**环境要求**:WSL(或 Linux),因为构建脚本依赖 `make` / `sed` / NDK;
|
|
212
|
+
|
|
213
|
+
### 3.1 准备源码与 NDK
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# 克隆源码(国内推荐 Gitee)
|
|
217
|
+
git clone https://gitee.com/OpenFlun/nodejs-mobile.git
|
|
218
|
+
cd nodejs-mobile
|
|
219
|
+
|
|
220
|
+
# 下载 Android NDK r27
|
|
221
|
+
cd /mnt/d/nodejs-mobile-build
|
|
222
|
+
wget https://dl.google.com/android/repository/android-ndk-r27-linux.zip
|
|
223
|
+
unzip android-ndk-r27-linux.zip
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 3.2 关键:启用 full-icu
|
|
227
|
+
|
|
228
|
+
**默认 `--with-intl=none`,必须改成 `full-icu`**,否则 Express 5 跑不了;
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
cd /mnt/d/nodejs-mobile-build/nodejs-mobile
|
|
232
|
+
|
|
233
|
+
# 改前确认
|
|
234
|
+
grep with-intl android_configure.py
|
|
235
|
+
|
|
236
|
+
# 改成 full-icu
|
|
237
|
+
sed -i 's/--with-intl=none/--with-intl=full-icu/' android_configure.py
|
|
238
|
+
|
|
239
|
+
# 改后确认
|
|
240
|
+
grep with-intl android_configure.py
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### 3.3 编译单架构
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
NDK=/mnt/d/nodejs-mobile-build/android-ndk-r27
|
|
247
|
+
SRC=/mnt/d/nodejs-mobile-build/nodejs-mobile
|
|
248
|
+
STRIP=$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip
|
|
249
|
+
|
|
250
|
+
cd $SRC
|
|
251
|
+
rm -rf out config.gypi config.mk config.status
|
|
252
|
+
|
|
253
|
+
# 目标: arm64 或 x86_64
|
|
254
|
+
./android-configure $NDK 30 arm64
|
|
255
|
+
make -j4
|
|
256
|
+
|
|
257
|
+
# 剥离符号(必须,否则 .so 会大很多)
|
|
258
|
+
cp out/Release/libnode.so out/Release/libnode.so.bak
|
|
259
|
+
$STRIP out/Release/libnode.so
|
|
260
|
+
|
|
261
|
+
# 验证符号表未变
|
|
262
|
+
diff <(nm -D out/Release/libnode.so.bak | awk '{print $2, $3}' | sort) \
|
|
263
|
+
<(nm -D out/Release/libnode.so | awk '{print $2, $3}' | sort) \
|
|
264
|
+
&& echo "符号表一致"
|
|
265
|
+
|
|
266
|
+
# 验证 full-icu
|
|
267
|
+
strings out/Release/libnode.so | grep -c "icudt78l"
|
|
268
|
+
# 应输出几千(约 4300),否则没编进 ICU
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### 3.4 多架构编译(推荐用脚本)
|
|
272
|
+
|
|
273
|
+
**一键脚本** `/mnt/d/nodejs-mobile-build/build-all-archs.sh`:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
#!/bin/bash
|
|
277
|
+
set -e
|
|
278
|
+
NDK=/mnt/d/nodejs-mobile-build/android-ndk-r27
|
|
279
|
+
SRC=/mnt/d/nodejs-mobile-build/nodejs-mobile
|
|
280
|
+
TMP=/mnt/d/nodejs-mobile-build/libnode-tmp
|
|
281
|
+
OUT=$SRC/out_android
|
|
282
|
+
STRIP=$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip
|
|
283
|
+
HEADERS=/mnt/d/nodejs-mobile-build/node-v<实际版本>-headers/include/node
|
|
284
|
+
|
|
285
|
+
# 编译前检查 full-icu
|
|
286
|
+
if ! grep -q "with-intl=full-icu" $SRC/android_configure.py; then
|
|
287
|
+
echo "❌ android_configure.py 未启用 full-icu"
|
|
288
|
+
exit 1
|
|
289
|
+
fi
|
|
290
|
+
echo "✅ full-icu 已启用"
|
|
291
|
+
|
|
292
|
+
mkdir -p $TMP $OUT
|
|
293
|
+
|
|
294
|
+
# 逐架构编译,暂存 .so
|
|
295
|
+
for ARCH in arm64 x86_64; do
|
|
296
|
+
echo "=== 编译 $ARCH ==="
|
|
297
|
+
cd $SRC
|
|
298
|
+
rm -rf out config.gypi config.mk config.status
|
|
299
|
+
./android-configure $NDK 30 $ARCH
|
|
300
|
+
make -j4
|
|
301
|
+
$STRIP out/Release/libnode.so
|
|
302
|
+
DIR_NAME=$([ "$ARCH" = "arm64" ] && echo "arm64-v8a" || echo "$ARCH")
|
|
303
|
+
mkdir -p $TMP/$DIR_NAME
|
|
304
|
+
cp out/Release/libnode.so $TMP/$DIR_NAME/libnode.so
|
|
305
|
+
echo "--- ICU 符号数: $(strings $TMP/$DIR_NAME/libnode.so | grep -c 'icudt78l') ---"
|
|
306
|
+
done
|
|
307
|
+
|
|
308
|
+
# 打包 android-libnode.zip
|
|
309
|
+
cd /mnt/d/nodejs-mobile-build
|
|
310
|
+
rm -rf libnode-package
|
|
311
|
+
mkdir -p libnode-package/libnode/bin/arm64-v8a
|
|
312
|
+
mkdir -p libnode-package/libnode/bin/x86_64
|
|
313
|
+
mkdir -p libnode-package/libnode/include
|
|
314
|
+
cp -r $HEADERS libnode-package/libnode/include/node
|
|
315
|
+
cp $TMP/arm64-v8a/libnode.so libnode-package/libnode/bin/arm64-v8a/
|
|
316
|
+
cp $TMP/x86_64/libnode.so libnode-package/libnode/bin/x86_64/
|
|
317
|
+
cd libnode-package && zip -r android-libnode.zip libnode
|
|
318
|
+
mv android-libnode.zip $OUT/android-libnode.zip
|
|
319
|
+
|
|
320
|
+
echo "=== 产物:$OUT/android-libnode.zip ==="
|
|
321
|
+
ls -lh $OUT
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
执行:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
chmod +x /mnt/d/nodejs-mobile-build/build-all-archs.sh
|
|
328
|
+
/mnt/d/nodejs-mobile-build/build-all-archs.sh
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 3.5 多架构编译注意事项
|
|
332
|
+
|
|
333
|
+
1. **每次切换架构必须清 `out`**:中间文件不能混用,否则奇怪的编译错误;脚本里已经 `rm -rf out`;
|
|
334
|
+
2. **切换架构会清空 `out`**:所以脚本编完一个架构立即把 `.so` 复制到 `libnode-tmp/<arch>/` 独立保存;
|
|
335
|
+
3. **每次切架构前确认 `--with-intl=full-icu` 还在**:`android_configure.py` 是源码,不会因切换架构被重置——但如果你从 Git 重新 clone 或覆盖过源码,修改会丢,务必再确认;
|
|
336
|
+
4. **验证两个架构都带 ICU**:`strings libnode.so | grep -c "icudt78l"` 应输出几千(约 4300),否则该架构没编进 ICU;
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 四、产物体积与 Express 版本
|
|
341
|
+
|
|
342
|
+
### 4.1 体积对照
|
|
343
|
+
|
|
344
|
+
**官方数据(strip 后)**:
|
|
345
|
+
|
|
346
|
+
| 版本 | arm64-v8a | x86_64 |
|
|
347
|
+
| ---------------------- | ------------- | ------------- |
|
|
348
|
+
| v18(官方,无 ICU) | 59.6 MB | 62.3 MB |
|
|
349
|
+
| v22(自编译,full-icu) | **约 113 MB** | **约 118 MB** |
|
|
350
|
+
|
|
351
|
+
**zip 后**:`android-libnode.zip`(含两架构 `.so` + headers)约 88 MB,具体以实际产物为准;
|
|
352
|
+
|
|
353
|
+
体积差异约 50 MB,来自三部分:Node v22 核心引擎 + full-icu 数据(约 25–30 MB)+ V8 版本升级;**不是编译出错**;
|
|
354
|
+
|
|
355
|
+
### 4.2 full-icu 与 Express 版本
|
|
356
|
+
|
|
357
|
+
| libnode.so | Node.js | ICU | Express |
|
|
358
|
+
| --------------------- | ------- | --- | --------------------------- |
|
|
359
|
+
| 官方 v18 | v18 | 无 | **必须 4.x**(如 `4.22.2`) |
|
|
360
|
+
| 自编译 v22 + full-icu | v22 | 有 | 可用 5.x(如 `5.2.1`) |
|
|
361
|
+
|
|
362
|
+
**Express 版本选错的典型崩溃**:
|
|
363
|
+
|
|
364
|
+
- `number 116 is not a function` → v18 无 ICU 加载 `path-to-regexp@8`
|
|
365
|
+
- `Invalid regular expression: /^[$_\p{ID_Start}]$/u` → 同上
|
|
366
|
+
|
|
367
|
+
**Express 5 路由语法迁移**:
|
|
368
|
+
|
|
369
|
+
| Express 4 | Express 5 |
|
|
370
|
+
| --------------------------- | --------------------------------- |
|
|
371
|
+
| `app.get('*', handler)` | `app.get('/{*splat}', handler)` |
|
|
372
|
+
| `app.get('/:id?', handler)` | `app.get('/user{/:id}', handler)` |
|
|
373
|
+
| `app.del('/x', handler)` | `app.delete('/x', handler)` |
|
|
374
|
+
| `res.json(obj, 200)` | `res.status(200).json(obj)` |
|
|
375
|
+
| `res.send(200, body)` | `res.status(200).send(body)` |
|
|
376
|
+
|
|
377
|
+
**不要用 `@alloc/path-to-regexp` 替换原版**:它的 `exports` 只有 `import` 条件,CJS 下 `require()` 报 `No "exports" main defined`;用原版 `path-to-regexp@8.4.2`;
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## 五、常见问题
|
|
382
|
+
|
|
383
|
+
### 5.1 armeabi-v7a 为什么放弃
|
|
384
|
+
|
|
385
|
+
**结论**:v22+ **放弃 armeabi-v7a(32 位 ARM)**;
|
|
386
|
+
|
|
387
|
+
**原因**:
|
|
388
|
+
|
|
389
|
+
1. **V8 官方限制**:x64 主机上交叉编译 32 位 arm 目标时,`deps/v8/include/v8config.h` 第 914 行硬性拒绝:
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
#error Target architecture arm is only supported on arm and ia32 host
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
即使强行绕过,后续 Torque 工具仍报大量 8 字节对齐错误;
|
|
396
|
+
|
|
397
|
+
2. **实际意义有限**:Android 从 2019 年起要求新应用提供 64 位版本,现代设备几乎全部 arm64-v8a;
|
|
398
|
+
|
|
399
|
+
**替代**:需 32 位 ARM 支持时,使用提供该架构的 `libnode.so`(如 v18 / v20 产物);`abiFilters` 需与之对应;
|
|
400
|
+
|
|
401
|
+
### 5.2 x86_64 的说明
|
|
402
|
+
|
|
403
|
+
x86_64 在 x64 主机上**无交叉编译问题**,与 arm64 流程一致;`android-libnode.zip` 已含 x86_64 产物;
|
|
404
|
+
|
|
405
|
+
**用途**:Android Studio / 命令行的 x86_64 架构 AVD(模拟器);
|
|
406
|
+
|
|
407
|
+
**用模拟器时**:
|
|
408
|
+
- `mobileAppConfig.js` 的 `abiFilters` 含 `x86_64`
|
|
409
|
+
- `libnode/bin/x86_64/libnode.so` 存在
|
|
410
|
+
- 模拟器镜像必须是 x86_64 架构(不是 arm64)
|
|
411
|
+
|
|
412
|
+
### 5.3 自编译相关坑
|
|
413
|
+
|
|
414
|
+
| 问题 | 原因 | 解决 |
|
|
415
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
416
|
+
| `number 116 is not a function` | `libnode.so` 无 full-icu | 重新用 `--with-intl=full-icu` 编译,或回退 Express 4 |
|
|
417
|
+
| `\p{...}` 正则报错 | 同上 | 同上 |
|
|
418
|
+
| `No "exports" main defined` | 用了 `@alloc/path-to-regexp` | 撤销 `overrides`,清 `node_modules` + `package-lock.json` 重装 |
|
|
419
|
+
| `Cannot find module 'rn-bridge'` | Node 20+ 不再把链接绑定暴露给 `require()` | 需修改 Node.js `lib/internal/modules/cjs/loader.js`,从 `NODE_PATH` 按目录加载 `rn-bridge`(入口由该目录 `package.json` 的 `main` 决定) |
|
|
420
|
+
| 启动即崩:`pthread_mutex_lock called on a destroyed mutex` + `Fatal signal 6 (SIGABRT)`;`require('rn-bridge')` 返回的对象无 `channel` 属性 | v22 loader 补丁按 `rn-bridge/index.js` 硬编码定位,而插件入口因 `type: module` 已改名(如 `index.cjs`),匹配失败 → Node 回退到 `process._linkedBinding('rn_bridge')` 拿裸 binding | 使用按**目录** `rn-bridge` 定位的 loader 补丁(入口由该目录 `package.json` 的 `main` 字段解析) |
|
|
421
|
+
| JNI 编译报 `undefined symbol: v8::Exception::Error` | 插件旧头文件(v18 时代)与 v22 `libnode.so` ABI 不匹配 | **用 `android-libnode.zip` 整目录替换**,其 `include/node/` 是 v22 官方 headers |
|
|
422
|
+
| `NODE_MODULE_VERSION` 不匹配 | 原生模块未重编 | `NODE_MOBILE_FORCE_REBUILD=1` 后重跑 CLI |
|
|
423
|
+
| Windows 下 `Unsupported operating system for nodejs-mobile native builds` | 插件硬编码只支持 macOS/Linux | 官方 v22 产物已含 Windows 支持(`windows-x86_64` + `host_os=win32`) |
|
|
424
|
+
| Gradle 9 报 `Could not find method exec()` | Gradle 9 移除 `exec()` | 官方 v22 产物已修复(改用 `providers.exec`) |
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## 六、注意事项
|
|
429
|
+
|
|
430
|
+
1. **插件本身可能不兼容**:`nodejs-mobile-react-native` 的 C++ 胶水代码为特定 Node.js 版本编写;升级到 v22+ 时可能需自行修改插件源码并重编;
|
|
431
|
+
2. **依赖库版本**:部分 npm 包限制 Node.js 版本;检查 `package.json` 的 `engines`;
|
|
432
|
+
3. **非官方产物**:自编译的 `libnode.so` 未经充分测试,生产环境前充分测试;
|
|
433
|
+
4. **多架构建议**:优先保证 `arm64-v8a`,按需补 `x86_64`;`armeabi-v7a` v22+ 已放弃;
|
|
434
|
+
5. **full-icu 是 Express 5 的硬前提**:不要图省事用 `--with-intl=none` 编译;
|
|
435
|
+
6. **`android-libnode.zip` 是唯一发布物**:与官方 nodejs-mobile 结构一致(`bin/` + `include/`),整目录替换,无需手动改头文件;
|
|
436
|
+
7. **CLI 会自动处理缓存**:替换 `libnode.so` 后跑 `test` / `build`,指纹机制检测到变化时清 `.cxx` / `android/build` / `app/build`,并清 `nodejs-assets` 与依赖快照,触发重装、重编、重优化;**无需手动清任何缓存**;
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## 相关文档
|
|
441
|
+
|
|
442
|
+
- [README.md](./README.md) —— 快速开始、配置字段、命令
|
|
443
|
+
- [Android 构建指南.md](./Android 构建指南.md) —— 环境准备、libnode.so 对照、Express 版本、调试
|
|
444
|
+
- **预编译产物下载**:[GitHub Releases](https://github.com/OpenFlun/nodejs-mobile/releases) / [Gitee Releases](https://gitee.com/OpenFlun/nodejs-mobile/releases)
|
|
445
445
|
- **源码仓库**:[GitHub](https://github.com/OpenFlun/nodejs-mobile) / [Gitee](https://gitee.com/OpenFlun/nodejs-mobile)
|