我需要使用NDK和JNI将一些函数实现到Android应用程序中。
以下是我编写的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;
}
我的问题在代码中或多或少都有解释。也许还有:函数的返回类型(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");
}
}
当我尝试运行代码时,它不起作用。
答案 0 :(得分:77)
由于Point
是一个内部类,因此获取它的方法是
jclass cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest$Point");
内部类的$
约定并没有在权威规范中明确记录,但是在如此多的工作代码中根深蒂固,以至于它不太可能改变。但是,如果您将JNI代码限制为使用顶级类,那么感觉会更强大。
您需要一个以两个整数作为参数的构造函数。签名是(II)V
,所以:
constructor = (*env)->GetMethodID(env, cls, "<init>", "(II)V");
下次,在代码中包含一些错误处理,这样你就可以知道哪一部分不起作用了!
答案 1 :(得分:13)
规范是正确的,但在这种情况下有点误导。 GetMethodID
需要方法名称和方法签名。 specification says:
要获取构造函数的方法ID,请提供&lt; init&gt;作为方法名称和void(V)作为返回类型。
请注意,它表示返回类型,而不是签名。虽然void(V)
表面看起来与签名相似,但规范告诉您签名必须指定void(即V
)返回类型。
无参构造函数的正确签名是()V
。如果构造函数有参数,则应在括号之间描述它们,正如其他评论者所指出的那样。
答案 2 :(得分:6)
您的代码存在一些问题。
首先,为什么要创建自己的Point类而不是使用库提供的android.graphics.Point?
其次,嵌套类的类规范是不同的 - 它将是“com / example / ndktest / NDKTest $ Point”。类嵌套与包不同。
第三,我不认为JNI允许您创建非静态嵌套类的实例。你需要在创建对象时传递嵌套类对象'this
指针 - 没有这样的参数。
最后,虽然我已经看到使用“void(V)”作为构造函数方法签名的指导,但这与其他方法签名不一致;通常,具有两个int参数和void返回类型的方法将是“(II)V”。
作为旁注,我发现将原始类型和从NDK类型化的原始数组传递给Java要简洁得多。对象创建/访问很麻烦,很难调试。
答案 3 :(得分:4)
在JNI中,您始终可以使用javap
工具查找方法签名。只需运行javap -s com.example.ndktest.NDKTest
并从输出中复制方法签名。
答案 4 :(得分:1)
三个步骤应该使用JNI创建Point对象:
{{1}}
答案 5 :(得分:0)
我使用:
jclass clazz = env->FindClass ( "ISOInfoEx" );
jobject obj = env->AllocObject ( clazz );
这将创建一个 ISOInfoEx 类的对象。就我而言,我稍后在代码中将其用作结构。