CMakeLists
在AS中创建c++ module后,会生成一个CMakeLists.txt文件,在add_library中配置需要添加的类库,在find_library中配置需要添加的系统类库,在target_link_libraries中对库进行链接。
cmake_minimum_required(VERSION 3.4.1)// 指定CMake的版本
//add_library是添加类库,下面3个分别表示类库的名字叫做native-lib.so,SHARED这个选项表示共享类库的意思(就是以so结尾)
// src/main/cpp/native-lib.cpp表示native-lib.so对应的C++源代码位置
//这个add_library很重要,因为如果要添加其他类库,那么都是这样的方法来的,比如
添加这个 wlffmpeg类库
add_library( # Sets the name of the library.
wlffmpeg
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/jni/player.cpp )
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp )
//表示系统的日志库,只需要导入一个就可以了
find_library(
log-lib
log )
//链接库,要跟上面的类库名字保持一致
target_link_libraries(
native-lib
${log-lib} )
接着在java层的交互类的静态代码块中执行System.loadLibrary()加载动态库,并定义native方法。
public class JniDemo {
static {
System.loadLibrary("native-lib");
}
//静态注册
public static native Object getPackage();
//静态注册
public static native int addTest(int a, int b);
//需要动态注册的方法
public static native Application getApplicationObject();
}
然后可以直接通过AS提供的快捷方式在JNI层生成对应静态注册的方法,方法格式是Java_包名_类名_方法名。虽然这样生成会比较方便,但是存在方法名称过长;会生成javah头文件;初次调用JNI方法时需要建立关联,并且建立关联需要全局搜素,影响效率;注册不灵活。
extern "C"
JNIEXPORT jObject JNICALL
Java_com_jni_JniDemo_getPackage(JNIEnv *env, jclass type) {
std::string hello = "com.example.test";
// TODO
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_jni_JniDemo_addTest(JNIEnv *env, jclass type, jint a, jint b) {
// TODO
return a + b;
}
因此我们可以通过动态注册规避这些问题,首先定义JNINativeMethod数组,每个对象包含Java层方法名,方法签名和C++中对应的方法,然后一般在JNI层的JNI_OnLoad()方法中调用JNIEnv->RegisterNatives方法完成动态注册。
//TODO 动态注册的方法集合
static JNINativeMethod gMethods[] = {
{"getApplicationObject", "()Landroid/app/Application;", (void *) getApplicationObject}
};
/*
* System.loadLibrary("lib")时调用
* 如果成功返回JNI版本, 失败返回-1
* 这个方法一般都是固定的
*/
extern "C"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
if (env == NULL) {
return -1;
}
// 需要注册的类
jclass clazz = env->FindClass(CLASSNAME);
if (clazz == NULL) {
return -1;
}
//TODO 这里是重点,动态注册方法
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
return -1;
}
LOGD("dynamic success is %d", JNI_VERSION_1_4);
return JNI_VERSION_1_4;
}
在JNI层调用java层方法可以通过JNIEnv->FindClass()获取到对应的class对象,接着通过JNIEnv->GetMethodID()获取java层方法的MethodID,然后执行JNIEnv->CallObjectMethod()调用对应的java层方法。
如果在JNI中使用了JNIEnv->GetStringUTFChars()或JNIEnv->GetByteArrayElements()后,需要调用JNIEnv->ReleaseStringUTFChars,JNIEnv->ReleaseByteArrayElements()释放内存。
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv *evn;
if (vm->GetEnv((void **)(&evn), JNI_VERSION_1_6) != JNI_OK)
{
return -1;
}
jclass appClass = evn->FindClass("com/***/App");
jmethodID getAppContextMethod = evn->GetStaticMethodID(appClass, "getContext", "()Landroid/content/Context;");
//获取APplication定义的context实例
jobject appContext = evn->CallStaticObjectMethod(appClass, getAppContextMethod);
// 获取应用当前的签名信息
jstring signature = loadSignature(evn, appContext);
// 期待的签名信息
jstring keystoreSigature = evn->NewStringUTF("31BC77F998CB0D305D74464DAECC2");
const char *keystroreMD5 = evn->GetStringUTFChars(keystoreSigature, NULL);
const char *releaseMD5 = evn->GetStringUTFChars(signature, NULL);
// 比较两个签名信息是否相等
int result = strcmp(keystroreMD5, releaseMD5);
if (DEBUG_MODE)
LOGI("strcmp %d", result);
// 这里记得释放内存
evn->ReleaseStringUTFChars(signature, releaseMD5);
evn->ReleaseStringUTFChars(keystoreSigature, keystroreMD5);
// 得到的签名一样,验证通过
if (result == 0){
return JNI_VERSION_1_6;
}
return -1;
}
Ndk Build
创建Android.mk文件,一般会包含:
- LOCAL_PATH := $(call my-dir):配置Android.mk的目录路径;
- include $(CLEAR_VARS):清除像LOCAL_XXX的全局变量,避免相互影响;
- LOCAL_MODULE:配置模块的模块名,名字必须唯一且不包含空格。Build System会自动添加适当的前缀和后缀;
- LOCAL_SRC_FILES:配置需要打包到模块的源码文件;
- include $(BUILD_SHARED_LIBRARY):配置为动态库;
创建Application.mk文件,一般包含:
- APP_BUILD_SCRIPT:配置自定义的Android.mk文件目录和文件名,默认情况下是在项目中JNI子目录下查找Android.mk;
- APP_ABI:配置对应的ABI生成二进制文件,默认使用armeabi;
- APP_STL := stlport_shared:配置STL运行库;
- APP_PLATFORM:指定目标Android平台;