如何使用JNI创建对象?

pmi*_*hna 51 java java-native-interface android android-ndk

我需要使用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;
}    
Run Code Online (Sandbox Code Playgroud)

在代码中或多或少地解释了我的问题.也许还有:函数的返回类型(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");
    }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试运行代码时,它不起作用.

Hen*_*olm 81

既然Point是一个内部阶级,那么获得它的方式就是

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

$权威规范中没有明确记录内部类的约定,但是在如此多的工作代码中根深蒂固,它不太可能改变.但是,如果您将JNI代码限制为使用顶级类,那么它会感觉更强大一些.

您需要一个以两个整数作为参数的构造函数.签名就是(II)V这样:

constructor = (*env)->GetMethodID(env, cls, "<init>", "(II)V");
Run Code Online (Sandbox Code Playgroud)

下一次,在代码中包含一些错误处理,这样你就可以知道它的哪一部分不起作用!

  • 注意:这个答案不起作用.请参阅下面Seva的答案中的注释(构造函数参数必须包含外部类) (3认同)

pbu*_*rka 13

规范是正确的,但在这种情况下有点误导.GetMethodID需要方法名称和方法签名.该规范说:

要获取构造函数的方法ID,请提供<init>作为方法名称,并将void(V)作为返回类型.

请注意,它表示返回类型,而不是签名.虽然void(V)看起来表面上与签名相似,但规范告诉您签名必须指定void(即V)返回类型.

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


Sev*_*yev 7

你的代码有些问题.

首先,为什么要创建自己的Point类而不是使用库提供的android.graphics.Point?

其次,嵌套类的类规范是不同的 - 它将是"com/example/ndktest/NDKTest $ Point".类嵌套与包不同.

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

最后,虽然我已经看到使用"void(V)"作为构造函数方法签名的指导,但这与其他方法签名不一致; 通常,具有两个int参数和void返回类型的方法将是"(II)V".

作为旁注,我发现将原始类型和从NDK类型化的原始数组传递给Java要简洁得多.对象创建/访问很麻烦,很难调试.

  • 实际上,您可以创建非静态嵌套类的实例,该方法有点不明显:嵌套类的所有构造函数都具有外部类类型的隐式第一个参数.因此对于上面的例子,使用Point类非静态,构造函数签名将是""<init>","(Lcom/example/ndktest/NDKTest; II)V"`.你可以通过在构建之后从classes目录运行`javap -s -p com.example.ndktest.NDKTest $ Point`来看到这一点. (10认同)
  • 文档说:"必须通过调用GetMethodID()并使用<init>作为方法名称并将void(V)作为返回类型来获取此ID." 当我第一次看到它时,我也对此感到困惑; 我认为这意味着"使用签名`void(V)`但它实际上意味着"当你构造签名时使用类型代码`V`(对于void).当然`void(V)`看起来像一个奇怪的签名,但是然后``<init>"`是一种指定构造函数的奇怪方法.无论如何,当所有的都是魔法时,混乱的东西真的很混乱!http://docs.oracle.com/javase/1.5.0/docs/guide/jni /spec/functions.html (2认同)

小智 5

在 JNI 中,您始终可以使用该javap工具来查找方法签名。只需运行javap -s com.example.ndktest.NDKTest并从输出中复制方法签名即可。