1.新建带c++支持的Android工程。demo编译运行无误
2.在CMakeLists.txt中添加,生成.so文件可以拷贝出去供其他工程使用
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
#在add_library之前设置
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp )
3.动态注册native方法名称,这样可以注册其他工程的包路径
采用RegisterNatives的注册jni的方法不需要javah命名生成的jni规则函数的命名方式
RegisterNatives为JNIEnv的成员函数,声明为:
jint (JNICALL *RegisterNatives) (JNIEnv *env, jclass clazz, const JNINativeMethod *methods,jint nMethods);
其对应的取消注册的函数为声明为:
jint (JNICALL *UnregisterNatives) (JNIEnv *env, jclass clazz);
在java中调用System.loadLibrary("somelib");的时候,系统会自动调用jni的函数JNI_OnLoad,
在程序退出的时候,系统卸载“somelib”,会自动调用jni的函数JNI_OnUnload,
所以我们需要在jni的接口文件中重写这两个函数
例子如下:
// native方法
static jstring JNICALL native_get_String(JNIEnv *env, jobject obj, jobject content) {
}
//先定义一个字符串,内容为类名
const char* JNIT_CLASS = "com/mobile/encrypt/api/whitebox/EncryptUtils";
/**
* 注册的native调用的方法
*/
static const JNINativeMethod gMethods[] = {
{"stringFromJNI", "(Landroid/content/Context;)Ljava/lang/String;", (jstring *) native_get_String}
};
/**
* 注册方法到JVM中
* System.loadLibrary会自动调用JNI_OnLoad,在此进行动态注册
*
* @param jvm
* @param reserved
* @return
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
// 防止动态调试
ptrace(PTRACE_TRACEME, 0, 0, 0);
JNIEnv *env = NULL;//注册时在JNIEnv中实现的,所以必须首先获取它
jint result = JNI_FALSE;
//获取env指针
if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {//从JavaVM获取JNIEnv,使用1.6的版本
return result;
}
//获取类引用,写类的全路径(包名+类名)
jclass clazz = env->FindClass(JNIT_CLASS);
if (clazz == NULL) {
return result;
}
//注册方法
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
return result;
}
//成功
result = JNI_VERSION_1_6;
return result;
}
JNIEXPORT void JNICALL
JNI_OnUnLoad(JavaVM *jvm, void *reserved){
JNIEnv *env = NULL;
//获取env指针
if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {//从JavaVM获取JNIEnv,使用1.6的版本
return result;
}
//获取类引用,写类的全路径(包名+类名)
jclass clazz = env->FindClass(JNIT_CLASS);
if (clazz == NULL) {
return result;
}
jint nRes = env->UnregisterNatives(cls clazz);
return;
}
说明:
定义的JNIT_CLASS表示的是要调用的jni的java类的名称
gMethods[]为RegisterNatives的第三个函数,表示的是所有jni的函数的集合。
JNINativeMethod是表示jni的方法的结构体,具体结构如下
typedef struct{
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
name:为Java类中的native函数的名称
signature:为Java类中的native函数的Java类型描述
fnPtr为jni中对应的函数名称,格式为类似(void*)MethodName
函数和变量的java类型描述可以通过命令 javap -s -p classname来获得,classname与使用javah时写的名称一致,javah生成的头文件的每个函数的注释中也有这个描述
对于“()Ljava/lang/String;”一个描述,表示该函数没有参数,返回值为String。括号内的是参数列表,后面跟的是返回值
java中简单类型和jni中的描述的对应关系如下表所示:
Field Descriptor Java Language Type
Z boolean
B byte
C char
S short
I int
J long
F float
D double
对于复杂类型,字符串描述以“L”开头,以“;”结束,例如java中的 String ,在jni中的描述为"Ljava/lang/String;"
对于数组,以“[”开头,接类型描述,例如int[ ],在jni中的描述为“[I”;String[ ], 对应为“[Ljava/lang/String;”;如果是数组维数增加一维,则"["增加一个,例如int[ ][ ],对应为“[[I”;
4.获取方法签名,在build的 /build/intermediates/classes/debug/目录下获取定义native方法名称工具类的class文件。使用javap -s xxx.class获取方法签名
5.CMakeLists.txt添加多c++源文件
6.gradle配置