JNI使用流程

252 阅读1分钟

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文件,一般会包含:

  1. LOCAL_PATH := $(call my-dir):配置Android.mk的目录路径;
  2. include $(CLEAR_VARS):清除像LOCAL_XXX的全局变量,避免相互影响;
  3. LOCAL_MODULE:配置模块的模块名,名字必须唯一且不包含空格。Build System会自动添加适当的前缀和后缀;
  4. LOCAL_SRC_FILES:配置需要打包到模块的源码文件;
  5. include $(BUILD_SHARED_LIBRARY):配置为动态库;

创建Application.mk文件,一般包含:

  1. APP_BUILD_SCRIPT:配置自定义的Android.mk文件目录和文件名,默认情况下是在项目中JNI子目录下查找Android.mk;
  2. APP_ABI:配置对应的ABI生成二进制文件,默认使用armeabi;
  3. APP_STL := stlport_shared:配置STL运行库;
  4. APP_PLATFORM:指定目标Android平台;

参照: mp.weixin.qq.com/s/0bNv2YVYe…

blog.csdn.net/mjl_man/art…

blog.csdn.net/feverd555/a…

blog.csdn.net/paradox_1_0…

www.cnblogs.com/yongdaimi/p…