const { withDangerousMod } = require('@expo/config-plugins'); const fs = require('fs'); const path = require('path'); function withSizeOptimization(config) { // 1. 在 prebuild 时修改 gradle.properties (开启 R8/ProGuard 代码混淆 + 资源裁剪 + .so 库压缩) config = withDangerousMod(config, [ 'android', async (modConfig) => { const projectRoot = modConfig.modRequest.platformProjectRoot; const propertiesPath = path.join(projectRoot, 'gradle.properties'); if (fs.existsSync(propertiesPath)) { let content = fs.readFileSync(propertiesPath, 'utf8'); // 限制打包架构为 arm64-v8a if (content.includes('reactNativeArchitectures=')) { content = content.replace(/reactNativeArchitectures=.*/, 'reactNativeArchitectures=arm64-v8a'); } else { content += '\nreactNativeArchitectures=arm64-v8a\n'; } // 1. 开启 R8 代码混淆与摇树裁剪 if (!content.includes('android.enableMinifyInReleaseBuilds=')) { content += 'android.enableMinifyInReleaseBuilds=true\n'; } else { content = content.replace(/android\.enableMinifyInReleaseBuilds=.*/, 'android.enableMinifyInReleaseBuilds=true'); } // 1. 开启无用资源裁剪 if (!content.includes('android.enableShrinkResourcesInReleaseBuilds=')) { content += 'android.enableShrinkResourcesInReleaseBuilds=true\n'; } else { content = content.replace(/android\.enableShrinkResourcesInReleaseBuilds=.*/, 'android.enableShrinkResourcesInReleaseBuilds=true'); } // 2. 开启 .so 动态库在 APK 内的高倍率压缩 (useLegacyPackaging=true) if (!content.includes('expo.useLegacyPackaging=')) { content += 'expo.useLegacyPackaging=true\n'; } else { content = content.replace(/expo\.useLegacyPackaging=.*/, 'expo.useLegacyPackaging=true'); } fs.writeFileSync(propertiesPath, content, 'utf8'); } return modConfig; } ]); // 2. 在 prebuild 时修改 app/build.gradle 启用 ABI Splits 分包 + 字体裁剪 config = withDangerousMod(config, [ 'android', async (modConfig) => { const projectRoot = modConfig.modRequest.platformProjectRoot; const buildGradlePath = path.join(projectRoot, 'app/build.gradle'); if (fs.existsSync(buildGradlePath)) { let content = fs.readFileSync(buildGradlePath, 'utf8'); // ABI Splits 分包配置 if (!content.includes('splits {')) { content = content.replace( /android\s*\{/, `android {\n splits {\n abi {\n enable true\n reset()\n include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"\n universalApk true\n }\n }` ); } // 3. 裁剪未使用的 @expo/vector-icons 矢量字体 if (!content.includes('variant.mergeAssetsProvider.configure')) { content += ` android.applicationVariants.all { variant -> if (variant.buildType.name == "release") { variant.mergeAssetsProvider.configure { doLast { delete fileTree(dir: outputDir, includes: [ "**/fonts/AntDesign.ttf", "**/fonts/Entypo.ttf", "**/fonts/EvilIcons.ttf", "**/fonts/Feather.ttf", "**/fonts/FontAwesome*.ttf", "**/fonts/Fontisto.ttf", "**/fonts/Foundation.ttf", "**/fonts/MaterialCommunityIcons.ttf", "**/fonts/MaterialIcons.ttf", "**/fonts/Octicons.ttf", "**/fonts/SimpleLineIcons.ttf", "**/fonts/Zocial.ttf" ]) } } } } `; } fs.writeFileSync(buildGradlePath, content, 'utf8'); } return modConfig; } ]); // 3. 在 prebuild 时修改 proguard-rules.pro 保护原生插件与 ONNX 模块 config = withDangerousMod(config, [ 'android', async (modConfig) => { const projectRoot = modConfig.modRequest.platformProjectRoot; const proguardPath = path.join(projectRoot, 'app/proguard-rules.pro'); if (fs.existsSync(proguardPath)) { let content = fs.readFileSync(proguardPath, 'utf8'); if (!content.includes('com.beancount.mobile')) { content += ` # Size optimization: keep custom native packages & ONNX -keep class com.beancount.mobile.** { *; } -keep class com.example.driftledger.** { *; } -keep class ai.onnxruntime.** { *; } `; fs.writeFileSync(proguardPath, content, 'utf8'); } } return modConfig; } ]); // 4. 在 prebuild 时修改 root build.gradle 强制指定所有模块的 NDK 版本以匹配 27.1.12297006 config = withDangerousMod(config, [ 'android', async (modConfig) => { const projectRoot = modConfig.modRequest.platformProjectRoot; const rootBuildGradlePath = path.join(projectRoot, 'build.gradle'); if (fs.existsSync(rootBuildGradlePath)) { let content = fs.readFileSync(rootBuildGradlePath, 'utf8'); if (!content.includes('ndkVersion = "27.1.12297006"')) { content += ` subprojects { project -> def configureProject = { proj -> if (proj.hasProperty("android")) { proj.android { ndkVersion = "27.1.12297006" } } } if (project.state.executed) { configureProject(project) } else { project.afterEvaluate { configureProject(project) } } } `; fs.writeFileSync(rootBuildGradlePath, content, 'utf8'); } } return modConfig; } ]); return config; } module.exports = withSizeOptimization;