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、异常处理和内存管理
待续...