Flutter 应用加载动态库失败

Posted

技术标签:

【中文标题】Flutter 应用加载动态库失败【英文标题】:Failed to load dynamic library in Flutter app 【发布时间】:2021-04-09 13:57:04 【问题描述】:

我在 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 控制台的 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 中的错误吗?这些设备加载动态库是否与其他设备不同? libraster.so在某些场景下是不是真的没有和APK打包?

【问题讨论】:

【参考方案1】:

此处报告了类似问题:https://github.com/simolus3/moor/issues/895

建议的解决方法是针对这种情况创建一个调用 System.loadLibrary() 的 Flutter 插件

【讨论】:

谢谢,是的,这是我最终采用的解决方案,正如 Dart 团队成员所建议的那样:github.com/flutter/flutter/issues/73318#issuecomment-754598119【参考方案2】:

我知道这是一个老问题,但我最近遇到了这个问题,我通过在 pub spec.yaml 中添加 sqlite3_flutter_libs 作为依赖项解决了这个问题

dependencies:
   moor: ^4.3.0
   sqlite3_flutter_libs: ^0.4.2 

【讨论】:

【参考方案3】:

感谢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();

【讨论】:

以上是关于Flutter 应用加载动态库失败的主要内容,如果未能解决你的问题,请参考以下文章

LoadLibrary加载动态库失败

golang编译so动态库加载失败

无法加载动态库“libtensorflowlite_c.so”:dlopen 失败:找不到库“libtensorflowlite_c.so”

无法加载 DLL“rasapi32.dll”: 动态链接库(DLL)初始化例程失败。的处理备注方案

电脑里的动态连接库是啥意思?如果打开文件经常与它连接不上是啥回事该怎么办?

插件之dlopen/dlsym/dlclose 加载动态链接库