Flutter 应用加载动态库失败
Failed to load dynamic library in Flutter app
我在 Google Play 商店上有一个正在生产中的 Flutter 应用程序,其中包括一个使用 NDK 构建并在运行时加载的本机动态库(我称之为 libraster.so
)。在大多数设备上,这个库都存在并且可以正常加载。但在某些设备上,以下 ArgumentError
会在运行时发生 Invalid argument(s): Failed to load dynamic library (dlopen failed: library "libraster.so" not found)
.
我认为有问题的设备是 ARM 设备。该应用未在应用模块的 build.gradle
文件中指定任何 abiFilter
。
使用 Google Play Console 的 App Bundle Explorer,我可以下载将分发到受影响设备的 APK,它们包含正常的 libraster.so
。
根据我的错误日志,目前受影响的设备是:
Model
Name
Android version
SM-G928F
Samsung Galaxy S6 Edge+
6.0.1
SM-J500M
Samsung Galaxy J5
6.0.1
SM-J710GN
Samsung Galaxy J7 2016
6.0.1
SM-T110
Samsung Galaxy Tab 3 Lite 7.0
4.2.2
SM-T111M
Samsung Galaxy Tab 3 Lite 7.0
4.2.2
GT-I8262
Samsung Galaxy Core Duos
4.1.2
GT-I8552
Samsung Galaxy Win Duos
4.1.2
GT-I8552B
Samsung Galaxy Win Duos
4.1.2
GT-I9082L
Samsung Galaxy Grand Duos
4.2.2
GT-I9300
Samsung Galaxy S III
4.1.2
GT-N8000
Samsung Galaxy Note 10.1
4.1.2
GT-N8010
Samsung Galaxy Note 10.1
4.1.2
GT-P3110
Samsung Galaxy Tab 2 7.0
4.1.2
GT-P5110
Samsung Galaxy Tab 2 10.1
4.2.2
SO-03E
Sony Xperia Tablet Z
4.1.2
B1-A71
Acer Iconia Tab B1-A71
4.1.2
F-01F
Fujitsu Arrows NX F-01F
4.2.2
ME173X
Asus Memo Pad HD7
4.2.2
主要是 Android 4.1.2、4.2.2 和 6.0.1 设备。
这是我的应用模块 build.gradle
:
的简化版本
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
applicationId "com.example.package"
minSdkVersion 16
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
externalNativeBuild {
cmake {
arguments "-DCOMPILE_TESTS:BOOL=OFF"
}
}
// Maintains debug symbols
packagingOptions {
doNotStrip '**.so'
}
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
ndk {
debugSymbolLevel = 'FULL'
}
}
}
externalNativeBuild {
cmake {
version "3.19.2"
path "path/to/CMakeLists.txt"
}
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'androidx.multidex:multidex:2.0.1'
}
apply plugin: 'com.google.gms.google-services'
和proguard-rules.pro
:
#Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-dontwarn io.flutter.embedding.**
-keep class com.example.package.** { *; }
使用的Flutter版本稳定1.22.5
.
这是 Flutter 的 bug 吗?这些设备加载动态库的方式是否与其他设备不同? libraster.so
只是在某些情况下实际上没有与 APK 打包在一起吗?
此处报告了类似的问题:https://github.com/simolus3/moor/issues/895
对于这种情况,建议的修复方法是创建一个调用 System.loadLibrary()
的 Flutter 插件
我知道这是一个老问题,但我最近 运行 解决了这个问题,我通过在 pub spec.yaml
中添加 sqlite3_flutter_libs 作为依赖项解决了这个问题
dependencies:
moor: ^4.3.0
sqlite3_flutter_libs: ^0.4.2
感谢 knaeckeKami,这里有一个解决方案。
首先,您需要创建一个插件:
package <your_package>
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
class Plugin: FlutterPlugin, MethodCallHandler {
companion object {
const val TAG = <YOUR_TAG>
}
private lateinit var channel: MethodChannel
private lateinit var context: Context
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
channel = MethodChannel(flutterPluginBinding.binaryMessenger, <CHANNEL_NAME>)
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getNativeLibraryDirectory") {
val applicationInfo: ApplicationInfo = context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA)
if (applicationInfo != null) {
result.success(applicationInfo.nativeLibraryDir)
} else {
result.success(null)
}
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
然后您可以使用 _getAndroidDynamicLibrary(String libraryName)
而不是 DynamicLibrary.open(libraryName)
来加载您的库:
Future<DynamicLibrary> _getAndroidDynamicLibrary(String libraryName) async {
try {
return DynamicLibrary.open(libraryName);
} catch (_) {
try {
final String? nativeLibraryDirectory = await _getNativeLibraryDirectory();
return DynamicLibrary.open('$nativeLibraryDirectory/$libraryName');
} catch (_) {
try {
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
final String packageName = packageInfo.packageName;
return DynamicLibrary.open('/data/data/$packageName/lib/$libraryName');
} catch (_) {
rethrow;
}
}
}
}
用法:
Future<DynamicLibrary> _getNativeAppTokenLibrary() async => Platform.isAndroid
? await _getAndroidDynamicLibrary(_libraryName)
: DynamicLibrary.process();
我在 Google Play 商店上有一个正在生产中的 Flutter 应用程序,其中包括一个使用 NDK 构建并在运行时加载的本机动态库(我称之为 libraster.so
)。在大多数设备上,这个库都存在并且可以正常加载。但在某些设备上,以下 ArgumentError
会在运行时发生 Invalid argument(s): Failed to load dynamic library (dlopen failed: library "libraster.so" not found)
.
我认为有问题的设备是 ARM 设备。该应用未在应用模块的 build.gradle
文件中指定任何 abiFilter
。
使用 Google Play Console 的 App Bundle Explorer,我可以下载将分发到受影响设备的 APK,它们包含正常的 libraster.so
。
根据我的错误日志,目前受影响的设备是:
Model | Name | Android version |
---|---|---|
SM-G928F | Samsung Galaxy S6 Edge+ | 6.0.1 |
SM-J500M | Samsung Galaxy J5 | 6.0.1 |
SM-J710GN | Samsung Galaxy J7 2016 | 6.0.1 |
SM-T110 | Samsung Galaxy Tab 3 Lite 7.0 | 4.2.2 |
SM-T111M | Samsung Galaxy Tab 3 Lite 7.0 | 4.2.2 |
GT-I8262 | Samsung Galaxy Core Duos | 4.1.2 |
GT-I8552 | Samsung Galaxy Win Duos | 4.1.2 |
GT-I8552B | Samsung Galaxy Win Duos | 4.1.2 |
GT-I9082L | Samsung Galaxy Grand Duos | 4.2.2 |
GT-I9300 | Samsung Galaxy S III | 4.1.2 |
GT-N8000 | Samsung Galaxy Note 10.1 | 4.1.2 |
GT-N8010 | Samsung Galaxy Note 10.1 | 4.1.2 |
GT-P3110 | Samsung Galaxy Tab 2 7.0 | 4.1.2 |
GT-P5110 | Samsung Galaxy Tab 2 10.1 | 4.2.2 |
SO-03E | Sony Xperia Tablet Z | 4.1.2 |
B1-A71 | Acer Iconia Tab B1-A71 | 4.1.2 |
F-01F | Fujitsu Arrows NX F-01F | 4.2.2 |
ME173X | Asus Memo Pad HD7 | 4.2.2 |
主要是 Android 4.1.2、4.2.2 和 6.0.1 设备。
这是我的应用模块 build.gradle
:
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
applicationId "com.example.package"
minSdkVersion 16
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
externalNativeBuild {
cmake {
arguments "-DCOMPILE_TESTS:BOOL=OFF"
}
}
// Maintains debug symbols
packagingOptions {
doNotStrip '**.so'
}
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
ndk {
debugSymbolLevel = 'FULL'
}
}
}
externalNativeBuild {
cmake {
version "3.19.2"
path "path/to/CMakeLists.txt"
}
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'androidx.multidex:multidex:2.0.1'
}
apply plugin: 'com.google.gms.google-services'
和proguard-rules.pro
:
#Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-dontwarn io.flutter.embedding.**
-keep class com.example.package.** { *; }
使用的Flutter版本稳定1.22.5
.
这是 Flutter 的 bug 吗?这些设备加载动态库的方式是否与其他设备不同? libraster.so
只是在某些情况下实际上没有与 APK 打包在一起吗?
此处报告了类似的问题:https://github.com/simolus3/moor/issues/895
对于这种情况,建议的修复方法是创建一个调用 System.loadLibrary()
的 Flutter 插件我知道这是一个老问题,但我最近 运行 解决了这个问题,我通过在 pub spec.yaml
中添加 sqlite3_flutter_libs 作为依赖项解决了这个问题dependencies:
moor: ^4.3.0
sqlite3_flutter_libs: ^0.4.2
感谢 knaeckeKami,这里有一个解决方案。
首先,您需要创建一个插件:
package <your_package>
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
class Plugin: FlutterPlugin, MethodCallHandler {
companion object {
const val TAG = <YOUR_TAG>
}
private lateinit var channel: MethodChannel
private lateinit var context: Context
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
channel = MethodChannel(flutterPluginBinding.binaryMessenger, <CHANNEL_NAME>)
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getNativeLibraryDirectory") {
val applicationInfo: ApplicationInfo = context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA)
if (applicationInfo != null) {
result.success(applicationInfo.nativeLibraryDir)
} else {
result.success(null)
}
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
然后您可以使用 _getAndroidDynamicLibrary(String libraryName)
而不是 DynamicLibrary.open(libraryName)
来加载您的库:
Future<DynamicLibrary> _getAndroidDynamicLibrary(String libraryName) async {
try {
return DynamicLibrary.open(libraryName);
} catch (_) {
try {
final String? nativeLibraryDirectory = await _getNativeLibraryDirectory();
return DynamicLibrary.open('$nativeLibraryDirectory/$libraryName');
} catch (_) {
try {
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
final String packageName = packageInfo.packageName;
return DynamicLibrary.open('/data/data/$packageName/lib/$libraryName');
} catch (_) {
rethrow;
}
}
}
}
用法:
Future<DynamicLibrary> _getNativeAppTokenLibrary() async => Platform.isAndroid
? await _getAndroidDynamicLibrary(_libraryName)
: DynamicLibrary.process();