JNI打通java和c

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

  待续...

原文地址:https://www.cnblogs.com/lanqie/p/7462503.html