2011-08-31 62 views
40

我需要使用NDK和JNI将一些函数实现到Android应用程序中。如何用JNI创建对象?

这里的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"); 
    } 
} 

当我尝试运行代码,这是行不通的。

+3

请解释“不起作用”。 –

+1

虽然你可能分别是指“分别”,但我认为重要的是要尊重你的对象。 :) – quasimodo

+0

@quasimodo你说得对。 :)我编辑了错误,谢谢。 – pmichna

回答

67

由于Point是一个内部类,得到它的方式是

jclass cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest$Point"); 

的内部类的$约定没有真正明确的权威的规范文件,但在这么多的工作代码的根深蒂固,以至于它不太可能改变。不过,如果您限制JNI代码以使用顶级类,那么会觉得更健壮一些。

您需要一个构造函数,它将两个int作为参数。该签名是(II)V,所以:

constructor = (*env)->GetMethodID(env, cls, "<init>", "(II)V"); 

下一次,包括一些错误处理代码中的,这样你就会有一个线索,其中一部分不能正常工作!

+1

注意:此答案无效。在Seva下面的答案中看到为什么(构造函数的参数必须包含外部类) – Colin

+0

@Colin:是的,我忽略了'Point'不是静态的。 –

4

您的代码存在一些问题。

首先,你为什么要创建自己的Point类而不是使用库提供的android.graphics.Point?其次,嵌套类的类规格是不同的 - 它会是“com/example/ndktest/NDKTest $ Point”。类嵌套与包不同。

第三,我不认为JNI可以让你创建非静态嵌套类的实例。你需要在对象创建时传递嵌套类对象'this指针 - 没有这样的参数。

最后,虽然我已经看到使用“void(V)”作为构造方法签名的指导,但这与方法签名的其余部分不符;通常,具有两个int参数和void返回类型的方法将是“(II)V”。作为一个方面说明,我发现将原始类型和从NDK类型化的基元数组传递给Java要简单得​​多。对象创建/访问很混乱,很难调试。

+0

@Hennig Makholm 我创建了我自己的Point类,例如。它可以像_foo_一样。我改变了我的班级到最高级别,将“void(V)改为”(II)V“,现在它可以工作。 – pmichna

+2

该文档说:”此ID必须通过调用GetMethodID(),作为方法名称void(V)作为返回类型“当我第一次阅读它时,我也被这个困惑;我认为它的意思是”使用签名'void(V)',但它实际上意味着“使用类型代码'V' )当你构建签名“。当然'void(V)'看起来像一个奇怪的签名,但是''“'是一种奇怪的方式来指定一个构造函数。无论如何,当所有东西都是魔法的时候,混乱的东西真的让人困惑! http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html – steveha

+9

你实际上可以创建非静态嵌套类的实例,该方法有点不明显:全部嵌套类的构造函数具有外部类的隐式第一个参数。因此,对于上面的例子,在Point类非静态的情况下,构造函数签名将是“”,“(Lcom/example/ndktest/NDKTest; II)V”。您可以通过在构建完成后从classes目录运行'javap -s -p com.example.ndktest.NDKTest $ Point'来看到这一点。 – benkc

9

该规范是正确的,但在这种情况下有点误导。 GetMethodID需要方法名称和方法签名。所述specification says

要获得构造的方法ID,供应<初始化>作为方法名和空隙(V)作为返回类型。

注意,它说返回类型,不签名。尽管void(V)看起来表面上与签名类似,但规范告诉您签名必须指定void(即,V)返回类型。

无参数构造函数的正确签名是()V。如果构造函数有参数,则应像其他评论者所指出的那样在括号之间进行描述。

0

三个步骤应该JNI创建Point对象:

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); 
    ... 
} 
+1

这很有用,它显示了一个简洁的代码示例。代码将失败,因为方法签名是错误的。它应该是'“(II)V”',如其他答案中所列。 – ephemer

1

在JNI你可以随时用于查找方法签名javap工具。只需运行javap -s com.example.ndktest.NDKTest并从输出中复制方法签名。