1. 第三方native library無法加載
由於我們的 flutter
應用中要集成第三方的 sdk
來實現一些特殊功能,該 sdk
中集成了一些 native library
,而且只有 32位
的。目前在 flutter
的 android
工程中添加了這些 library
, 如下圖所示:
隨后在 android
工程的 build.gradle
文件中手動指定 jniLibs
目錄的路徑,如下圖所示:
現在正常在 flutter 工程中執行 debug 啟動工程,如下圖所示:
點擊界面按鈕,調用第三方 native library
后立馬出現報錯,隨后 App
閃退。報錯內容如下:
通過日志可以看出來,我們前面添加的 native library
沒有被加載。
當時首先懷疑的是打包時是否將 native library
有打包到 apk
中,所以采用 android studio
的 analyze apk
命令分析,發現確實是已經打包到 apk
文件中了。如下圖所示:
仔細回顧上面的日志,不難發現,沒有被加載的原因是因為它尋找 library
的路徑不對,根本沒有包含我們需要的 armeabi-v7a
這個目錄。如下圖所示,加載的路徑基本上都是 arm64
相關的目錄,很顯然是因為我的真機是 arm64
架構的,所以可能導致這個問題。
關於這個問題,在文章 https://medium.com/codechai/flutter-app-couldnt-find-libflutter-so-c95ad81cbccd 中有詳細的說明。大體情況如下:
當apk安裝時,首先會標記當前應用是32位的還是64位的,這決定了后面在哪個目錄尋找本地庫。
- 首先在
lib/arm64-v8a
目錄中尋找是否有native libraray
,如果有就標記為64位
。(在上面的情況中,arm64-v8a文件夾中是包含有 libflutter.so 的,所以該應用被標記為 64 位)- 如果沒有在 lib/arm64-v8a 中找到 native libraray,則隨后在
lib/armeabi-v7a
和lib/armeabi
中尋找,有則標記為32位
。- 如果在上述目錄中都沒有找到
native library
則說明當前應用沒有使用本地庫,對 32 位還是 64位 並不敏感。此時會默認標記為64位
。
此時可以在網上找到的大多數回答是在 build.gradle
文件中指定 ndk
的 abiFilter
,如下圖所示:
這個配置的意思是指定一個 filter ,讓
Gradle
在打包 apk 時只添加armeabi-v7a
下的library
,其它的都不添加。
根據上面的尋找 library 的規則,這么做確實只保證armeabi-v7a
的庫被加載到。
添加這項配置后,再繼續執行 debug
,又拋出了 couldn't find "libflutter.so"
這個報錯信息。詳細內容如下所示:
E/FlutterLoader( 8399): java.util.concurrent.ExecutionException: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.dm.pcbd-A-gAqiQOV9EjRIg7TZRvKw==/base.apk"],nativeLibraryDirectories=[/data/app/com.dm.pcbd-A-gAqiQOV9EjRIg7TZRvKw==/lib/arm, /data/app/com.dm.pcbd-A-gAqiQOV9EjRIg7TZRvKw==/base.apk!/lib/armeabi-v7a, /system/lib, /product/lib]]] couldn't find "libflutter.so"
采用 analyze apk 查看了下生成的 apk
包,發現確實沒有將 libflutter.so
打包到 apk 中,如下圖所示:
翻了 flutter
的很多 issue
后發現有很多類似的問題,例如這個 issue
https://github.com/flutter/flutter/issues/29710
2. debug下 libflutter.so 無法加載
根據 issue
https://github.com/flutter/flutter/issues/29710 的描述,只要 armeabi-v7a
下添加了其它庫,就會出現 libflutter.so
打包不進去的問題。
經過多次嘗試后,發現使用 flutter build
命令行生成出來的包中是有 libflutter.so
的,具體命令如下:
// 生成debug包
flutter build apk --debug
// 生成release包
flutter build apk --release
// 安裝軟件包(在生成debug或release包之后運行)
flutter install
build
生成的 debug
包中包含 libflutter.so
build
生成的 release
包中也包含 libflutter.so
但是直接使用 Android Studio
上的 debug
運行時就是不打包 libflutter.so
到 armeabi-v7a
中。
Android Studio
上的debug
等同於命令行flutter run --debug -v
說實話,到這一步時覺得 flutter
還是不太成熟,差點想棄坑了。
仔細翻閱了 https://github.com/flutter/flutter/issues/29710 這個issue,發現有網友提到命令行手動指定 target-platform
來強制指定目標平台為 arm
32位,例如:
flutter build apk --debug --target-platform=android-arm
flutter run --use-application-binary=build/app/outputs/apk/debug/app-debug.apk
但是 target-platform
這個參數在新版本的 flutter
中不能直接用在 flutter run
上(即上述的 debug 上),只能用在 flutter build
上。那這就導致平常調試時很不方便了,不能直接按 debug
按鈕啟動啟用,得用命令行手動啟動,太不方便了。
在上面的 issue
中,又有網友提到直接在 gradle
中配置 target-platform
。具體是在 settings.gradle
文件中添加配置,如下所示:
具體添加的代碼如下:
gradle.beforeProject({ project->
if (project.hasProperty("target-platform") && !project.getProperty("target-platform").split(",").contains("android-arm")) {
project.setProperty("target-platform", "android-arm")
}
})
添加這段配置后,就可以 正常使用
界面上的 debug
按鈕了,其生成的 apk
包中 armeabi-v7a
文件夾下確實是包含了 libflutter.so
。
3. Release下出現閃退問題
上面解決 debug
正常后,為了看下 release
下的效果,隨即用命令行 flutter build apk --release
生成了 release
包,安裝后點擊按鈕訪問第三方sdk內容時出現閃退。
這就出現了 release 下和 debug 下行為不一致的問題
這種直接安裝 apk
的方式,出現閃退或報錯等問題,在 Android Studio
中看不到任何日志,非常不利於問題的排查。
這里可以采用 flutter run --release -v
的方式來啟動 release
包,這樣在控制台至少可以看到一些日志。flutter run --release -v
在界面上也可以配置出來,如下圖所示:
再點擊界面上的 Run
或 debug
按鈕就可以啟動這個 release
包了。(此時 debug 或 Run 按鈕不影響運行方式)
通過這種方式運行后,得到 Release包閃退
的報錯信息如下:
E/AndroidRuntime(22800): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.dm.pcbd/com.micropattern.sdk.ext.MPFaceQualityDetectActivity}: android.content.res.Resources$NotFoundException: Resource ID #0x0
E/AndroidRuntime(22800): at .................省略一些無關的日志
E/AndroidRuntime(22800): Caused by: android.content.res.Resources$NotFoundException: Resource ID #0x0
E/AndroidRuntime(22800): at android.content.res.ResourcesImpl.getValue(ResourcesImpl.java:292)
E/AndroidRuntime(22800): at android.content.res.Resources.loadXmlResourceParser(Resources.java:2390)
........... 省略一些無關的日志
com.android.internal.policy.HwPhoneWindow.setContentView(HwPhoneWindow.java:342)
E/AndroidRuntime(22800): at android.app.Activity.setContentView(Activity.java:2941)
E/AndroidRuntime(22800): at com.micropattern.sdk.ext.MPFaceQualityDetectActivity.onCreate(Unknown Source:36)
E/AndroidRuntime(22800): at android.app.Activity.performCreate(Activity.java:7458)
E/AndroidRuntime(22800): at android.app.Activity.performCreate(Activity.java:7448)
E/AndroidRuntime(22800): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1286)
E/AndroidRuntime(22800): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3409)
E/AndroidRuntime(22800): ... 11 more
出現這個問題,猜測是release打包時 shrink
壓縮導致的問題。首先考慮到可能是 flutter官方文檔中提到的 Shrinking your code with R8
導致的問題,遂在命令行打包時嘗試關閉 shrinking
,命令如下所示:
flutter build apk --release --no-shrink
不過這個包打出來后,還是存在上面的資源問題。
上面那個是 android
中獲取資源的問題,那么有可能是 Gradle
打包時出現的問題,所以考慮應該直接在 Gradle
中關閉 shrink
。具體操作如下,在 build.gradle
中添加如下配置
關於shrink,官方文檔中有詳細介紹: https://developer.android.com/studio/build/shrink-code?utm_source=android-studio#shrink-resources
到這里,再 Release
后就可以正常使用本地庫了。
4. 總結
解決這個問題耗了一整天,基於上都是停留在各種嘗試中。經過這一番折騰,確實了解到 flutter
這個坑有些大.......
- 第一次為了解決
libflutter.so
無法加載的問題,甚至嘗試過手動將libflutter.so
拷貝到armeabi-v7a
文件夾下。這樣確實解決了debug
下的問題,但是最后做出來的release
包一直卡在空白界面。在這里浪費了很多時間。 - 目前
flutter
並不是特別成熟,關於這個問題在issue
中說法有很多,這也導致了各種嘗試,花費了很多時間。
經過這次折騰,也確實了解到了很多東西,例如:
- ndk abiFilter 配置
- 對 native library 加載機制有些許了解
- flutter工程和android工程的關系也終於弄明白了一些
5. 關於作者
作者是一個熱愛學習、開源、分享,傳播正能量,頭發還很多的程序員-。-
熱烈歡迎大家關注、點贊、評論交流!