Android JNI 开发说明和示例

Android中开发JNI,把关键业务逻辑的代码使用C/C++的native实现可以隐藏代码逻辑;

一. JNI_OnLoad()

是Android的java层Dalvik虚拟机加载C库时,首先调用JNI_OnLoad()函数,所以, 在JNI_OnLoad()里面进行初始化操作,比如 注册JNI函数,获取Java的类等等。

注意: 初始化时动态注册native函数,运行时可以提高java层调用native函数的效率。

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){

    LOG_I("JNI_OnLoad: vm=%p, reserved=%p", vm, reserved);
     return JNI_VERSION_1_4;
}
返回值是jint 类型,表示Dalvik虚拟机C库使用JNI版本。如果库里面没有写明JNI_OnLoad()函数,VM会, 默认该库使用最老的JNI 1.1版本; 一定要返回版本号,否则会出错.

二. JNI_OnUnload()

当Java虚拟机释放该C库时,则会调用JNI_OnUnload()函数, 可以在此函数进行善后清除动作,通常不用。

————————————————

下面暂时开发逻辑关键代码段:

//JNI 函数声明
jstring udpSendto(JNIEnv *env, jclass clazz, jbyteArray _buf, jlong _len, jstring _host, jint _port);
void setHookVpnProxyAddr(JNIEnv *env, jclass clazz, jstring _proxy_host, jint _proxy_port);


/**
 * 全局java虚拟机
 * 首先, JNIEnv *env指针和 jobject对象都不能跨线程使用
 * 其次, jvm可以多线程共享,但是只有主线程可以销毁虚拟机
 * refer: https://blog.csdn.net/dream2009gd/article/details/8654928
 */
JavaVM *gJavaVM;
jint gJniVersion = JNI_VERSION_1_4;

/**
 * Java层C层回调的接口类
 */
static const char* HULK_JNI_UTIL_CLASS_PATH = "com/hulk/jni/HulkJniUtil";
/**
 * C回调Java层的接口类
 */
static const char *JAVA_JNI_CALLER_CLASS_PATH = "com/hulk/jni/JavaJniCaller";

/**
 * HulkJniUtil的方法列表
 * 

JNINativeMethod结构体,分别是java层的函数名称,签名,对应的函数指针 */ static const JNINativeMethod HULK_JNI_UTIL_METHODS[] ={ //jstring udpSendto(JNIEnv *env, jclass clazz, jbyteArray _buf, jlong _len, jstring _host, jint _port) {"udpSendto"/*java层方法名*/, "([BJLjava/lang/String;I)Ljava/lang/String;", (void*)udpSendto/*C层方法名*/}, {"setVpnUdpProxyAddr", "(Ljava/lang/String;I)V", (void*)setHookVpnProxyAddr}, }; /** * Java层 com.hulk.jni.JavaJniCaller 的全局引用. * C层可以直接调用里面的方法; * 注意: 每次调用JavaJniCaller的方法都需要env在当前线程,即调用ensureJniEnvCreated()返回的env. */ jclass gJavaJniCallerClass; /** * Android的Java层加载(启动)Native层模块的入口函数 *

Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数, *

所以在JNI_OnLoad()里面进行一些初始化工作,如注册JNI函数等等。 *

注册本地函数,可以加快java层调用本地函数的效率。 *

返回值是jint 类型,告诉Dalvik虚拟机此C库使用哪一个JNI版本。如果你的库里面没有写明JNI_OnLoad()函数,VM会默认该库使用最老的JNI 1.1版本; * @param vm Java 虚拟机 * @param reserved 保留字段 * @return */ jint JNI_OnLoad(JavaVM *vm, void *reserved) { LOG_I("JNI_OnLoad: vm=%p, reserved=%p", vm, reserved); jint result = -1; gJavaVM = vm; JNIEnv *env = NULL; // global_jvm = vm; gJavaVM->GetEnv(reinterpret_cast(&env), gJniVersion); LOG_I("JNI_OnLoad: env=%p", env); //JavaJniCaller的全局引用 jclass caller_clazz = env->FindClass(JAVA_JNI_CALLER_CLASS_PATH); gJavaJniCallerClass = (jclass)env->NewGlobalRef(caller_clazz); //动态注册方法: com/hulk/jni/HulkJniUtil.xxXXX() jclass hulk_clazz = (env)->FindClass(HULK_JNI_UTIL_CLASS_PATH); if (hulk_clazz == nullptr) { return -1; } int method_count = 0; method_count = sizeof(HULK_JNI_UTIL_METHODS) / sizeof(HULK_JNI_UTIL_METHODS[0]); if (method_count > 0) { LOG_I("JNI_OnLoad: Register natives method count=%d", method_count); if((env)->RegisterNatives(hulk_clazz, HULK_JNI_UTIL_METHODS, method_count) < 0) { LOG_E("JNI_OnLoad: Failed to register natives"); return -1; } } //一定要返回版本号,否则会出错: // Fatal signal 5 (SIGTRAP), code 1 (TRAP_BRKPT) (native函数缺少返回语句也会抛出此错误) result = gJniVersion;//eg JNI_VERSION_1_4 LOG_I("JNI_OnLoad: Finished result=%d", result); return result; } /** * 当虚拟机释放该C库时,则会调用JNI_OnUnload()函数来进行善后清除动作。 * @param vm Java 虚拟机 * @param reserved 保留字段 */ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) { LOG_W("JNI_OnUnload: vm=%p, reserved=%p", vm, reserved); } extern "C" JNIEnv *_getEnv() { JNIEnv *env = NULL; if (gJavaVM != NULL) { gJavaVM->GetEnv(reinterpret_cast(&env), gJniVersion); LOG_I("_getEnv: GetEnv env=%p", env); } else { LOG_W("_getEnv: gJavaVM in null"); } return env; } extern "C" JNIEnv *ensureEnvCreated() { JNIEnv *env = _getEnv(); if (env == NULL && gJavaVM != NULL) { gJavaVM->AttachCurrentThread(&env, NULL); LOG_I("ensureEnvCreated: AttachCurrentThread env=%p", env); } else { LOG_W("ensureEnvCreated: Failed to get env=%p, gJavaVM=%p", env, gJavaVM); } return env; } /** * 调用Java层(com.hulk.jni.JavaJniCaller)的方法: * public void call(String uri, String method, String arg) * @param uri 统一定位符 eg "context:.hulk/test" * @param method 方法 eg "get_proxy_port" * @param arg 参数 * @return 返回0表示成功,其他失败 */ int execJavaJniCall(char *uri, char *method, char *arg) { LOG_D("execJavaJniCall: uri=%s, uri=%s, uri=%s", uri, method, arg); JNIEnv *env = ensureEnvCreated(); if (env == nullptr) { LOG_E("execJavaJniCall: env is NULL"); return -1; } if (gJavaJniCallerClass == nullptr) { LOG_E("execJavaJniCall: gJavaJniCallerClass is NULL"); return -2; } jmethodID call_method_ID = env->GetMethodID(gJavaJniCallerClass, "call", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); if (call_method_ID == nullptr) { LOG_E("execJavaJniCall: Got call_method_ID is NULL"); return -3; } jstring j_uri = nullptr; jstring j_method = nullptr; jstring j_arg = nullptr; /** j_uri = env->NewStringUTF("context:.hulk/test"); j_method = env->NewStringUTF("get_proxy_port"); j_arg = env->NewStringUTF("34534"); */ j_uri = env->NewStringUTF(uri); j_method = env->NewStringUTF(method); j_arg = env->NewStringUTF(arg); LOG_D("execJavaJniCall: CallVoidMethod gJavaJniCallerClass=%p, call_method_ID%p", &gJavaJniCallerClass, &call_method_ID); env->CallVoidMethod(gJavaJniCallerClass, call_method_ID, j_uri, j_method, j_arg);//无返回值情况 LOG_D("execJavaJniCall: Finished."); return 0; }

你可能感兴趣的:(jni,NDK,Android,android,java,jvm,JNI,so)