我希望我的代码在模拟器上运行时与在设备上运行时略有不同。(例如,使用10.0.2.2代替公共URL在开发服务器上自动运行。)检测Android应用程序何时在模拟器中运行的最佳方法是什么?


当前回答

我收集了关于这个问题的所有答案,并提出了一个函数来检测Android是否运行在虚拟机/模拟器上:

public boolean isvm(){


        StringBuilder deviceInfo = new StringBuilder();
        deviceInfo.append("Build.PRODUCT " +Build.PRODUCT +"\n");
        deviceInfo.append("Build.FINGERPRINT " +Build.FINGERPRINT+"\n");
        deviceInfo.append("Build.MANUFACTURER " +Build.MANUFACTURER+"\n");
        deviceInfo.append("Build.MODEL " +Build.MODEL+"\n");
        deviceInfo.append("Build.BRAND " +Build.BRAND+"\n");
        deviceInfo.append("Build.DEVICE " +Build.DEVICE+"\n");
        String info = deviceInfo.toString();


        Log.i("LOB", info);


        Boolean isvm = false;
        if(
                "google_sdk".equals(Build.PRODUCT) ||
                "sdk_google_phone_x86".equals(Build.PRODUCT) ||
                "sdk".equals(Build.PRODUCT) ||
                "sdk_x86".equals(Build.PRODUCT) ||
                "vbox86p".equals(Build.PRODUCT) ||
                Build.FINGERPRINT.contains("generic") ||
                Build.MANUFACTURER.contains("Genymotion") ||
                Build.MODEL.contains("Emulator") ||
                Build.MODEL.contains("Android SDK built for x86")
                ){
            isvm =  true;
        }


        if(Build.BRAND.contains("generic")&&Build.DEVICE.contains("generic")){
            isvm =  true;
        }

        return isvm;
    }

在模拟器、Genymotion和Bluestacks上测试(2015年10月1日)

其他回答

上面建议的检查ANDROID_ID的解决方案对我来说很有效,直到我今天更新到Android 2.2发布的最新SDK工具。

因此,我目前切换到以下解决方案,到目前为止,缺点是你需要把PHONE_STATE读取权限(<uses-permission android:name="android.permission. read_phone_state "/>)

private void checkForDebugMode() {
    ISDEBUGMODE = false; //(Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID) == null);

    TelephonyManager man = (TelephonyManager) getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
    if(man != null){
        String devId = man.getDeviceSoftwareVersion();
        ISDEBUGMODE = (devId == null);
    }
} 

在模拟器的文件系统中放入一个文件;由于该文件不会存在于真正的设备上,这应该是稳定的,可靠的,容易修复当它崩溃。

我收集了关于这个问题的所有答案,并提出了一个函数来检测Android是否运行在虚拟机/模拟器上:

public boolean isvm(){


        StringBuilder deviceInfo = new StringBuilder();
        deviceInfo.append("Build.PRODUCT " +Build.PRODUCT +"\n");
        deviceInfo.append("Build.FINGERPRINT " +Build.FINGERPRINT+"\n");
        deviceInfo.append("Build.MANUFACTURER " +Build.MANUFACTURER+"\n");
        deviceInfo.append("Build.MODEL " +Build.MODEL+"\n");
        deviceInfo.append("Build.BRAND " +Build.BRAND+"\n");
        deviceInfo.append("Build.DEVICE " +Build.DEVICE+"\n");
        String info = deviceInfo.toString();


        Log.i("LOB", info);


        Boolean isvm = false;
        if(
                "google_sdk".equals(Build.PRODUCT) ||
                "sdk_google_phone_x86".equals(Build.PRODUCT) ||
                "sdk".equals(Build.PRODUCT) ||
                "sdk_x86".equals(Build.PRODUCT) ||
                "vbox86p".equals(Build.PRODUCT) ||
                Build.FINGERPRINT.contains("generic") ||
                Build.MANUFACTURER.contains("Genymotion") ||
                Build.MODEL.contains("Emulator") ||
                Build.MODEL.contains("Android SDK built for x86")
                ){
            isvm =  true;
        }


        if(Build.BRAND.contains("generic")&&Build.DEVICE.contains("generic")){
            isvm =  true;
        }

        return isvm;
    }

在模拟器、Genymotion和Bluestacks上测试(2015年10月1日)

无论您使用哪种代码来进行模拟器检测,我都强烈建议编写单元测试来覆盖所有构建。指纹,构建。硬件和构建。您所依赖的制造商值。下面是一些测试示例:

@Test
public void testIsEmulatorGenymotion() throws Exception {
    assertThat(
            DeviceUtils.isRunningOnEmulator(
                    "generic/vbox86p/vbox86p:4.1.1/JRO03S/eng.buildbot.20150217.102902:userdebug/test-keys",
                    "vbox86", "Genymotion")).isTrue();

    assertThat(
            DeviceUtils.isRunningOnEmulator(
                    "generic/vbox86p/vbox86p:5.1/LMY47D/buildbot06092001:userdebug/test-keys", "vbox86",
                    "Genymotion")).isTrue();
}

@Test
public void testIsEmulatorDefaultAndroidEmulator() throws Exception {
    assertThat(
            DeviceUtils.isRunningOnEmulator(
                    "generic_x86/sdk_google_phone_x86/generic_x86:5.0.2/LSY66H/1960483:eng/test-keys", "goldfish",
                    "unknown")).isTrue();

    assertThat(
            DeviceUtils.isRunningOnEmulator(
                    "Android/sdk_google_phone_x86_64/generic_x86_64:6.0/MASTER/2469028:userdebug/test-keys",
                    "ranchu", "unknown")).isTrue();
}

@Test
public void testIsEmulatorRealNexus5() throws Exception {
    assertThat(
            DeviceUtils.isRunningOnEmulator("google/hammerhead/hammerhead:6.0.1/MMB29K/2419427:user/release-keys",
                    "hammerhead", "LGE")).isFalse();
}

...下面是我们的代码(为了简洁起见,删除了调试日志和注释):

public static boolean isRunningOnEmulator() {
    if (sIsRunningEmulator == null) {
        sIsRunningEmulator = isRunningOnEmulator(Build.FINGERPRINT, Build.HARDWARE, Build.MANUFACTURER);
    }

    return sIsRunningEmulator;
}

static boolean isRunningOnEmulator(String fingerprint, String hardware, String manufacturer) {
    boolean isEmulatorFingerprint = fingerprint.endsWith("test-keys");
    boolean isEmulatorManufacturer = manufacturer.equals("Genymotion")
            || manufacturer.equals("unknown");

    if (isEmulatorFingerprint && isEmulatorManufacturer) {
        return true;
    } else {
        return false;
    }
}

Firebase Crashlytics是这样做的:

private static final String GOLDFISH = "goldfish";
private static final String RANCHU = "ranchu";
private static final String SDK = "sdk";
    
public static boolean isEmulator() {
    return Build.PRODUCT.contains(SDK)
        || Build.HARDWARE.contains(GOLDFISH)
        || Build.HARDWARE.contains(RANCHU);
}