安卓:从NativeActivity的使用JNI [英] Android: Using JNI from NativeActivity

查看:339
本文介绍了安卓:从NativeActivity的使用JNI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在开发使用 NativeActivity的类在Android上一个OpenGL游戏。到目前为止,一切正常,但现在我们需要访问一些功能,似乎只可从Java。

We are developing an OpenGL game on android using the NativeActivity class. So far everything went OK, but now we need to access some functionality that only seems to be available from Java.

有更多的,但第一个我们认为将是有益的被访问显示DPI。如上所述这里了Java code是这样的:

There are more, but the first one we thought would be useful was accessing the display DPI. As described here the Java code looks like this:

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

这里是不幸对应的C ++ code:

And here is the unfortunate corresponding C++ code:

// 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();
}

在code输出:

The code outputs:

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

这是因为如果在的 displayMetrics 的对象并没有得到在呼叫建立

It's as if the displayMetrics object did not get set in the call

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

这是不是情况JNI不会允许我使用的参数作为返回值?如果是这样,我们怎么能去解决它因为我们使用了NativeActivity的胶水。

Is it the case that JNI wouldn't allow me to use an argument as a return value? If so, how can we go around it given that we're using the NativeActivity glue.

推荐答案

ECH,我一直盯着$ C $下几个小时,没有看到它。然后离开了办公桌,回来了,它就在那里:

Ech, I've been staring at the code for couple of hours and didn't see it. Then left the desk, came back and there it was:

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硬盘的方式:))

(at least it can serve as an example if someone wants to get DPI the hard way :) )

这篇关于安卓:从NativeActivity的使用JNI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆