如何用JNI创build对象?
我需要使用NDK和JNI来实现一些Android应用程序的function。
这里是C代码,我的担心,我写道:
#include <jni.h> #include <stdio.h> jobject Java_com_example_ndktest_NDKTest_ImageRef(JNIEnv* env, jobject obj, jint width, jint height, jbyteArray myArray) { jint i; jobject object; jmethodID constructor; jobject cls; cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest/Point"); //what should put as the second parameter? Is my try correct, according to what //you can find in .java file? I used this documentation: http://download.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp16027 constructor = (*env)->GetMethodID(env, cls, "<init>", "void(V)"); //http://download.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp16660 //Again, is the last parameter ok? object = (*env)->NewObject(env, cls, constructor, 5, 6); //I want to assign "5" and "6" to point.x and point.y respectively. return object; }
我的问题或多或less在代码中解释。 也许还有:是函数的返回types(jobject)好吗?
现在NDKTest.java:
package com.example.ndktest; import android.app.Activity; import android.widget.TextView; import android.os.Bundle; public class NDKTest extends Activity { /** Called when the activity is first created. */ public native Point ImageRef(int width, int height, byte[] myArray); public class Point { Point(int myx, int myy) { x = myx; y = myy; } int x; int y; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tv = new TextView(this); byte[] anArray = new byte[3]; for (byte i = 0; i < 3; i++) anArray[i] = i; Point point = ImageRef(2, 3, anArray); tv.setText(String.valueOf(point.x)); setContentView(tv); } static { System.loadLibrary("test"); } }
当我尝试运行代码时,它不起作用。
由于Point
是一个内部类,得到它的方式是
jclass cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest$Point");
内部类的$
约定在权威规范中并没有真正清楚地logging,但是在很多工作代码中根深蒂固,所以不太可能改变。 不过,如果你限制你的JNI代码来使用顶级类,它会感觉更健壮。
你需要一个构造函数,它以两个整数作为参数。 那个签名是(II)V
,所以:
constructor = (*env)->GetMethodID(env, cls, "<init>", "(II)V");
下一次,在你的代码中包含一些error handling,这样你就会知道哪个部分不起作用了!
规范是正确的,但在这种情况下有点误导。 GetMethodID
需要一个方法名称和一个方法签名。 规范说 :
要获得构造函数的方法ID,请提供<init>作为方法名称,void(V)作为返回types。
请注意,它说的是返回types ,而不是签名 。 尽pipevoid(V)
看起来与签名表面上相似,但是规范告诉你签名必须指定一个void(即V
)返回types。
()V
的无参数构造函数的正确签名。 如果构造函数有参数,就应该在括号之间进行描述,正如其他评论者所指出的那样。
你的代码存在一些问题。
首先,你为什么要创build自己的Point类,而不是使用库提供的android.graphics.Point?
其次,嵌套类的类规范是不同的 – 它会是“com / example / ndktest / NDKTest $ Point”。 类嵌套不同于包。
第三,我不认为JNI可以让你创build非静态嵌套类的实例。 你需要在创build对象时传递嵌套类对象的this
指针 – 没有这个参数。
最后,虽然我已经看到使用“void(V)”作为构造方法签名的指导,但这与方法签名的其余部分不符; 通常,具有两个int参数和void返回types的方法将是“(II)V”。
作为一个附注,我发现把从NDKtypes的原始types和数组传递到Java是非常简单的。 对象创build/访问是混乱的,很难debugging。
在JNI中,您始终可以使用javap
工具来查找方法签名。 只需运行javap -s com.example.ndktest.NDKTest
并从输出中复制方法签名。
三个步骤应该用JNI创buildPoint对象:
jobject Java_com_example_ndktest_NDKTest_ImageRef(JNIEnv* env, jobject obj, jint width, jint height, jbyteArray myArray) { ... jclass cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest$Point"); jmethodID constructor = (*env)->GetMethodID(env, cls, "<init>", "void(V)"); jobject object = (*env)->NewObject(env, cls, constructor, obj, 5, 6); ... }