如何通过NDK使用c代码检查设备运行的API级别?

问题描述 投票:0回答:4

我开始使用 NDK 在 Android 上工作,我想检查设备在我的 C 代码上运行的 Android API 级别。 我怎样才能做到这一点?

起初我以为我可以使用/android/api-level.h下的定义

__ANDROID_API__
,但这是一个错误的假设。

**注意:我不是问如何通过 java 检查 API 级别。

android android-ndk
4个回答
9
投票

我刚刚在编写一些 JNI 代码,想要查询正在运行的操作系统构建版本,如 Jona 所描述的那样。我想尽早执行此操作(即在 JNI_OnLoad 中),因此宁愿不按照 FUBUs 的描述从 Java 中移交它。自 API Level 4 起,此信息已作为 android.os.Build.VERSION 中的 int 字段 SDK_INT 提供,这就是我在此代码段中查找的内容:

static const char* TAG = "testjnjni";

static bool _disableHttpKeepAliveOnBuggyPlatforms(JNIEnv *env)
{
    // Based on article here:
    //   http://android-developers.blogspot.co.uk/2011/09/androids-http-clients.html
    // Which references the issue documented here:
    //   http://code.google.com/p/android/issues/detail?id=2939
    // We need to set "http.keepAlive" to "false" if running an OS version earlier than Froyo (API Level 8)

    if ((*env)->ExceptionCheck(env))
        return false; // already got an exception pending

    bool success = true;

    // VERSION is a nested class within android.os.Build (hence "$" rather than "/")
    jclass versionClass = (*env)->FindClass(env, "android/os/Build$VERSION");
    if (NULL == versionClass)
        success = false;

    jfieldID sdkIntFieldID = NULL;
    if (success)
        success = (NULL != (sdkIntFieldID = (*env)->GetStaticFieldID(env, versionClass, "SDK_INT", "I")));

    jint sdkInt = 0;
    if (success)
    {
        sdkInt = (*env)->GetStaticIntField(env, versionClass, sdkIntFieldID);
        __android_log_print(ANDROID_LOG_VERBOSE, TAG, "sdkInt = %d", sdkInt);
    }

    if (success && sdkInt < 8)
    {
        jclass systemClass = (*env)->FindClass(env, "java/lang/System");
        if (NULL == systemClass)
            success = false;

        jmethodID setPropertyMethodID = NULL;
        if (success)
            success = (NULL != (setPropertyMethodID = (*env)->GetStaticMethodID(env, systemClass, "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;")));

        jstring propString = NULL;
        if (success)
            success = (NULL != (propString = (*env)->NewStringUTF(env, "http.keepAlive")));

        jstring valueString = NULL;
        if (success)
            success = (NULL != (valueString = (*env)->NewStringUTF(env, "false")));

        jobject oldValueString = NULL;
        if (success)
        {
            __android_log_print(ANDROID_LOG_VERBOSE, TAG, "Disabling http.keepAlive");
             oldValueString = (*env)->CallStaticObjectMethod(env, systemClass, setPropertyMethodID, propString, valueString);
        }

        // cleanup
        (*env)->DeleteLocalRef(env, propString);
        (*env)->DeleteLocalRef(env, valueString);
        (*env)->DeleteLocalRef(env, oldValueString);
        (*env)->DeleteLocalRef(env, systemClass);
    }

    // cleanup
    (*env)->DeleteLocalRef(env, versionClass);

    return success;
}

我编写此代码所需的所有信息都清楚地记录在由 Shen Liang 撰写的题为“Java 本机接口:程序员指南和规范”的 PDF 中,该 PDF 过去可以从 Oracle 网站此处 获取,但也可以作为书购买(例如这里)。 JNI 是一项非常强大的技术,我强烈建议任何想要掌握它的开发人员阅读 PDF 以及 Android 开发人员的JNI Tips

哦,最后,理解本地和全球参考资料的重要性怎么强调都不为过。 Android 的开发者博客有一篇很好的文章here,涵盖了 ICS 中的更改(没有任何内容偏离 JNI 规范,但值得重申的要点!)。


4
投票

存在完全原生的解决方案:

  1. 之前
    Android L
    您可以使用 __system_property_get("ro.build.version.sdk")
     中的 
    sys/system_properties.h
  2. 对于
    Android L
    及更新版本,您可以使用
    popen("getprop ro.build.version.sdk")

使用编译时工具链常量检查

#if __ANDROID_API__ < 21
在这两种实现之间切换。


1
投票

您可以将在 JAVA 中获得的 API 级别一次性传递给 C 代码,并将其存储在全局变量中。对我来说,这是更简单的方法。


0
投票

在 API 24+ 上,使用

android_get_device_api_level()

© www.soinside.com 2019 - 2024. All rights reserved.