JNI打通java和c

本文详细介绍了如何使用Java Native Interface (JNI)让Java应用调用C/C++代码,包括定义本地方法、编写C/C++实现及调用非JNI规范的C方法等步骤。同时,也讲解了如何从C/C++代码调用Java方法,实现跨语言双向调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
booleanjbooleanunsigned 8 bits
bytejbytesigned 8 bits
charjcharunsigned 16 bits
shortjshortsigned 16 bits
intjintsigned 32 bits
longjlongsigned 64 bits
floatjfloat32 bits
doublejdouble64 bits
voidvoidnot applicable

 java类型标识(主要用于在JNI规范的方法中获取java对象的相关属性)

Type Signature Java Type
Zboolean
Bbyte
Cchar
Sshort
Iint
Jlong
Ffloat
Ddouble
L fully-qualified-class ;fully-qualified-class
[ typetype[]
( arg-types ) ret-typemethod 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值