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");
}
}
コードを実行しようとしても、機能しません。
Point
は内部クラスであるため、取得する方法は
jclass cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest$Point");
$
内部クラスの規則は、信頼できる仕様では実際に明確に文書化されていませんが、非常に多くの作業コードに定着しているため、変更されることはほとんどありません。それでも、JNIコードをトップレベルのクラスで動作するように制限した場合、feelより堅牢になります。
引数として2つの整数を取るコンストラクターが必要です。その署名は(II)V
、 そう:
constructor = (*env)->GetMethodID(env, cls, "<init>", "(II)V");
次回、コードにエラー処理を含めて、どの部分が機能しないかの手掛かりを得られるようにしてください!
仕様は正しいですが、この場合は少し誤解を招きます。 GetMethodID
には、メソッド名とメソッドシグネチャが必要です。 仕様によると :
コンストラクターのメソッドIDを取得するには、メソッド名として<init>を指定し、戻り値の型としてvoid(V)を指定します。
return typeであり、signatureではないことに注意してください。 void(V)
は表面上は署名に似ていますが、仕様では、署名でvoid(つまり、V
)戻り値の型を指定する必要があることが示されています。
引数なしのコンストラクターの正しい署名は()V
。コンストラクタに引数がある場合は、他のコメント作成者が指摘しているように、括弧で囲んで説明する必要があります。
コードに関するいくつかの問題。
まず、ライブラリが提供するAndroid.graphics.Pointを使用するのではなく、なぜ独自のPointクラスを作成するのですか?
第二に、ネストされたクラスのクラス仕様は異なります-「com/example/ndktest/NDKTest $ Point」です。クラスのネストはパッケージとは異なります。
第三に、JNIでは、非静的なネストされたクラスのインスタンスを作成できるとは思いません。オブジェクトの作成時に、ネストクラスオブジェクトのthis
ポインターを渡す必要があります。そのような引数はありません。
最後に、コンストラクターメソッドシグネチャとして「void(V)」を使用するためのガイダンスを見ましたが、これは他のメソッドシグネチャと一致していません。通常、2つのintパラメーターとvoid戻り値型を持つメソッドは「(II)V」になります。
サイドノートとして、プリミティブ型とプリミティブ型の配列をNDKからJavaに渡す方がずっときれいだと感じました。オブジェクトの作成/アクセスは複雑で、デバッグが困難です。
JNIでは、javap
ツールを常に使用してメソッドシグネチャを見つけることができます。ただ走れ javap -s com.example.ndktest.NDKTest
そして、出力からメソッドシグネチャをコピーします。
3つのステップで、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);
...
}