[Android] 如何通過 Google Play 16 KB page size 要求

遇到的錯誤是因為 OpenVPN 的啟動機制與 useLegacyPackaging = false 互相衝突。

核心原因分析

  1. useLegacyPackaging = false 的作用:這個設定告訴 Android 建置系統(AGP),不要把 APK 裡面的 .so 檔案解壓縮到手機的 /data/app/…/lib/ 目錄下。它會讓系統直接從 APK 包裡面讀取這些函式庫(為了節省手機空間)。這就是為什麼你用 adb ls 看到目錄是空的。
  2. OpenVPN (ics-openvpn) 的運作方式:Cake-VPN(以及大多數基於 ics-openvpn 的專案)並不是像一般 App 那樣只用 System.loadLibrary。它實際上是把 libovpnexec.so 當作一個可執行檔 (Executable) 來跑。Java 程式碼會嘗試去執行類似這樣的指令:/data/app/…/lib/arm64/libovpnexec.so …。因為你設了 false,檔案不存在於那個路徑上,導致 java.io.IOException,程式崩潰。

解決方案

為了同時修復崩潰問題並通過 Google Play 的 16 KB Page Size 檢查,你需要採取兩步走的策略:

第一步:修復崩潰(改回 True)

你必須允許 Native Library 被解壓縮,因為這個 App 的架構依賴於實體檔案的存在。

修改 build.gradle

Groovy

android {
    packaging {
        jniLibs {
            // 改回 true (或者直接刪除這段,因為預設通常是 true)
            // 這樣 Android 才會把 .so 檔解壓到 /data/app/.../lib 目錄
            useLegacyPackaging = true
        }
    }
    // ... 其他設定
}

做完這步,你的 App 就能正常啟動了,但可能還過不了 16 KB 檢查。接下來看第二步。

第二步:真正解決 16 KB 對齊問題 (Rebuild NDK)

既然我們必須解壓縮 .so 檔(useLegacyPackaging = true),那麼 Google Play 的要求就是:這些解壓縮出來的 ELF (.so) 檔案,其 Segment 必須是 16 KB 對齊的。

這代表你不能只改 build.gradle 的 packaging 設定,你需要重新編譯 C/C++ 原始碼,並加上 Linker flags。

請在你的 build.gradle (Module level) 加入以下設定,強制 NDK 使用 16KB 對齊:

android {
    defaultConfig {
        // ... 其他設定
        
        externalNativeBuild {
            cmake {
                // 這是關鍵:告訴 Linker 使用 16KB (16384 bytes) 的 Page Size
                cppFlags "-Wl,-z,max-page-size=16384"
                arguments "-DANDROID_STL=c++_shared"
            }
            // 如果你是用 ndk-build (Android.mk),則需要用不同的參數,但大多數現在都用 cmake
        }

        ndk {
            abiFilters "arm64-v8a", "x86_64"
        }
    }
}

注意:

如果你的專案不是透過 CMake 編譯(例如直接引用 libs/ 資料夾下的預編譯 .so 檔),那麼你無法透過 Gradle 修改對齊。你必須找到這些 .so 檔的原始碼重新編譯,或者尋找已經支援 16KB Alignment 的第三方庫版本。

驗證方法

修改並重新 Build 出 APK 後,請使用 Android SDK 提供的工具 zipinfo 或 Linux 的 readelf 來檢查。

  1. 解壓 APK,取出裡面的 .so 檔。
  2. 在終端機執行:
    readelf -l libovpnexec.so | grep LOAD
  3. 檢查輸出中的 Align 欄位。如果是 0x4000 (16384),代表成功;如果是 0x1000 (4096),代表失敗。

總結

  1. useLegacyPackaging 改回 true (讓 OpenVPN 找得到檔案)。
  2. externalNativeBuild 加入 -Wl,-z,max-page-size=16384 (讓編譯出來的檔案符合 Google 規範)。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *