#!/bin/bash # ============================================================ # 星元智灵 APK 手动编译脚本 (无 Gradle) # 使用 Android SDK 命令行工具: aapt / javac / d8 / zipalign / apksigner # ============================================================ set -e # ---- 路径配置 ---- PROJECT="$(cd "$(dirname "$0")" && pwd)" SDK="$HOME/Library/Android/sdk" BT="$SDK/build-tools/34.0.0" PLATFORM="$SDK/platforms/android-28/android.jar" SRC="$PROJECT/app/src/main" BUILD="$PROJECT/build" KEYSTORE="$PROJECT/keystore/zhitou-debug.keystore" APK_BASENAME="xingyuan-zhiling" STORE_PASS="android" KEY_PASS="android" KEY_ALIAS="androiddebugkey" # ---- 工具路径 ---- AAPT="$BT/aapt" D8="$BT/d8" ZIPALIGN="$BT/zipalign" APKSIGNER="$BT/apksigner" NDK_VERSION="27.3.13750724" NDK="$SDK/ndk/$NDK_VERSION" ARM_CLANGXX="$NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi21-clang++" # ---- 输出目录 ---- OUT="$BUILD/out" GEN="$BUILD/gen" CLASSES="$BUILD/classes" DEX="$BUILD/dex" echo "==========================================" echo " 星元智灵 APK 编译" echo "==========================================" echo "SDK: $SDK" echo "BuildTools: 34.0.0" echo "Platform: android-28" echo "Project: $PROJECT" echo "" # ---- 恢复超过 Git 单文件限制的人脸模型 ---- "$PROJECT/scripts/restore-face-models.sh" # ---- Debug 签名证书 ---- if [ ! -f "$KEYSTORE" ]; then echo "未找到 debug keystore,正在生成: $KEYSTORE" mkdir -p "$(dirname "$KEYSTORE")" keytool -genkeypair \ -keystore "$KEYSTORE" \ -storepass "$STORE_PASS" \ -keypass "$KEY_PASS" \ -alias "$KEY_ALIAS" \ -keyalg RSA \ -keysize 2048 \ -validity 10000 \ -dname "CN=Android Debug,O=Xingyuan Zhiling,C=CN" >/dev/null fi # ---- Step 1: 清理 ---- echo "[1/8] 清理旧的编译产物..." rm -rf "$GEN" "$CLASSES" "$DEX" mkdir -p "$GEN" "$CLASSES" "$DEX" "$OUT" # ---- Step 2: 生成 R.java ---- echo "[2/8] 生成 R.java..." "$AAPT" package -f -m \ -J "$GEN" \ -M "$SRC/AndroidManifest.xml" \ -S "$SRC/res" \ -A "$SRC/assets" \ -I "$PLATFORM" # ---- Step 3: 收集 Java 源文件 ---- echo "[3/8] 收集 Java 源文件..." SRC_FILES=$(find "$SRC/java" "$GEN" -name "*.java" | tr '\n' ' ') echo " 源文件数量: $(echo $SRC_FILES | wc -w | tr -d ' ')" # ---- Step 4: 编译 Java ---- echo "[4/8] 编译 Java 源码..." javac --release 8 -parameters \ -classpath "$PLATFORM" \ -sourcepath "$GEN" \ -d "$CLASSES" \ $SRC_FILES 2>&1 echo " 编译完成" # ---- Step 5: 转换为 DEX ---- echo "[5/8] 转换为 DEX..." CLASS_FILES=$(find "$CLASSES" -name "*.class" | tr '\n' ' ') "$D8" \ --output "$DEX" \ --lib "$PLATFORM" \ $CLASS_FILES 2>&1 echo " DEX 生成完成: $DEX/classes.dex" # ---- Step 6: 打包资源 + Manifest + Assets ---- echo "[6/8] 打包 APK (资源 + Manifest + Assets)..." rm -f "$OUT/$APK_BASENAME-unsigned.apk" "$OUT/$APK_BASENAME-aligned.apk" "$OUT/$APK_BASENAME-signed.apk" "$AAPT" package -f \ -M "$SRC/AndroidManifest.xml" \ -S "$SRC/res" \ -A "$SRC/assets" \ -I "$PLATFORM" \ -F "$OUT/$APK_BASENAME-unsigned.apk" # ---- Step 7: 添加 DEX 和 Native 库 ---- echo "[7/8] 添加 DEX 和 Native 库到 APK..." # 添加 classes.dex cd "$DEX" zip -j "$OUT/$APK_BASENAME-unsigned.apk" classes.dex cd "$PROJECT" # 构建 SeetaFace6 JNI 桥(官方 Android 库就位后自动启用) FACE_LIB_DIR="$SRC/jniLibs/armeabi-v7a" FACE_JNI_SOURCE="$SRC/cpp/face_engine_jni.cpp" FACE_JNI_OUTPUT="$FACE_LIB_DIR/libxingyuan_face.so" FACE_REQUIRED_LIBS=( libTenniS.so libSeetaAuthorize.so libSeetaFaceDetector600.so libSeetaFaceLandmarker600.so libSeetaFaceRecognizer600.so ) FACE_LIBS_READY=true for library in "${FACE_REQUIRED_LIBS[@]}"; do if [ ! -f "$FACE_LIB_DIR/$library" ]; then FACE_LIBS_READY=false fi done if [ "$FACE_LIBS_READY" = true ] && [ -x "$ARM_CLANGXX" ]; then echo " 编译 SeetaFace6 JNI 桥..." "$ARM_CLANGXX" \ --sysroot="$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot" \ -std=c++11 -O2 -fPIC -shared -static-libstdc++ \ -I"$SRC/cpp/include" \ "$FACE_JNI_SOURCE" \ -L"$FACE_LIB_DIR" \ -lSeetaFaceDetector600 \ -lSeetaFaceLandmarker600 \ -lSeetaFaceRecognizer600 \ -lSeetaAuthorize \ -lTenniS \ -llog -lz -ldl -lm \ -o "$FACE_JNI_OUTPUT" else rm -f "$FACE_JNI_OUTPUT" echo " ⚠ SeetaFace6 官方 Android 动态库尚未齐备,人脸 JNI 暂不打包" fi # 添加 native 库 (APK 中需要 lib// 结构) NATIVE_STAGING="$BUILD/native_staging" rm -rf "$NATIVE_STAGING" mkdir -p "$NATIVE_STAGING/lib/armeabi-v7a" find "$SRC/jniLibs/armeabi-v7a" -maxdepth 1 -type f -name "*.so" -exec cp {} "$NATIVE_STAGING/lib/armeabi-v7a/" \; cd "$NATIVE_STAGING" zip -r "$OUT/$APK_BASENAME-unsigned.apk" lib/ cd "$PROJECT" echo " APK 打包完成" # ---- Step 8: Zipalign + 签名 ---- echo "[8/8] Zipalign 并签名..." # Zipalign "$ZIPALIGN" -f 4 "$OUT/$APK_BASENAME-unsigned.apk" "$OUT/$APK_BASENAME-aligned.apk" # 签名 "$APKSIGNER" sign \ --ks "$KEYSTORE" \ --ks-pass "pass:$STORE_PASS" \ --ks-key-alias "$KEY_ALIAS" \ --key-pass "pass:$KEY_PASS" \ --out "$OUT/$APK_BASENAME-signed.apk" \ "$OUT/$APK_BASENAME-aligned.apk" # 验证签名 "$APKSIGNER" verify --verbose "$OUT/$APK_BASENAME-signed.apk" 2>&1 # ---- 完成 ---- echo "" echo "==========================================" echo " ✅ 编译成功!" echo "==========================================" echo "" ls -lh "$OUT/$APK_BASENAME-signed.apk" echo "" echo "输出文件: $OUT/$APK_BASENAME-signed.apk" echo "" echo "安装命令:" echo " adb install -r $OUT/$APK_BASENAME-signed.apk"