2012-11-06 4 views
7

NativeActivityクラスを使用してAndroidでOpenGLゲームを開発しています。これまでのところすべてのことがOKになりましたが、今はJavaから利用できるように思われる機能にアクセスする必要があります。Android:NativeActivityのJNIを使​​用

私たちが最初に考えたのは、ディスプレイDPIにアクセスするのに便利だと思ったことがあります。このようにhereは、Javaコードは次のようになります。

DisplayMetrics metrics = new DisplayMetrics(); 
getWindowManager().getDefaultDisplay().getMetrics(metrics); 

そして、ここで不幸に対応するC++コードです:

// My checking routine. 
#define JNI_ASSERT(jni, cond) { \ 
    if (!(cond)) {\ 
    std::stringstream ss; \ 
    ss << __FILE__ << ":" << __LINE__; \ 
    throw std::runtime_error(ss.str()); \ 
    } \ 
    if (jni->ExceptionCheck()) { \ 
    std::stringstream ss; \ 
    ss << __FILE__ << ":" << __LINE__; \ 
    throw std::runtime_error("Exception: " + ss.str()); \ 
    } \ 
} 

void print_dpi(android_app* app) { 
    JNIEnv* jni; 
    app->activity->vm->AttachCurrentThread(&jni, NULL); 

    jclass activityClass = jni->FindClass("android/app/NativeActivity"); 
    JNI_ASSERT(jni, activityClass); 

    jmethodID getWindowManager = jni->GetMethodID 
            (activityClass 
            , "getWindowManager" 
            , "()Landroid/view/WindowManager;"); 
    JNI_ASSERT(jni, getWindowManager); 

    jobject wm = jni->CallObjectMethod(app->activity->clazz, getWindowManager); 
    JNI_ASSERT(jni, wm); 

    jclass windowManagerClass = jni->FindClass("android/view/WindowManager"); 
    JNI_ASSERT(jni, windowManagerClass); 

    jmethodID getDefaultDisplay = jni->GetMethodID(windowManagerClass 
               , "getDefaultDisplay" 
               , "()Landroid/view/Display;"); 
    JNI_ASSERT(jni, getDefaultDisplay); 

    jobject display = jni->CallObjectMethod(wm, getDefaultDisplay); 
    JNI_ASSERT(jni, display); 

    jclass displayClass = jni->FindClass("android/view/Display"); 
    JNI_ASSERT(jni, displayClass); 

    // Check if everything is OK so far, it is, the values it prints 
    // are sensible. 
    { 
    jmethodID getWidth = jni->GetMethodID(displayClass, "getWidth", "()I"); 
    JNI_ASSERT(jni, getWidth); 

    jmethodID getHeight = jni->GetMethodID(displayClass, "getHeight", "()I"); 
    JNI_ASSERT(jni, getHeight); 

    int width = jni->CallIntMethod(display, getWidth); 
    JNI_ASSERT(jni, true); 
    log("Width: ", width); // Width: 320 

    int height = jni->CallIntMethod(display, getHeight); 
    JNI_ASSERT(jni, true); 
    log("Height: ", height); // Height: 480 
    } 

    jclass displayMetricsClass = jni->FindClass("android/util/DisplayMetrics"); 
    JNI_ASSERT(jni, displayMetricsClass); 

    jmethodID displayMetricsConstructor = jni->GetMethodID(displayMetricsClass 
                 , "<init>", "()V"); 
    JNI_ASSERT(jni, displayMetricsConstructor); 

    jobject displayMetrics = jni->NewObject(displayMetricsClass 
             , displayMetricsConstructor); 
    JNI_ASSERT(jni, displayMetrics); 

    jmethodID getMetrics = jni->GetMethodID(displayClass 
             , "getMetrics" 
             , "(Landroid/util/DisplayMetrics;)V"); 
    JNI_ASSERT(jni, getMetrics); 

    jni->CallVoidMethod(display, getMetrics, displayMetrics); 
    JNI_ASSERT(jni, true); 

    { 
    jfieldID xdpi_id = jni->GetFieldID(displayMetricsClass, "xdpi", "F"); 
    JNI_ASSERT(jni, xdpi_id); 

    float xdpi = jni->GetFloatField(displayMetricsClass, xdpi_id); 
    JNI_ASSERT(jni, true); 

    log("XDPI: ", xdpi); // XDPI: 0 
    } 

    { 
    jfieldID height_id = jni->GetFieldID(displayMetricsClass 
             , "heightPixels", "I"); 
    JNI_ASSERT(jni, height_id); 

    int height = jni->GetIntField(displayMetricsClass, height_id); 
    JNI_ASSERT(jni, true); 

    log("Height: ", height); // Height: 0 
    } 
    // TODO: Delete objects here. 
    app->activity->vm->DetachCurrentThread(); 
} 

コード出力:

Width: 320 
Height: 480 
XDPI: 0 
Height: 0 

それはdisplayMetricsかのようです呼び出しでオブジェクトが設定されていません

jni->CallVoidMethod(display, getMetrics, displayMetrics); 

JNIで引数を戻り値として使用できない場合がありますか?もしそうなら、私たちがNativeActivityグルーを使用していることを考えれば、どうすればそれを回避できますか?

答えて

5

エッチ、私は数時間のコードを見つめていて、それを見ていませんでした。そこに続いて、机を左に戻ってきて、それがだった:

の代わりに私が使用している必要があります

float xdpi = jni->GetFloatField(displayMetricsClass, xdpi_id); 
int height = jni->GetIntField(displayMetricsClass, height_id); 

次の2行:

float xdpi = jni->GetFloatField(displayMetrics, xdpi_id); 
int height = jni->GetIntField(displayMetrics, height_id); 

いやはや:)

を(少なくとも、誰かがDPIを苦労して欲しいと思っている場合、例として役立てることができます:))

+0

Eew!ハードコアなJNIをやってからしばらくしていますが、私は同じような間違いを思い出しています。それは常にコーヒーをつかみ、新しい目で戻ってくるのに役立ちます! – Cliff

1

まず第一に、私はJNIで、私は非常に良いじゃないことに注意しなければならない:)

私は問題はdisplayMetrics変数が

displayMetrics = jni->NewGlobalRef(displayMetrics); 
でグローバル参照を行わなければならないということでしょう疑いがある、と述べたこと

などです。 DeleteGlobalRefで控えてください。 LocalRefもうまくいくかもしれません...

しかし、私がこの問題を解決しようとするなら、私はすべてのことをJava関数にラップし、それをネイティブから呼び出して、Java側に関数呼び出しの大部分をさせます - 関連するJavaネイティブフェンスのホッピングも少なくなりました。

+0

ありがとうございました。 h、私の恥に対して、私はコードに誤字があることが分かった。答えを書くでしょう。 –

関連する問題