Jni基础 (一)Java和c互调

     由于c语言更接近底层,运行效率要比 Java快,所以Java开发当中会用到c语言开发一些核心库,一方面可以提高运行效率,另一方面可以让代码保密性更好,不易于反编译。本节所讲内容算是jni的基础入门,使用代码示例了Java和c之间最基本的交互。
1.Java调用c函数

  步骤:1.编写本地native方法
            2.使用Javah命令生成头文件(.h文件)
            3.使用c语言实现头文件中的方法
            4.生成dell动态库文件
            5.给dell库文件配置环境变量或者直接把dell文件直接拷到项目根目录
             6.Java加载dell文件
             //加载动态库
                static{
                     System.loadLibrary("dell文件名");
                           }
生成的头文件代码示例:
#include "jni.h"  

JNIEXPORT jstring JNICALL Java_JniExamp_JniTest_getStringFromC
  (JNIEnv *, jclass);
说明:生成的头文件中引入的头文件  jni.h是jdk自带的,而且 jni.h中还引入了 jni_md.h,也是jdk中自带的.h文件
他们都属于Java和c交互的api,在实现c函数的时候,这两个文件都得引入。

实现c函数代码示例:
//该方法实现给Java返回一个字符串
JNIEXPORT jstring JNICALL Java_JniExamp_JniTest_getStringFromC
  (JNIEnv *env, jclass jcls){
    return (*env)->NewStringUTF(env,"C String,hello java");
   }
参数说明:
   JNIEnv 结构体指针, env二级指针
    代表Java运行环境,调用Java中的代码
每个native函数,都至少有两个参数(JNIEnv*,jclass或者jobject)
1)当native方法为静态方法时:
jclass 代表native方法所属类的class对象(JniTest.class)
2)当native方法为非静态方法时:
jobject 代表native方法所属的对象

Java代码调用示例:

public class JniTest {

  public native static String getStringFromC();

    static{
        System.loadLibrary("libjnitest");
    }
    public static void main(String[] args) {
        String stringFromC = getStringFromC();
        System.out.println(stringFromC);
    }
}
随着日志  C String,hello java 的输出,我们就完成了Java对c函数的调用。初级使用就是那么so easy!

2.C 访问Java中的成员和方法
  这个章节可以分为两类
一类是 C访问Java中的静态成员和静态方法
另一类是C访问Java中的非静态成员和方法
在此之前我们先看一下在基本数据类型方面Java和jni的对应关系

基本数据
Java基本数据类型与JNI数据类型的映射关系
Java类型->JNI类型->C类型
boolean jboolean
byte jbyte;
char jchar;
short jshort;
int jint;
long jlong;
float jfloat;
double jdouble;
void void
引用类型(对象)
String jstring
object jobject
数组,基本数据类型的数组
byte[] jByteArray
对象数组
object[](String[]) jobjectArray

2.1 C访问Java中的非静态成员
   Java代码示例:
public class JniTest {
     public String javaString = "javaString";
        //访问属性,返回修改之后的属性内容
    public native String accessField();
     static{
        System.loadLibrary("libjnitest");
    }
    public static void main(String[] args) {
         System.out.println("javaString修改前:"+test.javaString);
        test.accessField();
        System.out.println("javaString修改后:"+test.javaString);
    }
}
C代码示例:
JNIEXPORT jstring JNICALL Java_JniExamp_JniTest_accessField
  (JNIEnv *env, jobject jobj){

     //jobj是test对象,JniTest.class
    //1.先获取jclass
    jclass cls=(*env)->GetObjectClass(env,jobj);
     //2.获取属性名 id
    jfieldID fid=(*env)->GetFieldID(env,cls,"javaString","Ljava/lang/String;");
     //最后一个参数是属性签名 可以通过javap命令获取属性签名
    //具体做法是在命令行窗口中切换到Java项目的bin目录,运行 javap -s -p 类的全路径名即可获取属性签名
     //3.获取javaString的属性值
    jstring jstr=(*env)->GetObjectField(env,jobj,fid);

        //对jstr做出变化
        char *c_str = (*env)->GetStringUTFChars(env,jstr,JNI_FALSE);
        //拼接得到新的字符串
        char text[20] = "c change java";
        strcat(text,c_str);
        jstring new_jstr = (*env)->NewStringUTF(env, text);
        (*env)->SetObjectField(env, jobj, fid, new_jstr);

        printf("new_jstr:%#x\n", &new_jstr);
         return new_jstr;
}
日志输出:
javaString修改前:javaString
javaString修改后:c change javajavaString
2.2  C访问Java中的静态成员
   Java代码示例:
public class JniTest {
    public static int num = 999;
  
    //访问静态属性,修改属性内容
    public native void accessStaticField();
   static{
        System.loadLibrary("libjnitest");
    }
    public static void main(String[] args) {
        JniTest test=new JniTest();
         System.out.println("num修改前:"+num);
        test.accessStaticField();
        System.out.println("num修改后:"+num);
    }
}
C代码示例:
//访问Java中的静态成员
JNIEXPORT void JNICALL Java_JniExamp_JniTest_accessStaticField
  (JNIEnv *env, jobject jobj){
    jclass cls=(*env)->GetObjectClass(env,jobj);
    //
    jfieldID fid=(*env)->Get StaticFieldID(env,cls,"num","I");
    // GetStatic<Type>Field 和访问非静态成员的区别
    jint num=(*env)->Get StaticIntField(env,cls,fid);
    num++;
    //修改
    (*env)->SetStaticIntField(env,cls,fid,num);
}
日志输出:
num修改前:999
num修改后:1000
2.3C访问Java中的非静态方法
Java代码示例:
public class JniTest {
    //C访问非静态方法
    public native void accessMethod();
    static{
        System.loadLibrary("libjnitest");
    }
     //产生指定范围的随机数
        public int getRandomInt(int max){
            System.out.println("genRandomInt 被c执行了...");
            return new Random().nextInt(max); 
        }
    public static void main(String[] args) {
        JniTest test=new JniTest();
     test.accessMethod();//Java调用c实现的这个函数,在这个函数中C有调用了非静态方法getRandomInt
    }
}
C代码示例:
//访问Java中的非静态方法
JNIEXPORT void JNICALL Java_JniExamp_JniTest_accessMethod
  (JNIEnv *env, jobject jobj){
   jclass cls=(*env)->GetObjectClass(env,jobj);
    jmethodID mid=(*env)->GetMethodID(env,cls,"getRandomInt","(I)I");
    jint randomNum=(*env)->CallIntMethod(env,jobj,mid,10);
    printf("random num:%ld",randomNum);
}
日志输出:
genRandomInt 被c执行了...
random num:3
C访问Java中的静态方法:
Java代码示例:
public class JniTest {
    //C访问静态方法
    public native String accessStaticMethod();
    static{
        System.loadLibrary("libjnitest");
    }
  //产生UUID字符串
        public static String getUUID(){
            return UUID.randomUUID().toString();
        }
    public static void main(String[] args) {
        JniTest test=new JniTest();
        String uuid = test.accessStaticMethod(); //Java调用c实现的这个函数,在这个函数中C又调用了静态方法 getUUID
        System.out.println("uuid::::"+uuid);
    }
}
C代码示例:
//访问Java中的静态方法
JNIEXPORT jstring JNICALL Java_JniExamp_JniTest_accessStaticMethod
  (JNIEnv *env, jobject jobj){
     jclass cls=(*env)->GetObjectClass(env,jobj);
    jmethodID mid= (*env)->Get StaticMethodID(env,cls,"getUUID","()Ljava/lang/String;");

    jstring uuid=(*env)->Call StaticObjectMethod(env,cls,mid);

    return uuid;
}
日志输出:
uuid::::4d34e97c-ae54-4377-86ea-de37468843b8

总结:
C在访问Java的成员和方法其实是有固定范式的。总结如下:
C访问java成员 步骤:
       1.先获取jclass
       2.获取属性名 id
       3.获取属性值
这三个步骤对于静态变量和非静态变量都是一样的,只是在访问静态变量时方法名前面加了一个static(上面代码中有红色标注
C访问Java方法 步骤:
      1.先获取jclass
      2.获取方法id
      3.方法调用
这三个步骤对于静态方法和非静态方法也是一样的,只是在访问静态方法时方法名前面加了一个static(上面代码有红色标注)
本篇文章只是简单介绍了一下jni中Java和c互调的简单示例, 示例代码点这里


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值