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


当前回答

我找到了新的模拟器Build。硬件= "ranchu"。

参考:https://groups.google.com/forum/ # !主题/ android-emulator-dev / dltBnUW_HzU

而且我还找到了Android官方的方法来检查是否是模拟器。我认为这对我们是很好的参考。

Android API Level 23 [Android 6.0]

package com.android.internal.util;

/**
 * @hide
 */
public class ScreenShapeHelper {
    private static final boolean IS_EMULATOR = Build.HARDWARE.contains("goldfish");
}

我们有ScreenShapeHelper。IS_EMULATOR来检查emulator是否。

Android API Level 24 [Android 7.0]

package android.os;

/**
 * Information about the current build, extracted from system properties.
 */
public class Build {


    /**
     * Whether this build was for an emulator device.
     * @hide
     */
    public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");

}

我们有Build。IS_EMULATOR来检查emulator是否。

官方检查模拟器的方式是不是新的,也可能不够,上面的答案也提到了。

但这可能告诉我们,官方将提供官方检查是否模拟器的方法。

正如使用上面提到的所有方法一样,现在我们也可以使用这两种方法来检查模拟器是否正确。

如何访问com.android.internal包和@hide

并等待官方的开放SDK。

其他回答

我从来没有找到一个很好的方法来判断你是否在模拟器中。

但如果你只是需要检测你是否在开发环境中,你可以这样做:

     if(Debug.isDebuggerConnected() ) {
        // Things to do in debug environment...
    }

希望对大家有所帮助....

所有答案都在一个方法中

static boolean checkEmulator()
{
    try
    {
        String buildDetails = (Build.FINGERPRINT + Build.DEVICE + Build.MODEL + Build.BRAND + Build.PRODUCT + Build.MANUFACTURER + Build.HARDWARE).toLowerCase();

        if (buildDetails.contains("generic") 
        ||  buildDetails.contains("unknown") 
        ||  buildDetails.contains("emulator") 
        ||  buildDetails.contains("sdk") 
        ||  buildDetails.contains("genymotion") 
        ||  buildDetails.contains("x86") // this includes vbox86
        ||  buildDetails.contains("goldfish")
        ||  buildDetails.contains("test-keys"))
            return true;
    }   
    catch (Throwable t) {Logger.catchedError(t);}

    try
    {
        TelephonyManager    tm  = (TelephonyManager) App.context.getSystemService(Context.TELEPHONY_SERVICE);
        String              non = tm.getNetworkOperatorName().toLowerCase();
        if (non.equals("android"))
            return true;
    }
    catch (Throwable t) {Logger.catchedError(t);}

    try
    {
        if (new File ("/init.goldfish.rc").exists())
            return true;
    }
    catch (Throwable t) {Logger.catchedError(t);}

    return false;
}

谷歌在Flutter的device-info插件中使用以下代码来确定该设备是否是模拟器:

private boolean isEmulator() {
    return (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
        || Build.FINGERPRINT.startsWith("generic")
        || Build.FINGERPRINT.startsWith("unknown")
        || Build.HARDWARE.contains("goldfish")
        || Build.HARDWARE.contains("ranchu")
        || Build.MODEL.contains("google_sdk")
        || Build.MODEL.contains("Emulator")
        || Build.MODEL.contains("Android SDK built for x86")
        || Build.MANUFACTURER.contains("Genymotion")
        || Build.PRODUCT.contains("sdk_google")
        || Build.PRODUCT.contains("google_sdk")
        || Build.PRODUCT.contains("sdk")
        || Build.PRODUCT.contains("sdk_x86")
        || Build.PRODUCT.contains("sdk_gphone64_arm64")
        || Build.PRODUCT.contains("vbox86p")
        || Build.PRODUCT.contains("emulator")
        || Build.PRODUCT.contains("simulator");
}

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

@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;
    }
}

我尝试了几种技术,但最终选择了稍微修改过的检查Build的版本。产品如下。这似乎变化很大,从模拟器到模拟器,这就是为什么我有3个检查,我目前有。我想我本可以只检查product.contains("sdk"),但认为下面的检查更安全一些。

public static boolean isAndroidEmulator() {
    String model = Build.MODEL;
    Log.d(TAG, "model=" + model);
    String product = Build.PRODUCT;
    Log.d(TAG, "product=" + product);
    boolean isEmulator = false;
    if (product != null) {
        isEmulator = product.equals("sdk") || product.contains("_sdk") || product.contains("sdk_");
    }
    Log.d(TAG, "isEmulator=" + isEmulator);
    return isEmulator;
}

供你参考——我发现我的Kindle Fire有Build功能。BRAND = "generic",一些模拟器没有网络运营商的"Android"。