一、概述
android有两个世界,一个世界在app,jvm java/koltin, 另一个世界在linux c/c++/rust。 跨过了jni 就是另一个世界。
jni 的全称是 java native interface,分为java部分和c 部分。 java 部分就是关键字native 开头的这些。c部分写在c代码里,打包成so 给java 调用。
二、jni 的样板代码
jni的样板代码其实也很简单,就分两块 java 调c。 和c 回调java。
2.1、样板代码
#include <jni.h>
#include <string.h>
#include <android/log.h>
#include "log.h"
static JavaVM *local_jvm = NULL;
static jclass local_jni_connector_class = NULL;
static gboolean attachThreadToJvm(JNIEnv **env) {
gboolean attached = FALSE;
int rs2 = 0;
int rs1 = (*local_jvm)->GetEnv(local_jvm, (void **) env, JNI_VERSION_1_6);
switch (rs1) {
case JNI_OK:
break;
case JNI_EDETACHED:
rs2 = (*local_jvm)->AttachCurrentThread(local_jvm, env, NULL);
if (rs2 != JNI_OK) {
LOG_DEBUG("ERROR: Could not attach current thread to local_jvm.");
} else {
attached = TRUE;
}
break;
}
return attached;
}
static void detachThreadFromJvm() {
(*local_jvm)->DetachCurrentThread(local_jvm);
}
static gboolean jvmAndMethodReferences(JNIEnv *env) {
// Get a reference to the JVM to get JNIEnv from in (other) threads.
if (local_jvm != NULL){
return TRUE;
}
jint rs = (*env)->GetJavaVM(env, &local_jvm);
if (rs != JNI_OK) {
LOG_DEBUG("ERROR: Could not obtain local_jvm reference.");
return FALSE;
}
jclass local_class = (*env)->FindClass(env, "com/ruijie/est/client/SpiceCommunicator");
local_jni_connector_class = (jclass) ((*env)->NewGlobalRef(env, local_class));
return TRUE;
}
2.2、简单的接口代码示例
static jmethodID jni_auth_callback = NULL;
static void registerJavaCallback(JNIEnv *env){
jvmAndMethodReferences(env);
jni_auth_callback = (*env)->GetStaticMethodID(env, local_jni_connector_class, "onXxxAuthCallback","(I)V");
}
/*
* 授权回调
*/
static void uiAuthCallback(int code){
LOG_DEBUG("uiAuthCallback");
JNIEnv * env;
gboolean attached = attachThreadToJvm(&env);
(*env)->CallStaticVoidMethod(env, local_jni_connector_class, jni_auth_callback, code);
if (attached) {
detachThreadFromJvm();
}
}
static void callback(EST_AUTH_WORK_STATE_EVENT_TYPE event_type, char *msg, void *context){
LOG_DEBUG("uiAuthCallback %d ", event_type);
uiAuthCallback(event_type);
}
/**
* 初始化
*/
JNIEXPORT void JNICALL
Java_com_yy_xxx_client_SxCommunicator_xxxAuthInit(JNIEnv *env,
jobject cls,
jstring logPath){
LOG_DEBUG("xxxAuthInit");
registerJavaCallback(env);
char *c_logPath=(*env)->GetStringUTFChars(env, logPath, NULL);
LOG_DEBUG("xxxAuthInit %s", c_logPath);
xxx_auth_init(c_logPath, callback, NULL);
(*env)->ReleaseStringUTFChars(env, logPath, c_logPath);
}
JNIEXPORT void JNICALL
Java_com_yy_xxx_client_SxCommunicator_xxxAuthRegisterStart(
JNIEnv *env,
jobject cls,
jstring app_id,
jint mode,
jstring activation_code,
jstring auth_server_addr,
jstring auth_server_pwd,
jstring domain,
jstring key,
jstring secret
){
LOG_DEBUG("xxxAuthRegisterStart");
int c_mode = mode;
char *c_app_id = (*env)->GetStringUTFChars(env, app_id, NULL);
char *c_activation_code = (*env)->GetStringUTFChars(env, activation_code, NULL);
char *c_auth_server_addr=(*env)->GetStringUTFChars(env, auth_server_addr, NULL);
char *c_auth_server_pwd=(*env)->GetStringUTFChars(env, auth_server_pwd, NULL);
char *c_domain=(*env)->GetStringUTFChars(env, domain, NULL);
char *c_key=(*env)->GetStringUTFChars(env, key, NULL);
char *c_secret=(*env)->GetStringUTFChars(env, secret, NULL);
//调用c模块函数
xxx_auth_register_start(c_app_id,
c_mode,
c_activation_code,
c_auth_server_addr,
c_auth_server_pwd,
c_domain,
c_key,
c_secret);
//释放char*
(*env)->ReleaseStringUTFChars(env, app_id, c_app_id);
(*env)->ReleaseStringUTFChars(env, activation_code, c_activation_code);
(*env)->ReleaseStringUTFChars(env, auth_server_addr, c_auth_server_addr);
(*env)->ReleaseStringUTFChars(env, auth_server_pwd, c_auth_server_pwd);
(*env)->ReleaseStringUTFChars(env, domain, c_domain);
(*env)->ReleaseStringUTFChars(env, key, c_key);
(*env)->ReleaseStringUTFChars(env, secret, c_secret);
}
JNIEXPORT void JNICALL
Java_com_yy_xxx_client_SxCommunicator_xxxAuthRegisterStop(JNIEnv *env, jobject cls){
LOG_DEBUG("xxxAuthRegisterStop");
xxx_auth_register_stop();
}
2.3、业务代码对应的java 层jni
class SxCommunicator{
public native void xxxAuthInit(String logPath);
public native void xxxAuthRegisterStart(
String app_id,
int mode,
String activation_code,
String auth_server_addr,
String auth_server_pwd,
String domain,
String key,
String secret);
public native void xxxAuthRegisterStop();
private static void onXxxAuthCallback(int code) {
Logger.i(TAG, "onAuthCallback code=" + code);
}
}
三、说明
上文示例代码是,一个简单的鉴权模块的jni。
其中:
- 1、xxxAuthInit 函数进行了基础的模块初始化,初始化的时候把c回掉java的回调函数onXxxAuthCallback注册到c 层变量:jni_auth_callback 中。鉴权模块自己的回调uiAuthCallback传给鉴权模块c的代码。当uiAuthCallback被调用时也去调用java层的回调函数
- 2、xxxAuthRegisterStart 函数进行了启动授权,传到鉴权模块C中,进行一系列逻辑触发回调函数
- 3、xxxAuthRegisterStop java业务退出的时候调用。
该示例中涉及了,java调c, c调java。 java的int string类型到 c 的int,char*数据类型的转换与回收(string类型回收)。
【注】样板代码中的jbool 是glib的类型,可以直接改成int 或者unsigned char