Android Ndk开发进阶

来源:互联网 发布:中国贫困线知乎 编辑:程序博客网 时间:2024/05/22 04:36

前言

上一篇文章,我们讲了Ndk的基本用法即Java调用C方法返回基本类型或String类型(引用类型)。接下来,我们就要讲到C调用Java类里面的方法包括静态方法,然后用C来处理,Java代码中传入的对象,通过C代码处理过后,返回该对象。好了,话不多说,新建项目。

创建工程

这里写图片描述
通过上一篇blog介绍,大家都应该会创建项目了吧。不会的回去看看上一篇文章。这里多创建了一个MyInfo类。用于在创建对象Java与C中相互传递。如图:
这里写图片描述
实体类里面的字段有基本类型,也有引用类型(String类型)。对于NDK开发来说,接收一个MyInfo实体类对象和该对象内的引用类型属性来说,都是jobject类型。所以大家不要搞混了。
项目中H文件与C文件,根据命令和提示创建,创建的依据是,调用jni时写的方法。如下图:
这里写图片描述
这个大家一看就明白,创建JNI方法。目前Android studio 对于Ndk开发编程不能够很好的支持,所以,你会看到,即便是项目完成后成功运行,JNI方法也会标红报错。不过只要能运行,不管那么多了。再就是创建出H文件,作用类似于Java的interface(接口),C文件“实现”H文件,include进去。
创建h文件,在工程目录下我们用的是javah -jni 全类名(包名➕雷明),自动创建H文件。这里的全类名就是com.china.MainActivity。这样我们就生成了。如下图:
这里写图片描述
在MainActivity中通过提示创建出C文件,并引用(include)刚才我们生成的h文件。如下图:这里写图片描述
接下来,就开始我们的数据操作了。

C调用Java

首先,我们要确定在C中要调用java中的什么方法。这样我们就在MainActivity中写个加法运算方法add(int x,int y)。

public int add(int x, int y) {        Toast.makeText(this, "x+y=" + (x + y), Toast.LENGTH_SHORT).show();        return x + y;    }

同理,在C中有对应的调用方法

/* * Class:     com_china_MainActivity * Method:    getInt * Signature: (II)I */JNIEXPORT jintJNICALL Java_com_china_MainActivity_getInt        (JNIEnv *env, jobject jobj, jint ji, jint jj) {        //TODO};

我们要使C中能够调用java,那么就需要先去调用c的方法。然后c中的方法再调用java类里面暴露给C调用的方法。(有点绕啊,��)Java->C->Java. 对应的,在java中调用 public native int getInt(int x, int y);//。然后在C代码中操作使其调用MainActivity中的add(x,y)方法。开始吧。

JNIEXPORT jint JNICALL Java_com_china_MainActivity_getInt        (JNIEnv *env, jobject jobj, jint ji, jint jj) {    jclass jclazz = (*env)->GetObjectClass(env, jobj);//    jclass jclazz = (*env)->FindClass(env, "com/china/MainActivity");//2.得到方法//jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);//最后一个参数是方法签名    jmethodID jmethodIDs = (*env)->GetMethodID(env, jclazz, "add", "(II)I");//3.实例化该类// jobject     (*AllocObject)(JNIEnv*, jclass);//    jobject jobject = (*env)->AllocObject(env, jclazz);//4.调用方法//jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);    jint value = (*env)->CallIntMethod(env, jobj, jmethodIDs, ji, jj);    return value;}

这里要注意的是,我们让c调用java代码,那么就要让c知道是调用哪个类的哪个方法。哪个类呢?注意
jclass jclazz = (*env)->FindClass(env, “com/china/MainActivity”); 这句话表示,获取到MainActivity类是“静态的”,我们知道,Activity是有生命周期的类,普通类是没有的。这里理解为“静态的”。所以还有一个方法,得到这个类的引用:jclass jclazz = (*env)->GetObjectClass(env, jobj); 传入的jobj就是该方法传入的jobject(类似于java中this引用之类的意思)。得到这个类后,我们还要通过操作调用到MainActivity中的add方法。
这里的操作就是获得该方法的签名。获得方法签名,我们会用到javap命令。javap -s 全类名(包名加类名)。通过GetMethodID(xx,xx,xx,xx),第一二个参数就是env(C中的指针引用),获得的java类的引用。第三个参数就是C要调用的方法名。第四个就是该方法签名。然后,就是最后一步了。调用。(CallIntMethod)(JNIEnv, jobject, jmethodID, …);方法前几个参数。都有了,后面还有个可变参数。就是我们调用java方法的传入之。这里,java与C中int等基本类型通用的。所以可以直接丢进去。完了。
简单吧。接下来更难的来了。

传递java对象

在MAinActivity中先初始化一个MyInfo对象出来,并且赋值。

private void init() {        mInfo = new MyInfo();        mInfo.setAge(20);        mInfo.setName("renk");    }

调用jni方法。 Log.e(“renk”, “age=” + getAge(mInfo));当然在点击事件里面调用了哦。在来看C中怎么操作对象,并且将对象里面的age取出来,加上100,返回给mainActivity。打印出来。
刚才,也讲了。c中是不能直接操作java对象或者类的。但是,我们可以换个思路,在C中通过该对象的类得到相应的属性。然后在C中创建一个结构体,来存传进来的对象里面的值。好,先把结构图创建出来。
当然如果不知道结构体是什么的同学,建议复习一下C知识。

typedef struct {    _Bool gen;    int ag;    char str[255];} MyCInfo;//创建一个结构MyCInfo myCInfo;

创建出一个结构体后,我们就会在c方法中使用它,并且对其赋值,这里我们创建的时候,由于C没有String类型,故使用了char[]来处理字符串类型的数据。下面看具体取值、赋值、计算并返回结果的代码:
这里写图片描述
还是刚才的操作,先拿到MainActivity的对象的引用。然后,再获取传入对象的的类的相关jclass,属性id(fieldId),当然这里面同样有用到方法签名,属性签名。属性名称。。不用多做解释。主要看取值。myCInfo.gen = (env)->GetBooleanField(env, obj, jfiGen);取值是个个的来,有不同方法,这顾名思义是去boolean值得性别值。还有,GetIntField()/GetLongField()/Get…等等。要去String属性值,就用到GetObjectField(),第一二个参数不多说,第三个参数就是该属性的属性Id值。这里还要讲jstring转换为C可操作的字符串。类型为char 。通过这句话myCInfo.ag += 100;我们就实现了。将传入的年龄加上100.这边就可直接返回了。Ok。相关Get系列方法包括静态的见下图:
这里写图片描述

这里还有多说一点的,就是,怎样将C中的结构体内容赋给java对象,让其返回呢。同理如下代码。

//赋值    jobject joInfo = (*env)->AllocObject(env, jclazz);    (*env)->SetBooleanField(env, joInfo, jfiGen, myCInfo.gen);    (*env)->SetIntField(env, joInfo, jfiAG, myCInfo.ag);    jstring jstrTmp = (*env)->NewStringUTF(env, myCInfo.str);    (*env)->SetObjectField(env, joInfo, jfiNam, jstrTmp);    (*env)->SetObjectField(env, jobj, jInfofieldid, joInfo);

很好理解了,通过上面的学习,这个很容易看懂的额。先通过类,创建其对象,然后通过Set系列方法,分别赋值给该jobject对象。然后return joInfo,就over了。不多说了。
最开始讲到的还要通过C调用java静态方法,这个也很简单,就是把调用的方法CallStatic***Method系列方法,就完成了,其他都不变。相关方法下面给出:
这里写图片描述
还有一个功能就是,怎样让C去改变activity中的某个属性的值呢。哈哈,如果看了上面的东西,到这里都应该会了哈。代码如下:

/* * Class:     com_china_MainActivity * Method:    changeString * Signature: ()Ljava/lang/String; */JNIEXPORT jstringJNICALL Java_com_china_MainActivity_changeString        (JNIEnv *env, jobject jobj) {    jclass jclazz = (*env)->GetObjectClass(env, jobj);    jfieldID jfieldid = (*env)->GetFieldID(env, jclazz, "mString", "Ljava/lang/String;");    jstring jstr = (jstring)(*env)->GetObjectField(env, jobj, jfieldid);    char *mString = jString2cString(env, jstr);    char *add = " add c";    strcat(mString, add);    jstring encoding = (*env)->NewStringUTF(env, mString);    (*env)->SetObjectField(env, jobj, jfieldid, encoding);    return encoding;}

同样的通过传入的jobject,获得该jclass的引用。获取该类的属性Id(fieldID),然后转换赋值或追加都可以,随你。最后别忘啦,调用Set系列方法。就实现了我们想要的功能。
好了,讲完了,有什么不懂的额,在线留言哦 。最后贴一下,C中的代码。谢谢。

#include <jni.h>#include <string.h>#include "com_china_MainActivity.h"char *jString2cString(JNIEnv *env, jstring jstr) {    char *mStr = "";    jclass jclazz = (*env)->FindClass(env, "java/lang/String");    jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "getBytes", "(Ljava/lang/String;)[B");    jstring encode = (*env)->NewStringUTF(env, "GB2312");    jbyteArray jarry = (jbyteArray)(*env)->CallObjectMethod(env, jstr, jmethodid, encode);    jsize length = (*env)->GetArrayLength(env, jarry);    jbyte *jby = (*env)->GetByteArrayElements(env, jarry, JNI_FALSE);    if (length > 0) {        mStr = (char *) malloc(length + 1);        memcpy(mStr, jby, length);        mStr[length] = 0;    }    (*env)->ReleaseByteArrayElements(env, jarry, jby, 0);    return mStr;}typedef struct {    _Bool gen;    int ag;    char str[255];} MyCInfo;MyCInfo myCInfo;/* * Class:     com_china_MainActivity * Method:    getString * Signature: (Lcom/china/MyInfo;)Ljava/util/List; * public native List<String> getString(MyInfo info); */JNIEXPORT voidJNICALL Java_com_china_MainActivity_getString        (JNIEnv *env, jobject jobj, jobject obj) {    MyCInfo jinfo ;    jclass jclazz = (*env)->FindClass(env,"com/china/MyInfo");    jfieldID jinfoGender = (*env)->GetFieldID(env,jclazz,"gender","Z");    jfieldID jinfoage = (*env)->GetFieldID(env,jclazz,"age","I");    jfieldID jinfoname = (*env)->GetFieldID(env,jclazz,"name","Ljava/lang/String;");    jinfo.gen = (*env)->GetBooleanField(env,obj,jinfoGender);    jinfo.ag = (*env)->GetIntField(env,obj,jinfoage);    jstring jstr = (jstring)(*env)->GetObjectField(env, obj, jinfoname);    char *str = (*env)->GetStringUTFChars(env, jstr, 0);    strcpy(jinfo.str, str);    (*env)->ReleaseStringUTFChars(env, jstr, str);    str ="";    jclass jclazz2 = (*env)->GetObjectClass(env, jobj);    jmethodID jmethodIDs = (*env)->GetMethodID(env, jclazz2, "getInfoName", "(Ljava/lang/String;)V");    (*env)->CallVoidMethod(env,jobj,jmethodIDs,jstr);};/* * Class:     com_china_MainActivity * Method:    getAge * Signature: (Lcom/china/MyInfo;)I * public native int getAge(MyInfo info); */JNIEXPORT jintJNICALL Java_com_china_MainActivity_getAge        (JNIEnv *env, jobject jobj, jobject obj) {    jclass jclaz = (*env)->GetObjectClass(env, jobj);    jclass jclazz = (*env)->FindClass(env, "com/china/MyInfo");    jfieldID jfiGen = (*env)->GetFieldID(env, jclazz, "gender", "Z");    jfieldID jfiAG = (*env)->GetFieldID(env, jclazz, "age", "I");    jfieldID jfiNam = (*env)->GetFieldID(env, jclazz, "name", "Ljava/lang/String;");    jfieldID jInfofieldid = (*env)->GetFieldID(env, jclaz, "mInfo", "Lcom/china/MyInfo;");    //取值    myCInfo.gen = (*env)->GetBooleanField(env, obj, jfiGen);//  myCInfo.ag = (*env)->GetIntField(env,obj,jfiAG);    jstring jstr = (jstring)(*env)->GetObjectField(env, obj, jfiNam);    char *str = (*env)->GetStringUTFChars(env, jstr, 0);    strcpy(myCInfo.str, str);    (*env)->ReleaseStringUTFChars(env, jstr, str);    str = "";    myCInfo.ag += 100;    //赋值    jobject joInfo = (*env)->AllocObject(env, jclazz);    (*env)->SetBooleanField(env, joInfo, jfiGen, myCInfo.gen);    (*env)->SetIntField(env, joInfo, jfiAG, myCInfo.ag);    jstring jstrTmp = (*env)->NewStringUTF(env, myCInfo.str);    (*env)->SetObjectField(env, joInfo, jfiNam, jstrTmp);    (*env)->SetObjectField(env, jobj, jInfofieldid, joInfo);    return myCInfo.ag;};/* * Class:     com_china_MainActivity * Method:    changeString * Signature: ()Ljava/lang/String; */JNIEXPORT jstringJNICALL Java_com_china_MainActivity_changeString        (JNIEnv *env, jobject jobj) {    jclass jclazz = (*env)->GetObjectClass(env, jobj);    jfieldID jfieldid = (*env)->GetFieldID(env, jclazz, "mString", "Ljava/lang/String;");    jstring jstr = (jstring)(*env)->GetObjectField(env, jobj, jfieldid);    char *mString = jString2cString(env, jstr);    char *add = " add c";    strcat(mString, add);    jstring encoding = (*env)->NewStringUTF(env, mString);    (*env)->SetObjectField(env, jobj, jfieldid, encoding);    return encoding;};/* * Class:     com_china_MainActivity * Method:    changeNumber * Signature: ()I */JNIEXPORT jintJNICALL Java_com_china_MainActivity_changeNumber        (JNIEnv *env, jobject jobj) {    jclass jclazz = (*env)->GetObjectClass(env, jobj);    jfieldID jfieldid = (*env)->GetFieldID(env, jclazz, "mNumber", "I");    jint aa = (*env)->GetIntField(env, jobj, jfieldid);    aa += 100;    (*env)->SetIntField(env, jobj, jfieldid, aa);    return aa;};/* * Class:     com_china_MainActivity * Method:    testGetString * Signature: (Ljava/lang/String;)Ljava/lang/String; * public native String testGetString(String test); */JNIEXPORT jstringJNICALL Java_com_china_MainActivity_testGetString        (JNIEnv *env, jobject jobj, jstring jstr) {};/* * Class:     com_china_MainActivity * Method:    getInt * Signature: (II)I */JNIEXPORT jintJNICALL Java_com_china_MainActivity_getInt        (JNIEnv *env, jobject jobj, jint ji, jint jj) {    jclass jclazz = (*env)->GetObjectClass(env, jobj);//    jclass jclazz = (*env)->FindClass(env, "com/china/MainActivity");//2.得到方法//jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);//最后一个参数是方法签名    jmethodID jmethodIDs = (*env)->GetMethodID(env, jclazz, "add", "(II)I");//3.实例化该类// jobject     (*AllocObject)(JNIEnv*, jclass);//    jobject jobject = (*env)->AllocObject(env, jclazz);//4.调用方法//jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);    jint value = (*env)->CallIntMethod(env, jobj, jmethodIDs, ji, jj);    return value;}
1 0
原创粉丝点击