美文网首页
NDK基础(一)——方法调用

NDK基础(一)——方法调用

作者: 王志强_9380 | 来源:发表于2020-06-29 15:14 被阅读0次

方法定义

public native void unstaticFunction();
public static native void staticFunction();
extern "C"
JNIEXPORT void JNICALL
Java_com_example_ffmpegapplication_NativeStudy_unstaticFunction(JNIEnv *env, jobject instance) {

    // TODO

}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_ffmpegapplication_NativeStudy_staticFunction(JNIEnv *env, jclass type) {

    // TODO

}
  • 第一个参数JNIEnv是指向可用JNI函数表的接口指针,所有JNI的函数都可以通过这个参数来调用,比如env->GetObjectClass
  • 第二个参数jobject 是类实例的java对象引用。
    因为静态方法没有与实例绑定,因此通过第二个参数获取的是类引用(jclass )而不是引用实例

方法调用

env->GetObjectClass(nativeStudyTest);
env->GetObjectClass(env, nativeStudyTest);
  • 第一个是C++代码,JNIEnv实际上是C++类实例,JNI函数以成员函数的形式存在。因为JNI方法已经访问了当前的JNI环境,因此JNI方法调用不要求JNIEnv实例作参数
  • 第二个传入了JNIEnv的是C代码,JNIEnv是指向JNINativeInterface结构的指针,为访问任何一个JNI函数,该指针需要首先被解引用,因为C代码中的JNI函数不了解当前的JNI环境,JNIEnv实例应该作为第一个参数传递给每一个JNI函数调用者

JNI操作Java类

java代码

package com.example.ffmpegapplication;

import android.os.Handler;
import android.util.Log;

public class NativeStudyTest {

    private static NativeStudyTest mNativeStudyTest;
    private static Handler mHandler;
    public static int mIntfield = -1;
    public boolean mBooleanfield = false;

    private NativeStudyTest(){
        Log.e("","NativeStudyTest Construct");
    }

    private NativeStudyTest(Handler h, int intfield){
        Log.e("","NativeStudyTest Construct with field:"+intfield);
        mHandler = h;
        mIntfield = intfield;
    }

    private void playNativeStudyTest1() {
        Log.e("","NativeStudyTest playNativeStudyTest1");
    }

    private int playNativeStudyTest2() {
        Log.e("","NativeStudyTest playNativeStudyTest2");
        return 0;
    }

    private NativeStudyTest playNativeStudyTest3(Handler h, int intfield) {
        Log.e("","NativeStudyTest playNativeStudyTest3");
        if(mNativeStudyTest == null)
            mNativeStudyTest = new NativeStudyTest(h, intfield);
        return mNativeStudyTest;
    }

    public static void playNativeStudyTestStatic() {
        Log.e("","NativeStudyTest playNativeStudyTestStatic");
    }

    public static NativeStudyTest getInstance(Handler h, int intfield) {
        if(mNativeStudyTest == null)
            mNativeStudyTest = new NativeStudyTest(h, intfield);
        return mNativeStudyTest;
    }
}
创建一个对象
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_ffmpegapplication_NativeStudy_newJaveClass(JNIEnv *env, jclass type) {

    // TODO 创建一个对象
    jclass aClass = env->FindClass("com/example/ffmpegapplication/NativeStudyTest");
    jmethodID pConstructMethodID = env->GetMethodID(aClass, "<init>", "()V");
    jobject pJobject = env->NewObject(aClass, pConstructMethodID);

    return pJobject;
}

调用:

public static native NativeStudyTest newJaveClass();

NativeStudyTest mNativeStudyTest = newJaveClass();
Log.e("","NativeStudyTest newJaveClass mBooleanfield:"+mNativeStudyTest.mBooleanfield + "  mIntfield:" + mNativeStudyTest.mIntfield);    

打印:

01-19 12:44:07.024: E/www(28743): NativeStudyTest Construct
01-19 12:36:16.355: E/www(28743): NativeStudyTest newJaveClass mBooleanfield:false  mIntfield:-1

可以看到,创建了一个类的对象,并且调用了构造方法
注意这句GetMethodID(aClass, "<init>", "()V");第二个参数本应该是填方法名,这里填的却是<init>,这是因为java的构造函数是有名字,而且不是类名,构造函数的方法名字是<init>

修改属性
extern "C"
JNIEXPORT void JNICALL
Java_com_example_ffmpegapplication_NativeStudy_setJavaFeild(JNIEnv *env, jclass type,
                                                            jobject nativeStudyTest) {
    // TODO 改变参数
    jclass pJclass = env->GetObjectClass(nativeStudyTest);

    jfieldID pID_Booleanfield = env->GetFieldID(pJclass, "mBooleanfield", "Z");
    env->SetBooleanField(nativeStudyTest, pID_Booleanfield, true);

    jfieldID pID_Intfield = env->GetStaticFieldID(pJclass, "mIntfield", "I");
    jint intField = env->GetStaticIntField(pJclass, pID_Intfield);
    env->SetStaticIntField(pJclass, pID_Intfield, intField + 100);

}

调用

public static native void setJavaFeild(NativeStudyTest nativeStudyTest);

setJavaFeild(mNativeStudyTest);
Log.e("","NativeStudyTest setJavaFeild mBooleanfield:"+mNativeStudyTest.mBooleanfield + "  mIntfield:" + mNativeStudyTest.mIntfield);

打印:

01-19 12:36:16.355: E/www(28743): NativeStudyTest setJavaFeild mBooleanfield:true  mIntfield:99
  • GetFieldID:获取变量的ID
  • GetStaticFieldID:获取静态变量的ID
  • GetXXXField:获取变量的值,XXX是变量的类型,比如(GetIntField),静态变量需要在类型前面加Static,比如(GetStaticIntField)
  • SetXXXField:设置变量的值,XXX是变量的类型,比如(SetBooleanField),静态变量需要在类型前面加Static,比如(SetStaticBooleanField)

注意,获取属性的id以及设置属性值的时候,静态属性和非静态属性的方法是不一样的,如果搞错了,运行的时候会异常

调用方法
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_ffmpegapplication_NativeStudy_callJavaMethod(JNIEnv *env, jclass type, jobject h,
                                                              jint intfield) {
    // TODO 调用方法

    jclass aClass = env->FindClass("com/example/ffmpegapplication/NativeStudyTest");
    jobject dpobj = env->AllocObject(aClass);

    jmethodID pJmethodID1 = env->GetMethodID(aClass, "playNativeStudyTest1", "()V");
    env->CallVoidMethod(dpobj, pJmethodID1);

    jmethodID pJmethodID2 = env->GetMethodID(aClass, "playNativeStudyTest2", "()I");
    env->CallIntMethod(dpobj, pJmethodID2);

    //use of deleted weak global reference
    jmethodID pJmethodID3 = env->GetMethodID(aClass, "playNativeStudyTest3",
                                             "(Landroid/os/Handler;I)Lcom/example/ffmpegapplication/NativeStudyTest;");
    env->CallObjectMethod(dpobj, pJmethodID3, h, intfield);

    jmethodID pJmethodID4 = env->GetStaticMethodID(aClass, "playNativeStudyTestStatic", "()V");
    env->CallStaticVoidMethod(aClass, pJmethodID4);

    return dpobj;

}

调用

public static native NativeStudyTest callJavaMethod(Handler h, int intfield);

NativeStudyTest mNativeStudyTest2 = callJavaMethod(null, 10086);
Log.e("","NativeStudyTest callJavaMethod mBooleanfield:"+mNativeStudyTest.mBooleanfield + "  mIntfield:" + mNativeStudyTest.mIntfield);

打印:

01-19 12:36:16.355: E/www(28743): NativeStudyTest playNativeStudyTest1
01-19 12:36:16.355: E/www(28743): NativeStudyTest playNativeStudyTest2
01-19 12:36:16.355: E/www(28743): NativeStudyTest playNativeStudyTest3
01-19 12:36:16.355: E/www(28743): NativeStudyTest Construct with field:10086
01-19 12:36:16.355: E/www(28743): NativeStudyTest playNativeStudyTestStatic
01-19 12:36:16.355: E/www(28743): NativeStudyTest callJavaMethod mBooleanfield:true  mIntfield:10086
  • AllocObject:创建一个实例对象,由于调用的方法中,并没有把实例传进来,所以要调用类的非静态方法的话必须要实例化这个类的对象
  • 和操作变量一样,操作方法也有GetMethodID和GetStaticMethodID的区别,调用方法也分静态和非静态
  • JNI访问Java类会给应用程序增加额外的负担,进而导致性能下降。建议将所有需要的参数传递给native方法调用
参数相关

env->GetMethodID(aClass, "playNativeStudyTest3",
"(Landroid/os/Handler;I)Lcom/example/ffmpegapplication/NativeStudyTest;");

private NativeStudyTest playNativeStudyTest3(Handler h, int intfield);

我们看到,要找到一个方法,最后一个参数需要传入一个值来避免重载方法,这个值叫做方法的签名。
格式是:(参数1类型参数2类型参数3类型。。。)返回值类型
如果是Object类型的参数,那么就需要填 (L参数类的路径;),注意最后要加分号。
如果是基本类型,就是首字母的大写,比如byte就是B,int就是I
特殊的:boolean是Z,type[]是[Type,

相关文章

网友评论

      本文标题:NDK基础(一)——方法调用

      本文链接:https://www.haomeiwen.com/subject/bjgfbctx.html