How to create an object with JNI?
I need to implement some functions into an Android application using NDK and thus JNI.
Here's the C code, with my concerns, that I wrote:
#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;
}
My problems are more or less explained inside the code. Maybe also: is the return type of the function 开发者_开发百科(jobject) ok?
Now the 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");
}
}
When I try to run the code, it doesn't work.
Since Point
is an inner class, the way to get it would be
jclass cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest$Point");
The $
convention for inner classes is not really clearly documented in the authoritative specs, but is entrenched in so much working code that it's unlikely to change. Still, it would feel somewhat more robust if you restricted your JNI code to work with top-level classes.
You want a constructor that takes two ints as arguments. The signature for that is (II)V
, so:
constructor = (*env)->GetMethodID(env, cls, "<init>", "(II)V");
Next time, include some error handling in your code, such that you'll have a clue which part of it doesn't work!
The specification is correct, but a bit misleading in this case. GetMethodID
requires a method name and a method signature. The specification says:
To obtain the method ID of a constructor, supply <init> as the method name and void (V) as the return type.
Note that it says return type, not signature. Although void(V)
looks superficially similar to a signature, the specification is telling you that the signature must specify a void (that is, V
) return type.
The correct signature for a no-argument constructor is ()V
. If the constructor has arguments, they should be described between the parentheses, as other commenters have noted.
Some problems with your code.
First, why are you creating your own Point class rather than using library-provided android.graphics.Point?
Second, the class spec for nested classes is different - it would be "com/example/ndktest/NDKTest$Point". Class nesting is different from packages.
Third, I don't think JNI lets you create instances of nonstatic nested classes. You need to pass the nesting class object' this
pointer at object creation - there's no such argument.
Finally, while I've seen the guidance to use "void(V)" as a constructor method signature, but this is out of line with the rest of method signatures; normally, a method with two int parameters and void return type would be "(II)V".
As a side note, I found it much cleaner to pass primitive types and arrays of primitive typed from NDK to Java. Object creation/access is messy and hard to debug.
In JNI you can always use the javap
tool for finding method signatures. Just run javap -s com.example.ndktest.NDKTest
and copy the method signatures from the output.
Three steps should create the Point object with JNI:
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);
...
}
I use:
jclass clazz = env->FindClass ( "ISOInfoEx" );
jobject obj = env->AllocObject ( clazz );
This will create an object of the class ISOInfoEx. In my case i use it later in code as a structure.
精彩评论