Android如何调用so库
官方推荐使用NDK工具,通过CMake的方式创建动态库。不过,一些简单的库,也是可以直接使用命令行生成的。
android 默认使用clang编译C/C++。sdk中clang在:%ANDROID_SDK%\ndk\21.0.6113669\toolchains\llvm\prebuilt\windows-x86_64\bin
使用clang生成动态库时,需要指定target
,如x86的模拟器下:
1
clang -target=i686-linux-android26-clang -shared xxx.c -o libxxx.so
示例
创建一个外部so库
hello.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "hello.h" int main(int total, char *args[]) { if (total == 1) { printf("%s", "argument absent"); return 1; } char* str = args[1]; size_t len = strlen(str); //char* result = (char *)malloc(len); char result[len]; convert(str, len, result); printf("result: %s\n", result); //free(result); return 0; } void convert(char *source, int len, char *result) { for (int i = 0; i < len; i++) { result[len - 1 - i] = source[i]; } result[len] = '\0'; }
hello.h
1
void convert(char*, int, char*);
使用clang创建so库
1
clang -target=i686-linux-android26-clang -shared hello.c -o libhello.so
JNI调用该so库
test.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#include <jni.h> #include <string.h> #include "hello.h" JNIEXPORT jstring JNICALL Java_com_ejin_libso_MainActivity_convert(JNIEnv *env, jobject instance, jstring s_) { const char *s = (*env)->GetStringUTFChars(env, s_, 0); size_t len = strlen(s); char result[len]; convert(s, len, result); return (*env)->NewStringUTF(env, result); }
将hello.h/libhello.so 放到test.c的同目录下之后,生成libtest.so
1
clang -target=i686-linux-android26-clang -shared test.c libhello.so -o libtest.so
修改gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
android { ... defaultConfig { ... ndk { abiFilters 'x86' } } ... sourceSets { main { jniLibs.srcDirs = ['libs'] } } }
在libs下创建x86文件夹,并将libtest.so/libhello.so拷贝进去。
编辑android 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class MainActivity : AppCompatActivity() { companion object { init { System.loadLibrary("test") } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) tv.text = convert("123456") } external fun convert(s: String): String }
运行之后,发现屏幕上显示的是“654321”,表示动态库的编译调用是成功的。
Flutter如何调用so库
dart是支持调用so库的,主要的api在dart:ffi
和package:ffi/ffi.dart
下面。调用流程如下:
生成so库。如何生成so库,在上一篇已经有详细说明。
so库在项目中的位置。
原生调用so库时,so库怎么放置,在flutter项目中也是一致的。以android为例:
首先,修改build.gradle, 设置jni libs:
1 2 3 4 5 6 7
android { sourceSets { main { jniLibs.srcDirs = ['libs'] } } }
在libs下创建${abi}文件夹。如模拟器一般是x86架构,所以在模拟器上创建x86文件夹,并将生成的so库拷贝进来。
与android不同的是,不需要通过jni的方式间接调用。如上面的两个so库:libhello.so、libtest.so. 这里只需要libhello.so.
接着,就可以使用dart代码直接调用了。
导入第三方库
1
ffi: ^0.1.3
dart调用库示例
1 2 3 4 5 6 7 8 9 10 11 12 13
import 'dart:ffi' as ffi; import 'package:ffi/ffi.dart'; final solib = ffi.DynamicLibrary.open("libhello.so"); typedef ConvertNative = ffi.Void Function(ffi.Pointer<Utf8>, ffi.Int32, ffi.Pointer<Utf8>); typedef Convert = void Function(ffi.Pointer<Utf8>, int, ffi.Pointer<Utf8>); final Convert convert = solib .lookup<ffi.NativeFunction<ConvertNative>>('convert') .asFunction();
ffi.Pointer
代表 char*, ffi.Int32 代表 int 更多资料,参考dart官网:C interop using dart:ffi
使用示例
1 2 3 4 5 6 7
var source = Utf8.toUtf8("ABCDEF"); var len = Utf8.strlen(source); var p = allocate<Utf8>(count: len); convert(source, len, p); //txt: FEDCBA var txt = Utf8.fromUtf8(p); free(p);