Java盲怎么写JNI

一.准备工作 1.为eclipse增加c和c++的开发插件(CDT插件)

2.安装NDK开发环境 3.安裝交叉编译工具Cygwin,配置环境变量

 

二.操作步骤

1.首先在java类中声明一个native的方法

2.使用javah命令生成包含native的方法声明的c/c++头文件

3.按照生成的c/c++头文件来写c/c++源文件

4.将c/c++原文件编译成动态链接库(DLL,so等),在android环境下通过Cygwin来编译

 

JNI操作过程中要获得方法的签名通过用javap命令获得

数据类型、方法签名
byteB
charC
doubleD
floatF
intI
longJ (注意不是L)
shortS
voidV
booleanZ (注意不是B)
类类型L跟完整类名,如Ljava/lang/String; (注意,以L开头,要包括报名,以斜杠分隔,最后有一个分号作为类型表达式的结束)
数组type[]

[type,如果二维数组则为[[type

如,[float

[[float

方法

(参数类型签名)返回值类型签名

如:float fun(int a, int b),它的签名为(II)F

String toString(),它的签名为()Ljava/lang/String;

 

 

JNI函数总结:

JNI开发当中最重要的就是JNIEnv JNIEnv类型实际上代表了Java环境,通过这个JNIEnv*指针,就可以对java端的代码进行操作,例如:创建java类的对象,调用java对象的方法,获取java对象的,代码进行操作.

JNIEnv类中有很多的函数可用:

NewObject/NewString/New<TYPE>Array

Get/Set<TYPE>Field Get/SetStatic<Type>Field

Call<TYPE>Method/CallStatic<TYPE>Method 等很多函数.

java代码既可以访问本地的c/c++代码,同时c/c++代码也可以访问java代码并互相操作

 

下面对常用函数进行分析:

1.jclass的取得   为了能够在c/c++中使用java类.JNI.h头文件中专门定义了jclass来表示java中的Class类

  JNIEnv类中可以取得jclass的函数如下:

  jclass FindClass(const char* clsName); //通过传入类完整路径取得类, 

jclass GetObjectClass(jobject obj); //通过传入对象名取得其所在的类 

jclass GetSuperClass(jclass obj);  //通过传入对象名取得其所属类的父类

  FindClass会在系统环境变量下寻找类,传入完整的类名,注意包与包之间用'/'分隔而不是'.' 

例如:   jclass cls_string = env->FindClass("java/long/String");

2.方法java中的属性和方法 

JNI在JNI.h头文件中定义了jfieldID和jmethodID类型来代表java中的属性和方法 

要对java端的属性和方法进行访问和操作的时候需要先获得该属性的jfieldID和方法的jmethodID 

JNIEnv类中取得方法和属性的函数如下:

  jfieldID GetFieldID(jclass clazz, const char* name, const char* sig) //取得普通的jfieldID,参数1:类名,参数2:属性字段名,参数3:字段的签名()通过javap      命令获得 

jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)//静态的jfieldID

  jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)//普通的jmethodID参数1:类名,参数2:属性字段名,参数3:方法的签名()通过javap命令获得 

jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)//静态的jmethodID

  示例代码: 

1.通过使用签名取得属性/方法jfieldId/jmethodID 

java端: 

public class  Hello   {

public native void test(); 

public int property; 

public int function(int foo,Date date, int[] arr)  { 

   //....... 

  } 

}

  test方法在jni本地的实现: 

JNI端test的实现: 

JNIEXPORT void JNICALL java_Hello_test(JNIEnv* env,jobject obj)   {  

  //由于test不是静态方法,传入的就是调用该方法的对象,若是静态则传入的jclass

   jclass hello_clazz = env->GetObjectClass(obj);//取得对象所属的类

   jfieldID fieldID_prop = env->GetFieldID(hello_clazz,"property","I");//取得jfieldID 

  jmethodID methodID_func = env->GetMethodID(hello_clazz,"function",(ILjava/util/Date;[I)I)//取得jMethodID

  env->CallIntMethod(obj,methodID_func,0L,null,null);//调用方法 

}

3.获取属性并设置属性 

取得了相应属性的jfieldID后 就可以通过使用JNI中提供的 

Set<TYPE>Field(jobject obj,jfieldID fieldID,相应类型的值) 

Get<TYPE>Field(jobject obj,jfieldID fieldID) 

SetStatic<TYPE>Field(jclass clazz,jfieldID fieldID,相应类型的值) 

GetStatic<TYPE>Field(jclass clazz,jfieldID fieldID)   等函数对不同属性进行操作了,传入的参数为

  示例代码: 

package cn.itcast;   public class TestNative { 

  public int number = 10; 

  public native void sayHello(); 

  public static void main(String[] args) {  

  System.loadLibrary("NaviteCode");   

  TestNative tst = new TestNative();  

  tst.sayHello();  

  System.out.println(tst.number);

  } 

}

JNI端sayHello的实现: 

//通过在jni端设置java中属性的值 

JNIEXPORT void JNICALL java_cn_itcast_TestNative_sayHello(JNIEnv* env,jobject obj)   {

  jclass clazz_TestNative = env->GetObjectClass(obj);//取得對象所属的类 

  jfieldID id_number = env->GetFieldId(clazz_TestNative,"numver","I");//通过类取得number的jfieldID 

  jint number = env->GetIntField(obj,id_number);//取得number 

  env->SetIntField(obj,id_number,100L);//设置number的值 

}

4.JNI中对java方法的调用 

JNIEnv提供了很多对java方法调用的函数

  调用java中实例方法的三种形式: 

Call<TYPE>Method(jobject obj,jmethodID MethodID,...); 

Call<TYPE>Method(jobject obj,jmethodID MethodID,va_list lst); 

Call<TYPE>Method(jobject obj,jmethodID MethodID,jvalue* v);   

通常第一种方式最常用,第二种方式需要传入一个参数表的va_list变量,第三种方式需要传入指向一个jvalue或jvalue数组的指针   

调用java中的静态方法三种形式: 

CallStatic<TYPE>Method(jclass clazz,jmethodID MethodID,...); 

CallStatic<TYPE>Method(jclass clazz,jmethodID MethodID,va_list lst); 

CallStatic<TYPE>Method(jclass clazz,jmethodID MethodID,jvalue* v);

  示例代码:   package cn.itcast;   public class TestNative {

public native void sayHello(); 

public double max(double num1,double num2)  {  

  return num1 > num2 ? num1 : num2; 

public static void main(String[] args) {  

  System.loadLibrary("NaviteCode");   

  TestNative tst = new TestNative();  

  tst.sayHello();  

  } 

}

  JNIEXPORT void JNICALL java_cn_itcast_TestNative_sayHello(JNIEnv* env,jobject obj)   {

   jclass clazz_TestNative = env->GetObjectClass(obj);//取得對象所属的类

   jmethodID max_methodID = env->GetMethodID(obj,"max","(DD)D");//取得jmethodID

  env->CallDoubleMethod(obj,max_methodID,3.14,3.15);//调用max方法   

JNI中要想调用父类的方法可以使用CallNonVirtual<TYPE>Method(),使用此方法时必须要取得父类的jclass和父类的方法的jmethodID

5.JNI中对java对象的创建   通过NewObject()函数来创建java对象,使用GetMethodID取得构造函数的jMethodID,若传入的函数名称为"<init>"就能取得构造函数   构造函数的返回值始终为void

  示例代码: 

package cn.itcast;   public class TestNative {

  public static native void outputDate(); 

   public static void main(String[] args) { 

  System.loadLibrary("NaviteCode");   

  outputDate();//调用本地方法 

  } 

}

  JNIEXPORT void JNICALL java_cn_itcast_TestNative_outputDate(JNIEnv* env,jobject obj)   {

   jcalss clazz_date= env->FindClass("java/util/Date"); //取得Date类的jclass 

  jmethodID mid_Date_constr = env->GetMethodId(clazz_date,"<init>","()V"); //取得Date类的构造函数 

  jobject now = env->NewObject(clazz_date,jmethodID_Date_constr );//通過构造函数创建Date类的对象

  jmethodID mid_Date_getTime  = env->GetMethodID(clazz_date,"getTime","()J");//取得date类中getTime()方法的jmethodID 

  jlong time = env->CallLongMethod(now,mid_Date_getTime)//调用getTime()方法  

}

6.JNI中对java字符串的操作  

相关函数:  

1.const jchar* GetStringChars(jstring string, jboolean* isCopy);  

2.const char* GetStringUTFChars(jstring string, jboolean* isCopy);  

3.void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf)     

第一个参数是指向java中String對象的jstring变量  

第二个参数是一个jboolean的指针,用来标示是否对java中的String进行拷贝,为NULL则不关心拷贝,为JNI_TRUE和JNI_FALSE则说明是否拷贝     

这两个函数会有不同的动作:  

1.开启新内存,然后把java中的String拷贝到这块内存中,然后返回执行这块内存地址的指针;  

2.直接返回指向java中String的内存的指针,此时千万不能改变这个内存中的内容,否则将会破坏String在java中始终是常量这个原则     

示例代码:  

package cn.itcast;    public class TestNative {

   public String message = null;

   public static native void callCppFunction(); 

   public static void main(String[] args) {  

  System.loadLibrary("NaviteCode");//加载库文件

    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));//取得用戶键盘录入流  

  String str = reader.readLine();//读取用户输入的字符串     

  TestNative  obj = new TestNative();  

  obj.message = str;  

  obj.callCppFunction();//调用本地方法 

  }  

}     

JNIEXPORT void JNICALL java_cn_itcast_TestNative_callCppFunction(JNIEnv* env,jobject obj)    { 

  jclass clazz_TestNative = env->GetObjectClass(obj);//取得对象所属的类 

  fieldID fid_msg = env->GetFieldID(obj,"message","Ljava/long/String;"); //取得message字段的jfieldID 

  jstring j_msg = (jstring)env->GetObjectField(obj,fid_msg )//取得string字段 

  const char* jstr = env->GetStringChars(j_msg ,NULL);/取得java端用戶从键盘录入的字符串 

  //....对字符串进行处理  env->ReleaseStringChars(j_msg,jstr);//释放资源  

}

7.JNI中对java数组的操作  

数组分为基本类型数组和对象类型数组   

相关函数:

   基本类型数组:   

Get<TYPE>ArrayElements(<TYPE>Array array,jboolean* isCopied);这类函数用于将java中的基本类型数组转化到c/c++中的数组    Release<TYPE>ArrayElements(<TYPE>Array,<TYPE>* array,jint mode);此函数用于如何处理java跟c/c++的数组,   

mode可以取下面的值  

0             -->对java 的数组进行更新并释放c/c++的数组  

JNI_COMMIT    -->对java 的数组进行更新但不释放c/c++的数组  

JNI_ABORT     -->对java 的数组不进行更新但释放c/c++的数组     

GetArrayLength(jarray array)用于取得不同类型数组的长度  

Get<TYPE>ArrayRegion(<TYPE>Array array,jsize jstart,jsize len,<TYPE>* buf) ,此类函数用于在c/c++中先开辟一块内存把java中基本类型数组拷贝到这块内存中   

Set<TYPE>ArrayRegion(<TYPE>Array array,jsize jstart,jsize len,const <TYPE>* buf) 此类函数用于将java中的基本类型数组用c/c++中的数组元素来赋值

   对象类型数组:  

JNI中没有直接的将java中的对象类型数组转化到c++中的对象类型数组,通过使用Get/SetObjectArrayElement这样的函数对java的object[]数组进行操作  

   示例代码:  

package cn.itcast;    public class TestNative {

int [] arrays = {1,4,6,8,5,3,1,5,8,9}; 

public static native void callCppFunction();   

public static void main(String[] args) {  

System.loadLibrary("NaviteCode");//加载库文件   

TestNative  obj = new TestNative();  

obj. callCppFunction();  

}  

}  

JNIEXPORT void JNICALL java_cn_itcast_TestNative_callCppFunction(JNIEnv* env,jobject obj)    { 

  jclass clazz_TestNative = env->GetObjectClass(obj);//取得对象所属的类 

  jfieldID fid_arrays = env->GetFieldID(clazz_TestNative,"arrays","[I");//取得数组字段的jfieldID

  jintarray jint_arr (jintarray)env->GetObjectField(obj,fid_arrays);//取得数组对象 

  jint* int_arr = env->GetIntArrayElements(jint_arr,null);//取得数组的元素 

  jint len = env->GetArrayLength(jint_arr);//取得数组的长度

  for(jsize i =0 ; i<len ;i++)  { 

  //....循环对数组元素进行处理 

  } 

  env->ReleaseIntArrayElements(jint_arr,int_arr,JNI_ABORT);//释放c++的数组 

}    

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值