准备工作
Android Studio下载的NDK是最新版,已不支持armabi和mips架构,所以想支持生成所有cpu平台,需要下载r15或之前版本ndk,可通过官网下载旧版NDK
如果有现成的so,可跳过第一步制作生成so。
一,制作生成 .so
这里我一次同时做两个,方便之后互相调用
新建两个java类,
NativeMethodCall 生成目标so
CallNativeMethod 生成用来调用目标so的中间so
public class CallNativeMethod {
public static native String callNativeMethodFromSO();
public static native int callNativeMethodFromSOSub(int a,int b);;
}
public class NativeMethodCall {
public static native String helloFormC();
public static native int sub(int a,int b);
}
注意所属的包名,之后生成的so方法名是基于 "包名_类名_方法名” 的。
创建好之后,打开Terminal
使用cd 命令到当前项目的java目录
再使用 javah + 包名类名 命令,生成 .h文件。
我这里将两个文件都生成了.h
之后在main中新建jni目录,并将 .h 文件都拖到 jni目录
之后在jni目录创建几个文件,分别是:
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := NativeMethod #对应生成libcreate.so库
LOCAL_SRC_FILES := NativeMethod.c #create.c源文件的名字
include $(BUILD_SHARED_LIBRARY) #构建动态链接库
include $(CLEAR_VARS)
LOCAL_MODULE :=CallNativeMethod
LOCAL_SRC_FILES :=CallNativeMethod.c
LOCAL_SHARED_LIBRARIES := NativeMethod
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
Application.mk:
APP_ABI := all
NativeMethod.c:
(注意这个里面的很长的方法名,可以拷贝上面自动生成的.h中对应的名称)
(#include <com_wwb_test_testcallnativelibrary_NativeMethodCall.h> 这句也要改成自己的.h)
#include <stdio.h>
#include <stdlib.h>
#include <com_wwb_test_testcallnativelibrary_NativeMethodCall.h>
JNIEXPORT jstring JNICALL Java_com_wwb_test_testcallnativelibrary_NativeMethodCall_helloFormC
(JNIEnv * env, jclass jobject){
return (*env) -> NewStringUTF(env,"hello form C");
};
JNIEXPORT jint JNICALL Java_com_wwb_test_testcallnativelibrary_NativeMethodCall_sub
(JNIEnv *env, jclass jobject, jint a, jint b){
return a+b;
};
CallNativeMethod.c:
(注意这个里面的很长的方法名,可以拷贝上面自动生成的.h中对应的名称)
(#include <com_wwb_test_testcallnativelibrary_NativeMethodCall.h> 这句也要改成自己的.h)
#include <stdio.h>
#include <stdlib.h>
#include "string.h"
#include "jni.h"
#include "dlfcn.h"
#include <fcntl.h>
#include <com_wwb_test_testcallnativelibrary_CallNativeMethod.h>
void * filehandle = NULL;
jstring (*getResult)(JNIEnv *, jobject) =NULL;
JNIEXPORT jstring JNICALL Java_com_wwb_test_testcallnativelibrary_CallNativeMethod_callNativeMethodFromSO
(JNIEnv * env, jclass thiz){
jstring result = "";
filehandle = dlopen("libNativeMethod.so", RTLD_LAZY);
if (filehandle) {
getResult = (jstring (*)(JNIEnv *, jobject)) dlsym(filehandle,"Java_com_wwb_test_testcallnativelibrary_NativeMethodCall_helloFormC");
if (getResult)
result = getResult(env, thiz);
dlclose(filehandle);
filehandle = NULL;
}
return result;
};
jint (*getSubResult)(JNIEnv *, jobject, jint, jint) =NULL;
JNIEXPORT jint JNICALL Java_com_wwb_test_testcallnativelibrary_CallNativeMethod_callNativeMethodFromSOSub
(JNIEnv * env, jclass thiz, jint a, jint b){
jint result = 0;
filehandle = dlopen("libNativeMethod.so", RTLD_LAZY);
if (filehandle) {
getSubResult = (jint (*)(JNIEnv *, jobject, jint, jint)) dlsym(filehandle,"Java_com_wwb_test_testcallnativelibrary_NativeMethodCall_sub");
if (getSubResult)
result = getSubResult(env, thiz, a, b);
dlclose(filehandle);
filehandle = NULL;
}
return result;
};
其中CallNativeMethod是在调用NativeMethod里面的对应方法。
随后,回到Terminal,cd命令到jni目录下
使用ndk目录的 ndk-build 命令,如下图,是我的ndk位置,之后回车
完成后,可以在生成的libs目录中看到,各种架构的.so。
二,调用.so
在app目录下的build.gradle中添加如下代码
sourceSets {
main() {
jniLibs.srcDirs = ['src/main/libs']
jni.srcDirs = [] //This prevents the auto generation of Android.mk
}
}
修改MainActivity.java
public class MainActivity extends AppCompatActivity {
//直接加载调用最终so
// static {
// //加载so库
// System.loadLibrary("NativeMethod");
// }
//加载调用中间so
static {
//加载so库
System.loadLibrary("CallNativeMethod");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//调用最终so方法,返回结果
// Log.e("CallNative",""+NativeMethodCall.helloFormC());
// Log.e("CallNative",""+NativeMethodCall.sub(1,2));
//调用中间so方法, 中间so再去调用最终so,返回结果
Log.e("CallNative",""+CallNativeMethod.callNativeMethodFromSO());
Log.e("CallNative","1 + 2 = "+CallNativeMethod.callNativeMethodFromSOSub(1,2));
}
}
其中注释了的是直接调用最终so的代码。
未注释的是调用中间so,中间so调用最终so的方法详见CallNativeMethod.c中的实现。
在手机或者模拟器里运行一下,可以看到打印结果
实现了,java调用so,so调用另一个so的方法。
如果.so是现成的,那就直接放到src/main/libs目录下就可以了
只需添加
和java的调用即可。
更多类型的库调用jar,so,aar,可以借鉴 android.mk中引用第三方库的方法