Android jni/ndk编程三:native访问java

一.访问静态字段

Java层的field和method,不管它是public,还是package、private和protected,从 
JNI都可以访问到,Java面向语言的封装性不见了。 
静态字段和非静态的字段访问方式不同,jni规范提供了一系列带static标示的访问静态字段的函数:

    jobject     (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
    jboolean    (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID);
    jbyte       (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
    jchar       (*GetStaticCharField)(JNIEnv*, jclass, jfieldID);
    jshort      (*GetStaticShortField)(JNIEnv*, jclass, jfieldID);
    jint        (*GetStaticIntField)(JNIEnv*, jclass, jfieldID);
    jlong       (*GetStaticLongField)(JNIEnv*, jclass, jfieldID);
    jfloat      (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID) __NDK_FPABI__;
    jdouble     (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID) __NDK_FPABI__;

    void        (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
    void        (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean);
    void        (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
    void        (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar);
    void        (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort);
    void        (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint);
    void        (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong);
    void        (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat) __NDK_FPABI__;
    void        (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble) __NDK_FPABI__;

访问流程:

  1. 获得java层的类:jclass cls = (*env)->GetObjectClass(env, obj);
  2. 获得字段的ID:jfieldID fid = (*env)->GetStaticFieldID(env, cls, “s”, “Ljava/lang/String;”);
  3. 获得字段的值:jstring jstr = (*env)->GetStaticObjectField(env, cls, fid);
  4. 设置字段的值:(*env)->SetStaticObjectField(env, cls, fid, jstr);

按照以上的流程,参照上面访问静态字段的函数定义,写如下测试代码:


void  native_accessJava(JNIEnv * env, jobject obj){
    LOGE("lstr:native_accessJava");
    //1. 获得java层的类:jclass cls = (*env)->GetObjectClass(env, obj);
    jclass cls = (*env)->GetObjectClass(env, obj);
    //2. 获得字段的ID:jfieldID fid = (*env)->GetStaticFieldID(env, cls, "s", "Ljava/lang/String;");
    jfieldID fid = (*env)->GetStaticFieldID(env, cls, "s", "Ljava/lang/String;");
    if (fid == NULL) {
        LOGE("get feild id error");
        return; /* failed to find the field */
    }
    //3. 获得字段的值:jstring jstr = (*env)->GetStaticObjectField(env, cls, fid);
    jstring jstr = (*env)->GetStaticObjectField(env, cls, fid);
    LOGE("lstr:native_accessJava");
    const char * lstr = (*env)->GetStringUTFChars(env,jstr,NULL);
    LOGE("lstr: %s",lstr);
    (*env)->ReleaseStringUTFChars(env,jstr,lstr);
    //4. 设置字段的值:(*env)->SetStaticObjectField(env, cls, fid, jstr);
    jstr = (*env)->NewStringUTF(env, "jni set");
    if (jstr == NULL) {
        return; /* out of memory */
    }
    (*env)->SetStaticObjectField(env, cls, fid, jstr);
}

注册方法的数组:

static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
};  

java中访问的代码:

public class MainActivity extends AppCompatActivity {
    TextView textView = null;
    static String s = "java str";
    static {
        System.loadLibrary("hello");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        accessJava();
        textView.setText(s);
    }
    public native  int sayHello(int []arr);
    public native String[] arrayTry(String [] arr);
    public native void accessJava();
}

在jni代码所在目录执行adk-build命令,把编译生成的libhello.so文件拷贝到Android工程的jniLibs目录下,运行android程序即可看到现象。

二.访问实例字段

有了访问静态字段的经历,在去写访问实例字段的代码就简单多了,这里总结下使用流程:

  1. 获得java层的类:jclass cls = (*env)->GetObjectClass(env, obj);
  2. 获得字段的ID:jfieldID fid = (*env)->GetFieldID(env, cls, “ss”, “Ljava/lang/String;”);
  3. 获得字段的值:jstring jstr = (*env)->GetObjectField(env, obj, fid);
  4. 设置字段的值:(*env)->SetObjectField(env, obj, fid, jstr);

在写代码之前,先看一下jni.h中定义的访问实例字段的函数:

    jfieldID    (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);

    jobject     (*GetObjectField)(JNIEnv*, jobject, jfieldID);
    jboolean    (*GetBooleanField)(JNIEnv*, jobject, jfieldID);
    jbyte       (*GetByteField)(JNIEnv*, jobject, jfieldID);
    jchar       (*GetCharField)(JNIEnv*, jobject, jfieldID);
    jshort      (*GetShortField)(JNIEnv*, jobject, jfieldID);
    jint        (*GetIntField)(JNIEnv*, jobject, jfieldID);
    jlong       (*GetLongField)(JNIEnv*, jobject, jfieldID);
    jfloat      (*GetFloatField)(JNIEnv*, jobject, jfieldID) __NDK_FPABI__;
    jdouble     (*GetDoubleField)(JNIEnv*, jobject, jfieldID) __NDK_FPABI__;

    void        (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
    void        (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
    void        (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
    void        (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);
    void        (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);
    void        (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);
    void        (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
    void        (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat) __NDK_FPABI__;
    void        (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble) __NDK_FPABI__;

可以看到访问实例字段的函数和访问静态字段的函数在名字上就有区别,而且一定要注意的是,访问实例字段函数的第三个参数是jobject,是一个对象,而访问静态字段的第三个参数是jclass,是一个类。

我们使用上面给出的函数和我们总结的使用流程写如下代码:

void  native_accessinstanceJava(JNIEnv * env, jobject obj){
    LOGE("lstr:native_accessinstanceJava");
    //1. 获得java层的类:jclass cls = (*env)->GetObjectClass(env, obj);
    jclass cls = (*env)->GetObjectClass(env, obj);
    //2. 获得字段的ID:jfieldID fid = (*env)->GetFieldID(env, cls, "ss", "Ljava/lang/String;");
    jfieldID fid = (*env)->GetFieldID(env, cls, "ss", "Ljava/lang/String;");
    if (fid == NULL) {
        LOGE("get feild id error");
        return; /* failed to find the field */
    }
    //3. 获得字段的值:jstring jstr = (*env)->GetObjectField(env, cls, fid);
    jstring jstr = (*env)->GetObjectField(env, obj, fid);
    const char * lstr = (*env)->GetStringUTFChars(env,jstr,NULL);
    LOGE("lstr: %s",lstr);
    (*env)->ReleaseStringUTFChars(env,jstr,lstr);
    //4. 设置字段的值:(*env)->SetObjectField(env, cls, fid, jstr);
    jstr = (*env)->NewStringUTF(env, "jni set");
    if (jstr == NULL) {
        return; /* out of memory */
    }
    (*env)->SetObjectField(env, obj, fid, jstr);
}

注册方法的数组:

static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
{"accessinstanceJava","()V",(void *)native_accessinstanceJava},

};  

JNI_OnLoad等方法请参考之前的博客。 
java层调用很非常简单,这里就不贴了。

三.访问静态方法

静态方法的访问总结为两步: 
? 首先通过GetStaticMethodID在给定类中查找方法 
如:jmethodID mid = (*env)->GetStaticMethodID(env,cls,”changeStr”,”()V”); 
? 通过CallStaticMethod调用 
如:(*env)->CallStaticVoidMethod(env, cls, mid); 
jni中定义的访问静态方法的函数有如下一些:

    jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);

    jobject     (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
    jobject     (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    jobject     (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
    jboolean    (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
    jboolean    (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
                        va_list);
    jboolean    (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID,
                        jvalue*);
    jbyte       (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
    jbyte       (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    jbyte       (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
    jchar       (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
    jchar       (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    jchar       (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
    jshort      (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
    jshort      (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    jshort      (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
    jint        (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
    jint        (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    jint        (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
    jlong       (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
    jlong       (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    jlong       (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
    jfloat      (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...) __NDK_FPABI__;
    jfloat      (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list) __NDK_FPABI__;
    jfloat      (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) __NDK_FPABI__;
    jdouble     (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...) __NDK_FPABI__;
    jdouble     (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list) __NDK_FPABI__;
    jdouble     (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) __NDK_FPABI__;
    void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
    void        (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    void        (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);

结合上面总结的流程和jni.h中定义的函数,写如下测试代码: 
代码功能:调用java层的静态方法,修改静态字段的值,把修改后的字段的值使用TextView显示出来。

void  native_staticMethod(JNIEnv * env, jobject obj){
    LOGE("native_staticMethod");
    //1.获得类中方法id
    jclass cls = (*env)->GetObjectClass(env, obj);
    jmethodID mid = (*env)->GetStaticMethodID(env,cls,"changeStr","()V");
    if (mid == NULL) {
        LOGE("GetStaticMethodID error");
        return; /* method not found */
    }
    LOGE("GetStaticMethodID sucess");

    //2.调用CallStatic<ReturnValueType>Method函数调用对应函数.
    (*env)->CallStaticVoidMethod(env, cls, mid);
}

注册方法的数组:

static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
{"accessinstanceJava","()V",(void *)native_accessinstanceJava},
{"staticMethod","()V",(void *)native_staticMethod},
};  

添加了staticMethod方法的注册。 
java层的调用:

public class MainActivity extends AppCompatActivity {
    TextView textView = null;
    static String s = "java str";
    String ss = "instance str";
    static {
        System.loadLibrary("hello");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        staticMethod();
        textView.setText(s);
    }
    public native  int sayHello(int []arr);
    public native String[] arrayTry(String [] arr);
    public native void accessJava();
    public native void accessinstanceJava();
    public native void staticMethod();
    public static void changeStr(){
        s = "chang str";
    }
}

四.访问实例方法

访问实例方法与访问静态方法类似,要注意的主要是:实例方法是属于对象jobject的,而静态方法是属于类的。

4.1普通实例方法

访问普通的实例方法的步骤还是总结为两步: 
? 首先通过GetMethodID在给定类中查找方法 
如:jmethodID mid = (*env)->GetMethodID(env,cls,”changeStr”,”()V”); 
? 通过CallMethod调用 
如:(*env)->CallStaticVoidMethod(env, obj, mid);

jni.h中定义的访问实例方法的相关函数有:

    jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);

    jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
    jobject     (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jobject     (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
    jboolean    (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
    jboolean    (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jboolean    (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
    jbyte       (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
    jbyte       (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jbyte       (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
    jchar       (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
    jchar       (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jchar       (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
    jshort      (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
    jshort      (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jshort      (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
    jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    jint        (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jint        (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
    jlong       (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
    jlong       (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jlong       (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
    jfloat      (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;
    jfloat      (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;
    jfloat      (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;
    jdouble     (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;
    jdouble     (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;
    jdouble     (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;
    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    void        (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    void        (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);

    jobject     (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...);
    jobject     (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass,
                        jmethodID, va_list);
    jobject     (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass,
                        jmethodID, jvalue*);
    jboolean    (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...);
    jboolean    (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass,
                         jmethodID, va_list);
    jboolean    (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass,
                         jmethodID, jvalue*);
    jbyte       (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...);
    jbyte       (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass,
                        jmethodID, va_list);
    jbyte       (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass,
                        jmethodID, jvalue*);
    jchar       (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...);
    jchar       (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass,
                        jmethodID, va_list);
    jchar       (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass,
                        jmethodID, jvalue*);
    jshort      (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...);
    jshort      (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass,
                        jmethodID, va_list);
    jshort      (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass,
                        jmethodID, jvalue*);
    jint        (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...);
    jint        (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass,
                        jmethodID, va_list);
    jint        (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass,
                        jmethodID, jvalue*);
    jlong       (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...);
    jlong       (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass,
                        jmethodID, va_list);
    jlong       (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass,
                        jmethodID, jvalue*);
    jfloat      (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...) __NDK_FPABI__;
    jfloat      (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass,
                        jmethodID, va_list) __NDK_FPABI__;
    jfloat      (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass,
                        jmethodID, jvalue*) __NDK_FPABI__;
    jdouble     (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...) __NDK_FPABI__;
    jdouble     (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass,
                        jmethodID, va_list) __NDK_FPABI__;
    jdouble     (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass,
                        jmethodID, jvalue*) __NDK_FPABI__;
    void        (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass,
                        jmethodID, ...);
    void        (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass,
                        jmethodID, va_list);
    void        (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass,
                        jmethodID, jvalue*);

4.2被子类覆盖的父类方法

我们看到了很多Nonvirtual方法,这是jni提供用来访问被子类赋给的父类的方法的,使用步骤如下: 
调用被子类覆盖的父类方法: JNI支持用CallNonvirtualMethod满足这类需求: 
? GetMethodID获得method ID 
? 调用CallNonvirtualVoidMethod, CallNonvirtualBooleanMethod 
上述,等价于如下Java语言的方式: 
super.f(); 
CallNonvirtualVoidMethod可以调用构造函数

4.3构造函数

你可以像调用实例方法一样,调用构造方法,只是此时构造函数的名称叫做””.

综合上面三个知识点,我们设计如下代码时间这些知识: 
1.在c函数中调用String类的构造函数新建一个字符串对象。 
2.调用java的实例方法,传入我们1中构建的字符串对象,修改TextView的内容。 
代码如下:

void  native_instanceMethod(JNIEnv * env, jobject obj){
    LOGE("native_instanceMethod");
    //1.使用String类的构造函数构造String
    //1.1找到String类
    jclass  stringClass = (*env)->FindClass(env, "java/lang/String");
    if (stringClass == NULL) {
        LOGE("FindClass error");
        return;
    }
    //1.2找到String类的构造函数
    jmethodID cid = (*env)->GetMethodID(env, stringClass, "<init>", "([C)V");
    if (cid == NULL) {
        LOGE("GetMethodID <init> error");
        return; /* exception thrown */
    }
    //1.3创建字符数组
    jint len = 10;
    jcharArray elemArr = (*env)->NewCharArray(env, len);
    if (elemArr == NULL) {
        LOGE("NewCharArray  error");
        return; /* exception thrown */
    }
    jchar java_char[]={97,98,99,100,101,102,103,104,105,106};//abcdefghij
    //1.4设置字符数组
    (*env)->SetCharArrayRegion(env, elemArr, 0, len, java_char);
    //1.5 创建一个字符串对象
    jstring result = (*env)->NewObject(env, stringClass, cid, elemArr);
    //2.获得类中方法id
    jclass cls = (*env)->GetObjectClass(env, obj);
    jmethodID mid = (*env)->GetMethodID(env,cls,"changeTextView","(Ljava/lang/String;)V");
    if (mid == NULL) {
        LOGE("GetMethodID error");
        return; /* method not found */
    }
    LOGE("GetMethodID sucess");

    //2.调用Call<ReturnValueType>Method函数调用对应函数.
    (*env)->CallVoidMethod(env, obj, mid,result);
}

注册方法的数组:

static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
{"accessinstanceJava","()V",(void *)native_accessinstanceJava},
{"staticMethod","()V",(void *)native_staticMethod},
{"instanceMethod","()V",(void *)native_instanceMethod},

};  

java层调用:

static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
{"accessinstanceJava","()V",(void *)native_accessinstanceJava},
{"staticMethod","()V",(void *)native_staticMethod},
{"instanceMethod","()V",(void *)native_instanceMethod},

};  

访问实例方法的实验到此结束。

五.性能与优化

5.1缓存Field 和 Method IDs

每次获得Field和Method IDS都比较耗时,如果我们需要多次获取他们,那就应该把它们缓存起来,这样以后用的时候就可以直接用了,从而节约了时间。 
缓存的方式可以使用局部static字段缓存,也可以在类的初始化时,一次性缓存好全部的Field 和 Method IDs。 
上述第一次使用缓存的方式,每次都有与NULL的判断,并且可能有一个无害的竞争条件。 
而初始化类时,同时初始化JNI层对该类成员的缓存,可以弥补上述缺憾。

5.2影响jni回调性能的因素

首先比较Java/native和Java/Java 
前者因下述原因可能会比后者慢: 
? Java/native与Java/Java的调用约定不同. 所以,VM必须在调用前,对参数和调用 
栈做特殊准备 
? 常用的优化技术是内联. 相比Java/Java调用,Java/native创建内联方法很难 
粗略估计:执行一个Java/native调用要比Java/Java调用慢2-3倍. 也可能有一些VM实 
现,Java/native调用性能与Java/Java相当。(此种虚拟机,Java/native使用Java/Java 
相同的调用约定)。 
其次比较native/Java与Java/Java 
native/Java调用效率可能与Java/Java有10倍的差距,因为VM一般不会做Callback的 
优化。

时间: 2025-01-20 12:48:29

Android jni/ndk编程三:native访问java的相关文章

超全Android JNI&NDK编程总结

由于网上关于JNI/NDK相关的知识点介绍的比较零散而且不具备参照性,所以写了这篇JNI/NDK学习笔记,便于作为随时查阅的工具类型的文章,本文主要的介绍了在平时项目中常用的命令.JNI数据类型.签名等,便于查阅相关资料.文末相关参考资料比较适合刚接触或者不熟悉Android NDK开发的朋友参阅. 常用命令 javac 编译java源文件生成.class文件 由于JNI对应的头文件由javah工具根据对应的.class文件生成,所以在进行JNI编程之前,写好Java代码后需要先编译,在使用ja

Android jni/ndk编程二:jni数据类型转换(primitive,String,array)

一.数据类型映射概述 从我们开始jni编程起,就不可能避开函数的参数与返回值的问题.java语言的数据类型和c/c++有很多不同的地方,所以我们必须考虑当在java层调用c/c++函数时,怎么正确的把java的参数传给c/c++函数,怎么正确的从c/c++函数获取正确的函数返回值:反之,当我们在c/c++中使用java的方法或属性时,如何确保数据类型能正确的在java和c/c++之间转换. 回顾我们上一篇文章中的那个c函数: #include <stdio.h> #include <jn

Android jni/ndk编程五:jni异常处理

在Java的编程中,我们经常会遇到各种的异常,也会处理各种的异常.处理异常在java中非常简单,我们通常会使用try-catch-finally来处理,也可以使用throw简单抛出一个异常.那么在jni编程的时候我们又是如何处理异常的呢? 异常处理流程 jni规范已经给我们做好了所有需要做的事情.回想一下处理异常的过程: 我们首先要在有可能产生异常的地方检测异常 处理异常 是的,我觉得异常的处理就是可以简单的总结为两步,在异常处理中我们通常会打印栈信息等.在jni编程中,异常处理的思路应该也与之

Android jni/ndk编程四:jni引用类型

一.JNI引用类型 JNI支持三种类型的 opaque reference:local references, global references,和weak global references,下面我们将逐一探讨. local references 大部分JNI 函数都会创建LocalRef,如NewObject创建一个实例,并返回一个指向该实例的LocalRef.LocalRef只在本线程的 native method中有效. 一但native method返回,LocalRef 将被释放.

Android jni本地编程入门

在某些情况下,java编程已经不能满足我们的需要,比如一个复杂的算法处理,这时候就需要用到jni技术: jni : java native interface jni 其实就是java和c/cpp之间进行通信的一个接口规范,java可以调用c/cpp里面的函数,同样,c/cpp也可以调用java类的方法: jni开发工具ndk的安装:在最新的ndk版本中,安装ndk很简单,只需要装ndk的路径配置到系统环境变量中即可:在编译的时候,进入工程根目录:执行命令  ndk-build  即可完成编译:

两分钟学会Android平台NDK编程(无须Eclipse和cygwin,可使用命令行打包多个so)

之前在进行cocos2dx开发时,已经详细介绍了如何将win32的c++代码移植到Android平台,当再次回顾时,发现一些基础的东西理解并不是很彻底,今天使用Android NDK提供的一个例子做一个简单的移植.在进行该demo前,请确认你已经配置了Android开发环境和安装了最新的Android NDK. 1.创建Android项目 创建一个Android项目 , 包名是com.example.hellojni,创建一个Activity作为程序进入的Acitivity,命名为HelloJn

Android平台NDK编程

转自:http://blog.csdn.net/wangbin_jxust/article/details/37389383 之前在进行cocos2dx开发时,已经详细介绍了如何将win32的c++代码移植到Android平台,当再次回顾时,发现一些基础的东西理解并不是很彻底,今天使用Android NDK提供的一个例子做一个简单的移植.在进行该demo前,请确认你已经配置了Android开发环境和安装了最新的Android NDK. 1.创建Android项目 创建一个Android项目 ,

Android JNI/NDK开发之基本姿势&lt;二&gt;

接着上篇文章Android JNI/NDK开发之基本姿势<一>,今天我们讲讲怎么调用native方法,以及native调用java层方法,上篇文章我们已经完成了编译工作,今天我们就来学习下怎么去调用,在这之前我们更改下之前的JniDemo.c采用C++语法来实现,满足下口味不同的人,更改如下: // // Created by Q.Jay on 2016/5/8. // #include <com_jay_ndkdemo_JniDemo.h> /* * Class: com_jay

一天掌握Android JNI本地编程 快速入门

一.JNI(Java Native Interface)  1.什么是JNI:               JNI(Java Native Interface):java本地开发接口 JNI是一个协议,这个协议用来沟通java代码和外部的本地代码(c/c++) 外部的c/c++代码也可以调用java代码  2.为什么使用JNI: 效率上 C/C++是本地语言,比java更高效 代码移植,如果之前用C语言开发过模块,可以复用已经存在的c代码 java反编译比C语言容易,一般加密算法都是用C语言编写