Я попытался выполнить файл .so (сборка файла golang для Android) на Android через flutter dart FFI. Все работает нормально, когда я делаю то же самое с файлом .dll в Windows.
FFI завершает работу с ошибкой:
E/flutter (12713): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s): Failed to load dynamic library 'assets/lib.so': dlopen failed: library "assets/lib.so" not found
E/flutter (12713): #0 _open (dart:ffi-patch/ffi_dynamic_library_patch.dart:11:43)
E/flutter (12713): #1 new DynamicLibrary.open (dart:ffi-patch/ffi_dynamic_library_patch.dart:22:12)
E/flutter (12713): #2 dylib (package:proof2/fficheck.dart:7:34)
E/flutter (12713): #3 dylib (package:proof2/fficheck.dart)
E/flutter (12713): #4 runServer (package:proof2/fficheck.dart:10:5)
E/flutter (12713): #5 runServer (package:proof2/fficheck.dart)
E/flutter (12713): #6 main (package:proof2/main.dart:11:3)
E/flutter (12713): #7 _runMain.<anonymous closure> (dart:ui/hooks.dart:301:23)
E/flutter (12713): #8 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
E/flutter (12713): #9 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
Фрагмент кода. lib находится в каталоге /assets/
import 'dart:ffi' as ffi;
import 'package:ffi/ffi.dart';
import 'package:ffi/src/utf8.dart';
typedef run_server = ffi.Pointer<Utf8> Function(); // FFI fn signature
typedef RunServer = ffi.Pointer<Utf8> Function(); // Dart fn signature
final dylib = ffi.DynamicLibrary.open('assets/lib.so');
final RunServer runServer =
dylib.lookup<ffi.NativeFunction<run_server>>('RunServer').asFunction();
В Android вы не можете получить доступ к ресурсам как к обычному файлу, как в Windows, поскольку они упакованы в двоичной форме.
Чтобы связать готовую стороннюю библиотеку, вы можете добавить файлы .so в android/src/main/jniLibs/[abi]. Тогда вы сможете загрузить библиотеку через DynamicLibrary.open('libfoo.so'). Это задокументировано здесь.
Вам необходимо предоставить библиотеку для каждого ABI, поэтому в результате структура вашего проекта должна выглядеть следующим образом.
.
└── android/
└── src/
└── main/
└── jniLibs/
├── arm64-v8a/
│ └── libfoo.so
├── armeabi-v7a/
│ └── libfoo.so
├── x86/
│ └── libfoo.so
└── x86_64/
└── libfoo.so
В противном случае, как упомянул Ричард Хип, вы можете скопировать .so из ресурсов в хранилище приложения в виде файла с помощью следующего фрагмента кода.
Но я думаю, что позволить Gradle управлять библиотеками через jniLibs — меньшая головная боль, чем самостоятельно решать, какие файлы .so загружать для каждого ABI.
Future<File> getFileFromAssets(String assetPath, String filename) async {
final Directory docDir = await getApplicationDocumentsDirectory();
final String localPath = docDir.path;
final String targetFilePath = '$localPath/$filename';
File targetFile = File(targetFilePath);
final asset = await rootBundle.load(assetPath);
final buffer = asset.buffer;
return targetFile.writeAsBytes(
buffer.asUint8List(asset.offsetInBytes, asset.lengthInBytes));
}
Папка ресурсов не будет доступна во время выполнения. Вы можете загрузить это из ресурсов, сохранить во временный файл, а затем
openэто сделать. Или следуйте инструкциям здесь, чтобы добавить so в gradle.