1、JNI简介
The Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call and be called by[1] native applications (programs specific to a hardware and operating system platform) and libraries written in other languages such as C, C++ and assembly.
--引用于wiki
2、Java调用C
2.1、java类文件中定义本地方法,其他方法调用此本地方法
下面给出三个比较有代表性的方法:
1、传递java对象 public static native int JNVSDKDEVOpen(ConfigObj config);
2、返回java对象 public static native ResultObj JNVSDKDEVGetConfig(int i, int type);
3、回调java方法 public static native int JNVSDKDEVStatusCallback();
jniFactory.java
package com.xxxx.xxxxplayer.jni; import com.xxxx.xxxxplayer.conf.ConfigObj; import com.xxxx.xxxxplayer.modelView.ResultObj;
public class JniFactory { static { System.loadLibrary("sdk-lib"); } public static native int JNVSDKDEVOpen(ConfigObj config); public static native ResultObj JNVSDKDEVGetConfig(int i, int type); public static native int JNVSDKDEVStatusCallback(); }
java类型和本地类型对比(主要用于定义JNI规范的方法)
Java Type | Native Type | Description |
---|---|---|
boolean | jboolean | unsigned 8 bits |
byte | jbyte | signed 8 bits |
char | jchar | unsigned 16 bits |
short | jshort | signed 16 bits |
int | jint | signed 32 bits |
long | jlong | signed 64 bits |
float | jfloat | 32 bits |
double | jdouble | 64 bits |
void | void | not applicable |
java类型标识(主要用于在JNI规范的方法中获取java对象的相关属性)
Type Signature | Java Type |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
L fully-qualified-class ; | fully-qualified-class |
[ type | type[] |
( arg-types ) ret-type | method type |
2.2、编写本地方法的实现
sdk-lib.c
#include <jni.h> #include "jnvsdk_net_interface.h" #include <stdlib.h> #define TAG "SDK_LIB>>" // 这个是自定义的LOG的标识 JNVSDK_HANDLE handle;//全局handle初始化之后其他函数都引用此变量 JavaVM *gs_jvm=NULL;//全局JavaVM,用于C主动调用java方法,用于UI刷新 jclass g_jniActClass=NULL;//保存主Activity的类,用于UI刷新 int JNVSDKCallback(JNVSDK_HANDLE jnvsdk_handle, int iChn, int cmdType, int cmdType2, char *ptr, int dataLen); jint Java_com_xxxx_xxxxplayer_jni_JniFactory_JNVSDKDEVOpen ( JNIEnv* env, jobject jobj , jobject config) { //第一种获取java对象参数的方法 //jclass clazz=(*env)->FindClass(env,"com/xxxx/xxxxplayer/conf/ConfigObj"); //第二种获取java对象参数的方法 jclass clazz=(*env)->GetObjectClass(env,config); if(clazz==NULL){ return 0; } //获取java对象的字段 jfieldID device_ip=(*env)->GetFieldID(env,clazz,"device_ip","Ljava/lang/String;"); jfieldID device_port=(*env)->GetFieldID(env,clazz,"device_port","I"); jfieldID uname=(*env)->GetFieldID(env,clazz,"uname","Ljava/lang/String;"); jfieldID password=(*env)->GetFieldID(env,clazz,"password","Ljava/lang/String;"); //第一种获取字段值的方法(直接获取) jstring test = (jstring)(*env)->GetObjectField(env,config,uname); const char *test_uname_ = (*env)->GetStringUTFChars(env,test ,NULL); //第二种获取字段值的方法(通过java对象提供的getxx()获取) jmethodID mid = (*env)->GetMethodID(env,clazz, "getUname", "()Ljava/lang/String;"); jstring strObj = (jstring)(*env)->CallObjectMethod(env,config, mid); const char *uname_ = (*env)->GetStringUTFChars(env,strObj ,NULL);
... //将java对象封装到结构体 strcpy(u_info.username,uname_); //调用目标方法 return JNVSDK_DEV_Open(&handle,u_info,n_info); } jobject Java_com_xxxx_xxxxplayer_jni_JniFactory_JNVSDKDEVGetConfig ( JNIEnv* env, jobject jobj ,jint iChn ,jint cfgType) { //得到返回对象的类和初始化方法 jclass clazz = (*env)->FindClass(env,"com/xxxx/xxxxplayer/modelView/ResultObj"); jmethodID method_init = (*env)->GetMethodID(env,clazz,"<init>","()V"); //获取java对象的字段 jfieldID resultNum = (*env)->GetFieldID(env,clazz, "resultNum", "I"); jfieldID resultStr = (*env)->GetFieldID(env,clazz, "resultStr", "Ljava/lang/String;"); //创建java对象 jobject return_Obj = (*env)->NewObject(env,clazz, method_init); int result=0; char result_str[128]=""; division_display_cfg_t st; switch(cfgType){ case 51: result= JNVSDK_DEV_GetConfig(handle, iChn, cfgType, (char *)&st, sizeof(st)); //向对象设值 (*env)->SetIntField(env,return_Obj,resultNum,result); int length = sizeof(st.display_mode)/sizeof(st.display_mode[0]); for(int i=0;i<length;i++){ char tmp[10]; sprintf(tmp, "%d",st.display_mode[i]); strcat(tmp,";"); strcat(result_str,tmp); } //向对象设值 (*env)->SetObjectField(env,return_Obj,resultStr,(*env)->NewStringUTF(env,result_str)); break; } //返回对象 return return_Obj; }
2.3、调用非JNI规范的C方法
这在上一步已经实现,单独列出是强调java调用非JNI规范的库该做些什么。
项目需要调用两类库文件:
1、libvlcjni.so(JNI规范),这是VLClan提供的,并且还提供了java类,这种情形下我们只需要关注java层面的接口就好了。
2、libjnvsdk.a(非JNI规范),这类比较麻烦,需要自己编写JNI规范的c文件,再调用libjnvsdk.a的头文件libjnvsdk.h里的方法,所以这种情形下我们需要库文件(*.so/*.a)以及对应的头文件。
我在下面给的示例都是非JNI规范的。至于怎么将这类库编译生成JNI规范的动/静态库,我在这篇博文已交代。
3、C调用Java
3.1、项目背景
项目是安卓平台的APP,用于直播,监控。程序主代码还是java,库文件提供额外功能。大多JNI项目是单向的,双向较少。
本项目出现C调用java是由于服务器(c开发)要向客户端(web,android,ios,qt)发送同步UI的数据,app获取数据后更新UI。
sdk-lib.c
jint Java_com_xxxx_xxxxplayer_jni_JniFactory_JNVSDKDEVStatusCallback ( JNIEnv* env, jobject jobject ) { jclass jniActClass = (*env)->FindClass(env, "com/xxxx/xxxxplayer/activity/VideoActivity"); (*env)->GetJavaVM(env,&gs_jvm); //保存到全局变量中 g_jniActClass = (*env)->NewGlobalRef(env,jniActClass);//保存到全局变量中 jint result= JNVSDK_DEV_RegisterRefreshCfgAndStatusCallback(JNVSDKCallback);//注册回调函数,c端数据更新会调用JNVSDKCallback return result; } int (JNVSDK_HANDLE jnvsdk_handle, int iChn, int cmdType, int cmdType2, char *ptr, int dataLen) { division_display_status_t *divi_display = (division_display_status_t *)ptr; JNIEnv *jnienv;
//将全局的jvm加到当前线程 (*gs_jvm)->AttachCurrentThread(gs_jvm,&jnienv, NULL);
//得到VideoActivity中的jniCallback4java方法,然后调用 jmethodID jniCallback4java = (*jnienv)->GetStaticMethodID(jnienv, g_jniActClass, "jniCallback4java", "(Ljava/lang/String;)V"); (*jnienv)->CallStaticVoidMethod(jnienv, g_jniActClass, jniCallback4java,(*jnienv)->NewStringUTF(jnienv, "OOOOOOOOOOOO")); }
VideoActivity.java
public class VideoActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { mHandler = new Handler() { public void handleMessage(Message msg) { Bundle tBundle=msg.getData(); String jni_msg=tBundle.getString("JNI_MSG"); Log.e(TAG,jni_msg);
//更新UI
...
} }; } //***callback*********JNVSDKCallback调用此函数,负责发送更新UI信息 public static void jniCallback4java(String msg){ Message tMsg=new Message(); Bundle tBundle=new Bundle(); tBundle.putString("JNI_MSG", msg); tMsg.setData(tBundle); mHandler.sendMessage(tMsg); } }
4、异常处理和内存管理
待续...