方法定义
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,
网友评论