如何检测Android运行时(Dalvik或ART)?

39

Google在Android 4.4中增加了一个新的ART运行时。如何确定当前运行时是ART还是Dalvik?


2
为什么你想知道它呢?对开发者来说有区别吗? - korro
5
我的应用程序在ART下运行时存在问题,但在Dalvik下运行正常。因此,我需要找到一种方式来告知用户他们正在使用实验性的运行环境,并提醒他们在等待我的修复之前可能会遇到稳定性问题。 - Chris Lacy
1
在ART下运行的应用程序可以通过遵循此页面上的指南和提示进行验证:https://developer.android.com/guide/practices/verifying-apps-art.html - rwong
6个回答

33

更新

至少在2014年6月,谷歌发布了一份关于如何正确验证当前运行时的官方文档(点击此处查看)

您可以通过调用System.getProperty("java.vm.version")来验证正在使用哪个运行时。如果使用的是ART,则该属性的值为"2.0.0"或更高。

因此,现在无需通过反射来检查相应的系统属性,只需检查它即可。

private boolean getIsArtInUse() {
    final String vmVersion = System.getProperty("java.vm.version");
    return vmVersion != null && vmVersion.startsWith("2");
}

一种可能的方法是通过反射读取相应的SystemProperty

示例:

package com.example.getcurrentruntimevalue;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MainActivity extends Activity {
    private static final String SELECT_RUNTIME_PROPERTY = "persist.sys.dalvik.vm.lib";
    private static final String LIB_DALVIK = "libdvm.so";
    private static final String LIB_ART = "libart.so";
    private static final String LIB_ART_D = "libartd.so";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = (TextView)findViewById(R.id.current_runtime_value);
        tv.setText(getCurrentRuntimeValue());
    }

    private CharSequence getCurrentRuntimeValue() {
        try {
            Class<?> systemProperties = Class.forName("android.os.SystemProperties");
            try {
                Method get = systemProperties.getMethod("get",
                   String.class, String.class);
                if (get == null) {
                    return "WTF?!";
                }
                try {
                    final String value = (String)get.invoke(
                        systemProperties, SELECT_RUNTIME_PROPERTY,
                        /* Assuming default is */"Dalvik");
                    if (LIB_DALVIK.equals(value)) {
                        return "Dalvik";
                    } else if (LIB_ART.equals(value)) {
                        return "ART";
                    } else if (LIB_ART_D.equals(value)) {
                        return "ART debug build";
                    }

                    return value;
                } catch (IllegalAccessException e) {
                    return "IllegalAccessException";
                } catch (IllegalArgumentException e) {
                    return "IllegalArgumentException";
                } catch (InvocationTargetException e) {
                    return "InvocationTargetException";
                }
            } catch (NoSuchMethodException e) {
                return "SystemProperties.get(String key, String def) method is not found";
            }
        } catch (ClassNotFoundException e) {
            return "SystemProperties class is not found";
        }
    }
}

希望这能帮到您。


但要注意,属性名称不能保证不变。它已经改变了:https://android.googlesource.com/platform/frameworks/base/+/c6c633608ad4cd77ed21227b0bdb11eb79797c31 - fadden
这个问题的其他答案似乎更可取:它们更容易实现,而且不那么繁琐和容易出错。 - DCKing
1
@DCKing,当这个回答发布时(2013年11月),开发者文档中没有提到如何验证当前运行时(因此使用了反射:)。感谢您的提示,帖子已相应更新。 - ozbek

4

如果需要JNI版本:

#include <sys/system_properties.h>

static bool isArtEnabled() {
    char buf[PROP_VALUE_MAX] = {};
    __system_property_get("persist.sys.dalvik.vm.lib.2", buf);
    // This allows libartd.so to be detected as well.
    return strncmp("libart", buf, 6) == 0;
}

如果您想要更接近Shoe Rat发布的代码路径,可以选择以下方法:

static bool isArtEnabled(JNIEnv *env)
{
    // Per https://developer.android.com/guide/practices/verifying-apps-art.html
    // if the result of System.getProperty("java.vm.version") starts with 2,
    // ART is enabled.

    jclass systemClass = env->FindClass("java/lang/System");

    if (systemClass == NULL) {
        LOGD("Could not find java.lang.System.");
        return false;
    }

    jmethodID getProperty = env->GetStaticMethodID(systemClass,
        "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");

    if (getProperty == NULL) {
        LOGD("Could not find java.lang.System.getProperty(String).");
        return false;
    }

    jstring propertyName = env->NewStringUTF("java.vm.version");

    jstring jversion = (jstring)env->CallStaticObjectMethod(
        systemClass, getProperty, propertyName);

    if (jversion == NULL) {
        LOGD("java.lang.System.getProperty('java.vm.version') did not return a value.");
        return false;
    }

    const char *version = env->GetStringUTFChars(jversion, JNI_FALSE);

    // Lets flip that check around to better bullet proof us.
    // Consider any version which starts with "1." to be Dalvik,
    // and all others to be ART.
    bool isArtEnabled = !(strlen(version) < 2 ||
        strncmp("1.", version, 2) == 0);

    LOGD("Is ART enabled? %d (%s)", isArtEnabled, version);

    env->ReleaseStringUTFChars(jversion, version);

    return isArtEnabled;
}

3

Android文档 实际上提供了以下建议:

您可以通过调用System.getProperty(“java.vm.version”)来验证正在使用哪个运行时。如果正在使用ART,则属性的值为“2.0.0”或更高。

在我的启用ART的Nexus 4上(运行Android 4.4.4),此方法似乎很准确。开启Dalvik的Nexus 5返回了1.6.0


2
一个简单的解决方案:

最初的回答:

String vm = System.getProperty("java.vm.name") + " " + System.getProperty("java.vm.version");

在我的Android 8.0(API 26)手机上,它返回的是Dalvik 2.1.0。最初的回答。

1
我认为您应该能够使用 System.getProperty,将 java.vm.name 作为键。在 JavaDoc 中,它的值为 Dalvik,希望在使用该运行时时,其值为 ArtART。 这值得一试...

1
当运行ART时: System.getProperty("java.vm.name") 返回 DalvikSystem.getProperty("java.specification.name") 返回 Dalvik Core Library - Chris Lacy
1
嗯,那可能是查询它最简单的方法。也许他们没有改变它,因为ART还没有发布... - tilpner
无论是发布版还是开发版,都需要进行检测。即使将来更新了,也不能被视为可靠。 - Joseph Lennox

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接