用JNI 去了解C++

论坛 期权论坛 期权     
会敲代码的二哈   2019-7-7 23:04   2593   0
JNI开发,java层其实都是次要的,只管调用设计好的API就可以,最主要的角色,自然要属JNI层C++语言开发。由于JNI开发和Java可能是表亲戚的关系,所以JNI的一些语言特性,提供的API还是很类似于Java的,所以相对于纯C开发,其实我们java开发对JNI更容易上手。熟读JNI 提供的API 方法,助你开发如鱼得水
[h3]java调用C++方法[/h3]之前有说过,JNI层代码,是由Java每个native 方法映射过去的,
比如java 方法
  1. final private native long CreateInstance(int logLevel,String logPath);
复制代码

  1. JNIEXPORT jlong JNICALL Java_com_hexin_tdsapi_AbstractTds_CreateInstance  (JNIEnv *env, jobject ob,jint logLevel, jstring logPath)
复制代码
, 这里的方法名其实可以在
  1. .h
复制代码
头文件里 中 Copy过来,具体在方法体里实现自己需要的C代码。
JNIEXPORT jlong JNICALL Java_com_hexin_tdsapi_AbstractTds_CreateInstance  (JNIEnv *env, jobject ob,jint logLevel, jstring logPath){    LOGD("im from static moethod C++ , value is : %d",logLevel);}=====================Java====================public class test {    public static void main(String[] args) {        TDS_TEST tds = new TDS_TEST();        tds.CreateInstance(100,"");    }}      

[h3]C++调用Java实例方法[/h3]
  1. callJavaInstanceMethod
复制代码
是个简单的C的方法体
void callJavaInstanceMethod(JNIEnv *env, jobject instance) {    jclass clazz = NULL;    jmethodID method_id = NULL;    jstring str_log = NULL;    clazz = env->FindClass("com/hexin/demo/Hello");    if (clazz == NULL){        LOGD("没有发现该类");        return;    }    method_id = env->GetMethodID(clazz,"instanceMethod","(Ljava/lang/String;)V");// 方法签名    if (method_id == NULL){        LOGD("没有发现该方法名");        return;    }    str_log = env->NewStringUTF("c++ 调用java的实例方法");    env->CallVoidMethod(instance,method_id,str_log); //clazz 改为instance    env->DeleteLocalRef(clazz);    env->DeleteLocalRef(str_log);    return ;}
首先定义了三个变量,然后使用env调用封装好的方法FindClass,传入类名全路径,在jvm中如果有加载这个类,那么就会返回我们的这个类。
接着是获取方法的id,使用env调用GetMethodID,第一个参数是方法所在的类,第二个是方法名,第三个是方法签名。
然后使用env调用CallVoidMethod,传入类和方法和参数,完成对java层方法的调用。
如果一开始都不是很清楚,
  1. CallVoidMethod
复制代码
  1. FindClass
复制代码
等这些JNI方法入参规则,可以在JDK 目录下 找到
  1. jni.h
复制代码
文件,查找对应的方法名字和具体方法。
最后不要忘记删除引用,不然会发生内存泄漏
另外 这里要讲一下的就是: 方法签名
在学习c++调用java方法时需要了解的是方法签名,关于方法签名,我觉得只要关注这两个地方就行了:
  • 什么是方法签名:方法签名由方法名称和一个参数列表(方法的参数的顺序和类型)组成。
  • 为什么要用方法签名:c语言中没有方法重载这个概念,如果java中有两个方法:long test(int n, String str, int[] arr) ,long test(String str) 。那么没有方法签名来标注一下,编译器不就懵逼了嘛(ノ`Д)ノ。
方法签名规则:
Java类型签名类型booleanZbyteBcharClongJcharCfloatFdoubleDshortSintI类L全限定类名数组[全限定类名

上述中类的签名规则是:”L+全限定类名+;”三部分组成,其中全限定类名以”/”分隔,而不是用”.”或”_”分隔。

[h3]C++调用Java静态方法[/h3]和实例方法的区别就两个地方,一个是CallStaticVoidMethod,一个是GetStaticMethodID:
callJavaInstanceMethod(JNIEnv *env, jobject instance) {    jclass clazz = NULL;    jmethodID method_id = NULL;    jstring str_log = NULL;    clazz = env->FindClass("com/hexin/demo/Hello");    if (clazz == NULL){        LOGD("没有发现该类");        return;    }    method_id = env->GetMethodID(clazz,"instanceMethod","(Ljava/lang/String;)V");    if (method_id == NULL){        LOGD("没有发现该方法名");        return;    }    str_log = env->NewStringUTF("c++ 调用java的实例方法");    env->CallVoidMethod(instance,method_id,str_log); //clazz 改为instance    env->DeleteLocalRef(clazz);    env->DeleteLocalRef(str_log);    return ;}
[h3]C++调用Java变量[/h3]首先在java类中定义一个变量:
public String name = "im is java";然后贴上jni代码,主要方法是GetFieldID,第一个参数传入变量所在类,第二个参数是变量名,第三个参数是签名类型:
void changeField(JNIEnv *env, jobject instance) {    jclass clazz = env->GetObjectClass(instance);    if (clazz == NULL){        return;    }    jfieldID jfieldID = env->GetFieldID(clazz,"name","Ljava/lang/String;");    if (jfieldID == NULL){        return;    }    jstring obj_str = (jstring) env->GetObjectField(instance,jfieldID);    if (obj_str == NULL){        return;    }    char* c_str = (char*) env->GetStringUTFChars(obj_str,JNI_FALSE);    const char new_char[40] = "changed from c";    //复制new_char的内容到c_str    strcpy(c_str,new_char);    jstring new_str = env->NewStringUTF(c_str);    LOGD("%s",new_char);    env->SetObjectField(instance,jfieldID,new_str);    env->DeleteLocalRef(clazz);    env->DeleteLocalRef(obj_str);    env->DeleteLocalRef(new_str);    return;}[h3]C++调用Java静态变量[/h3]同理,静态变量也没啥好讲的了,这里就贴一下代码:
void changeStaticField(JNIEnv *env, jclass type) {    jclass clazz = env->FindClass("com/hexin/demo/Hello");    if (clazz == NULL){        return;    }    jfieldID jfieldID = env->GetStaticFieldID(clazz,"age","I");    if (jfieldID == NULL){        return;    }    int age = env->GetStaticIntField(clazz,jfieldID);    LOGD("%d",age);    jint change_int = 12;    env->SetStaticIntField(clazz,jfieldID,change_int);    env->DeleteLocalRef(clazz);}
[h2]总结[/h2]其实JNI开发,并不是纯C的开发,JNI很多API已经提供完全,我们开发需要做的就是用C语言去实现调用这些API,可能遇到复杂的业务,对这些需求C的基础要求会比较高。纵观整个开发,从Java方向转入C++,在内存处理和C的指针性操作会比较难上手些,其他语言通性其实差不多。
总之,C++的开发 对于Android开发还是有很大帮助的!




分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:5
帖子:1
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP