NDK を使用して Android で作業を開始しています。デバイスが C コードで実行している Android API レベルを確認したいと考えています。どうやってやるの?
最初は /android/api-level.h の下の定義を使用できると思って__ANDROID_API__
いましたが、それは間違った仮定でした。
**注: Java 経由で API レベルを確認する方法を尋ねているわけではありません。
NDK を使用して Android で作業を開始しています。デバイスが C コードで実行している Android API レベルを確認したいと考えています。どうやってやるの?
最初は /android/api-level.h の下の定義を使用できると思って__ANDROID_API__
いましたが、それは間違った仮定でした。
**注: Java 経由で API レベルを確認する方法を尋ねているわけではありません。
私はいくつかの JNI コードに取り組んでおり、Jonaで説明されているように、実行中の OS ビルド バージョンを照会したいと考えていました。これをできるだけ早く(つまりJNI_OnLoadで)実行したかったので、 FUBUsで説明されているように Java から渡さないようにしました。API レベル 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;
}
このコードを書くために必要なすべての情報は、Sheng Liang による「The Java Native Interface: Programmer's Guide and Specification」というタイトルの PDF に明確に文書化されています。)。JNI は非常に強力なテクノロジです。JNI を理解したい開発者は、その PDF と Android Developers のJNI Tipsを読むことを強くお勧めします。
最後に、ローカル参照とグローバル参照を理解することの重要性は強調してもしきれません。Android の開発者ブログには、ICS の変更点をカバーする優れた記事があります(JNI 仕様から逸脱するものは何もありませんが、繰り返し説明する良い点があります!)。
完全にネイティブなソリューションが存在します:
Android L
利用いただけます__system_property_get("ro.build.version.sdk")
sys/system_properties.h
Android L
および新しいものを使用できますpopen("getprop ro.build.version.sdk")
コンパイル時のツールチェーン定数チェックを使用して、#if __ANDROID_API__ < 21
これら 2 つの実装を切り替えます。
Javaで取得したAPIレベルをCコードに一度渡し、それをグローバル変数にストックできます。私にとっては、それを行うためのより簡単な方法です。