8

NativeActivityクラスを使用して、Android で OpenGL ゲームを開発しています。ここまではすべて問題ありませんでしたが、Java からしか利用できないと思われるいくつかの機能にアクセスする必要があります。

他にもありますが、私たちが最初に役立つと思ったのは、ディスプレイの 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 グルーを使用していることを考えると、どうすればそれを回避できますか。

4

2 に答える 2

6

ええと、私はコードを数時間見つめていましたが、見当たりませんでした。それから机を離れて戻ってくると、そこには次のものがありました。

この2行の代わりに

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

私は使用する必要がありました:

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

ドー:)

(少なくとも、誰かが難しい方法でDPIを取得したい場合の例として役立ちます:))

于 2012-11-06T14:22:49.977 に答える
1

まず、私はJNIがあまり得意ではないことに注意する必要があります:)

とはいえ、問題は、displayMetrics変数を次のようにグローバル参照する必要があることだと思います。

displayMetrics = jni->NewGlobalRef(displayMetrics);

またはそのようなもの。で参照解除することを忘れないでくださいDeleteGlobalRef。LocalRefも機能する可能性があります...

しかし、この問題を解決しようとすると、すべてをJava関数でラップし、それをネイティブから呼び出して、Java側にほとんどの関数呼び出しを行わせることになります。また、Javaネイティブのフェンスをホッピングすることも少なくなります。 。

于 2012-11-06T13:13:10.170 に答える