安卓端flutter通过ffi调用so动态库

背景

自从抖音上线web版后不久,旧版的无水印接口就开始失效了,需要找新的官方接口。

但是新的官方接口开始接入x-bogus算法,需要用golang的一个js虚拟机来生成x-bogus签名,这里就需要将golang打包到flutter中使用。

资料

  1. embed库可以将资源文件一起打包成二进制,比如js、image等。
  2. import "C"作用于将golang函数导出给外部调用,部分入参和返回的数据类型也要用这个库进行转换,比如字符串。
  3. github.com/dop251/goja这个库用于运行js文件,作为一个js虚拟机。

编码

package main
import "C"

func main() {}

//export hello
func hello(name *C.char) *C.char {
    msg := "hello " + name
	return C.CString(msg)
}

打包脚本

演示系统是windows环境

set ANDROID_NDK_HOME=D:\Android\SDK\ndk\22.0.7026061

set GOARCH=arm
set GOOS=android
set CGO_ENABLED=1
set CC=%ANDROID_NDK_HOME%\toolchains\llvm\prebuilt\windows-x86_64\bin\armv7a-linux-androideabi21-clang
go build -ldflags "-w -s" -buildmode=c-shared -o ./bin/armeabi-v7a/libhello.so ./main.go
echo Build armeabi-v7a finish

set GOARCH=arm64
set GOOS=android
set CGO_ENABLED=1
set CC=%ANDROID_NDK_HOME%\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android21-clang
go build -ldflags "-w -s" -buildmode=c-shared -o ./bin/arm64-v8a/libhello.so ./main.go
echo Build arm64-v8a finish

set GOARCH=amd64
set GOOS=android
set CGO_ENABLED=1
set CC=%ANDROID_NDK_HOME%\toolchains\llvm\prebuilt\windows-x86_64\bin\x86_64-linux-android24-clang
go build -ldflags "-w -s" -buildmode=c-shared -o ./bin/x86_64/libhello.so ./main.go
echo Build x86_64 finish


set GOARCH=386
set GOOS=android
set CGO_ENABLED=1
set CC=%ANDROID_NDK_HOME%\toolchains\llvm\prebuilt\windows-x86_64\bin\i686-linux-android24-clang
go build -ldflags "-w -s" -buildmode=c-shared -o ./bin/x86/libhello.so ./main.go
echo Build x86 finish
  1. 修改ANDROID_NDK_HOME变量为你的ndk目录
  2. 实际上如果没有其他平台兼容打算的话,比如不去兼容一些老设备,这时只需要编译arm64-v8a即可。

执行完脚本后会在bin目录中生成对应平台的so动态库文件。

flutter项目配置

  1. 添加依赖dart pub add -d ffigen

  2. golang代码生成的.h头文件复制到flutter工程目录中,可以自由放置。比如我将其放置在lib/ffi目录。

  3. golang代码生成的bin目录下的所有内容复制到flutter工程的android/libs目录中。然后在app/bild.gradle文件中加入以下代码

    // 放入android节点下
    sourceSets {
        main {
            jniLibs.srcDirs = ["libs"]
        }
    }
  4. 修改pubspec.yaml,增加以下配置

    ffigen:
    output: 'lib/ffi/libhello.h.dart' # 根据上述的路径填入
    headers:
        entry-points:
        - 'lib/ffi/libhello.h' # 根据上述的路径填入
  5. 运行dart run ffigen,此时会生成一个libhello.h.dart文件。接下来编写dart快捷代码

    新建dart文件lib/ffi/HelloFFi.dart

    import 'dart:ffi';
    import 'package:ffi/ffi.dart' as ffi;
    import './hello.h.dart';
    
    class HelloFFi {
        HelloFFi._();
    
        final _native = NativeLibrary(DynamicLibrary.open('libhello.so'));
    
        static final HelloFFi _instant = HelloFFi._();
    
        factory HelloFFi() => _instant;
    
        /// 和golang代码同名的方法名
        String hello(String name) {
            // input
            final raw = name.codeUnits;
            final buffer = ffi.malloc.allocate<Int8>(raw.length + 1);
            buffer.asTypedList(raw.length + 1)
            ..setAll(0, raw)
            ..[raw.length] = 0;
            return _native.hello(buffer).cast<ffi.Utf8>().toDartString();
        }
    }

    踩坑

  6. so文件名必须以lib开头,否则release模式下会找不到so文件,而debug模式下正常

  7. 使用//export导出函数时,//不能有空格。


安卓端flutter通过ffi调用so动态库
http://blog.icy8.cn/posts/43743/
作者
icy8
发布于
2023年4月5日
许可协议