2011-08-31 8 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コードがトップレベルクラスで動作するように制限した場合、の方がやや頑強です。

引数として2つのintを取るコンストラクタが必要です。そのための署名はそう、(II)Vです:

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

次回は、あなたがそれの一部が動作しないの手掛かりを持っていますように、あなたのコードで扱ういくつかの誤りが含まれます!

+1

注:この回答はうまくいきません。理由(コンストラクタの引数には外部クラスが含まれている必要があります)については、Sevaの答えのコメントを参照してください。 – Colin

+0

@Colin:ええと、私は 'Point'は静的ではないことを見落としました。 –

4

コードにいくつかの問題があります。

まず、ライブラリ提供のandroid.graphics.Pointではなく、独自のPointクラスを作成するのはなぜですか?

第2に、ネストされたクラスのクラス仕様は異なります。「com/example/ndktest/NDKTest $ Point」です。クラスネストはパッケージとは異なります。

第3に、JNIでは非静的ネストされたクラスのインスタンスを作成することはできません。オブジェクト作成時にネストクラスオブジェクト 'thisポインタを渡す必要があります。そのような引数はありません。

最後に、 "void(V)"をコンストラクタメソッドのシグネチャとして使用するガイダンスを見てきましたが、これは残りのメソッドシグネチャとは異なります。通常、2つのintパラメータとvoid戻り型を持つメソッドは "(II)V"になります。

副作用として、プリミティブ型とNDKからJavaに型付けされたプリミティブの配列を渡すのがはるかにクリーンであることがわかりました。オブジェクトの作成/アクセスが面倒でデバッグが難しい。

+0

@Hennig Makholm 私は自分のPointクラスを作成しました。それは_foo_だったかもしれません。 – pmichna

+2

ドキュメントでは、「このIDはメソッド名としてでGetMethodID()を呼び出すことで取得する必要があります。 "私はこれを最初に読んだときに私が混乱していた;それは" void(V) "という署名を使用することを意味すると思ったが、実際には" use type code " )署名を作成するとき "。確かに 'void(V)'は奇妙な署名のように見えますが、 '' ''はコンストラクタを指定する奇妙な方法です。とにかくすべてが魔法であるとき、混乱するものは本当に混乱しています! http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html – steveha

+9

実際に非静的ネストされたクラスのインスタンスを作成できますが、メソッドはちょっと分かりません。すべてネストされたクラスのコンストラクタには、外部クラスの型の暗黙の最初のパラメータがあります。上の例では、Pointクラスが非staticの場合、コンストラクタシグネチャは '' "、"(Lcom/example/ndktest/NDKTest; II)V "'になります。これを見るには、ビルド後にclassesディレクトリから 'javap -s -p com.example.ndktest.NDKTest $ Point'を実行します。 – benkc

9

仕様は正しいものの、この場合は少し誤解を招く可能性があります。 GetMethodIDには、メソッド名とメソッドシグネチャが必要です。 specification says

戻り型とメソッド名とボイド(V)としてコンストラクタ、供給INIT > <のメソッドIDを取得します。それは戻り値の型、ない署名を言うこと

注意。 void(V)は外見上シグネチャに似ていますが、シグネチャではvoid型(つまりV)の戻り値の型を指定する必要があることを指定しています。

引数のないコンストラクタの正しいシグニチャは()Vです。コンストラクタに引数がある場合は、他のコメント作成者が指摘したように、それらを括弧で囲む必要があります。あなたは常にメソッドのシグネチャを見つけるためのjavapツールを使用することができます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); 
    ... 
} 
0

3つのステップは、JNIを持つPointオブジェクトを作成する必要があります。 javap -s com.example.ndktest.NDKTestを実行し、出力からメソッドシグネチャをコピーしてください。

+1

これは、簡潔なコード例を示しているので便利です。メソッドのシグネチャが間違っているため、コードは失敗します。他の回答に記載されているように ''(II)V "'でなければなりません。 – ephemer

1

関連する問題