如何从代码中判断我的Android应用程序是在模拟器上运行还是在真实设备上运行?

16

我已经阅读了这个stackoverflow主题,并尝试使用那个答案中给出的代码来找出我是在模拟器上运行还是在真实设备上运行:

import android.content.ContentResolver;
import android.provider.Settings.Secure;
...     
mTextView.setText(Secure.getString(getContentResolver(), Secure.ANDROID_ID));

在我的真实设备上,它返回"2bccce3...",但是在模拟器上它不会返回null,而是一个字符串"bd9f8..."

有什么方法可以从代码中找出是模拟器还是真实设备将不胜感激

9个回答

18

这应该就可以了:

boolean inEmulator = false;
String brand = Build.BRAND;
if (brand.compareTo("generic") == 0)
{
    inEmulator = true;
}

编辑:

boolean inEmulator = "generic".equals(Build.BRAND.toLowerCase());

1
是的,我认为这比我发现的其他方法更可靠。虽然我很确定没有人写下来说这对模拟器始终成立(我愿意打赌对于所有设备来说都是错误的,尽管可能也没有人保证)。顺便说一句,我认为您的代码可以作为一行代码清晰地表达:boolean inEmulator = "generic".equals(Build.BRAND); - Ted Hopp
编辑后,更加清晰明了。感谢建议。此外,我认为这对大多数设备都有效,因为我已在模拟器上测试了1.5、1.6和2.1版本,且运行良好。 - A. Abiri
我也在 Samsung 的 Galaxy Tab 和 Sony Erikson 的 ETK 模拟器上测试通过了。但谁知道它将来是否能够正常工作。同时,我也在2.3.3模拟器上进行了测试。 - Ted Hopp
嗯,我认为未来没有任何理由改变这个值。所有模拟器保持相同数值似乎是合理的。如果出现差异,只会使事情更加复杂。 - A. Abiri
这可以满足我的需求,尽管正如@Ted所提到的“谁知道它将来是否会起作用”。我需要知道我是否在真实设备上,以确保我不运行调试代码。我不认为硬件设备品牌被称为“通用”。如果将来模拟器发生变化,我只需调整我的代码即可。对我来说很好。问题已回答。谢谢Arash。 - Addi
仅供参考,如果您正在使用加速的x86模拟器,则品牌将是“generic_x86”。因此我使用Build.BRAND.toLowerCase().startsWith("generic");(这安全吗?有人见过带有null品牌的设备吗?) - daemontus

9

随着新的英特尔本地模拟器的出现,上述方法不再起作用了。现在我正在使用这段代码片段,在英特尔和ARM模拟器上都能正常工作:

if (Build.MODEL.contains("google_sdk") ||
    Build.MODEL.contains("Emulator") ||
    Build.MODEL.contains("Android SDK")) {
  RunsInEmulator = true;
}

7

有一个相当古老的Android开发者组上的帖子建议检查设备上的传感器数量。类似这样的内容可能会起作用:

SensorManager manager = (SensorManager) getSystemService(SENSOR_SERVICE);
if (manager.getSensorList(Sensor.TYPE_ALL).isEmpty()) {
    // running on an emulator
} else {
    // running on a device
}

我没有尝试过这个建议,所以不知道它有多可靠。(也许一些模拟器现在报告一些传感器; 也许一些设备报告没有传感器。[现在有安卓牙刷吗?]) 但是它肯定比检查空的ANDROID_ID要好(自2.2以来已经不起作用)。
P.S. 另一个线程声称,从2.2开始,模拟器的ANDROID_ID始终为“9774D56D682E549C”。然而,你显然得到了其他十六进制字符串,所以我认为这也是不正确的。
P.P.S. 我没有尝试过的其他建议在这里。其中一个看起来特别好(如果有效):
if (android.os.Build.MODEL.equals(“google_sdk”)) {
   // emulator
} else {
   //not emulator
}

我已经在3.0模拟器(使用Google地图API)和设备Nexus One、Motorola Defy和Samsung Galaxy S上尝试了P.P.S.中提供的代码,它按预期工作。即使模拟器的型号将来发生变化,我也会进行适应。唯一不应发生的事情是:如果运行的是真实设备,则应用程序假定其在模拟器上运行。但是很不可能有真实设备型号被命名为“google_sdk”。因此这解决了我的问题。非常感谢 Ted。 - Addi
传感器检查对我没有起作用。模拟器的传感器列表没有出现空白,但它的行为却像一个设备。 - Serdar Samancıoğlu

2

我认为最好的答案是先确定你真正关心的原因,然后检查模拟器上与设备不同的任何具体特征。


我需要在模拟器模式下运行调试代码。因此,我需要确切的东西告诉我我正在使用真实设备,以确保执行“真实代码”。如果将来检查模拟器失败,那也不是什么大问题,我可以轻松适应。<br/> Ted在他的答案的P.P.S中给出了一个试用版,适合我的目的,请参见我的评论。 - Addi
我建议你将调试与模拟器/设备分开,因为有时候你会发现当真实设备暴露出模拟器没有的问题时,你需要在真实设备上运行调试代码。 - Chris Stratton

2
这个方案怎么样?
  public static boolean isRunningOnEmulator()
    {
    boolean result=//
        Build.FINGERPRINT.startsWith("generic")//
            ||Build.FINGERPRINT.startsWith("unknown")//
            ||Build.MODEL.contains("google_sdk")//
            ||Build.MODEL.contains("Emulator")//
            ||Build.MODEL.contains("Android SDK built for x86");
    if(result)
      return true;
    result|=Build.BRAND.startsWith("generic")&&Build.DEVICE.startsWith("generic");
    if(result)
      return true;
    result|="google_sdk".equals(Build.PRODUCT);
    return result;
    }

编辑:看起来我已经在这里回答过了:

https://dev59.com/k3E85IYBdhLWcg3wYigB#21505193


1
this post所述,IMEI和IMSI在模拟器中被硬编码。
2325     { "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL },   /* request internation subscriber identification number */
2326     { "+CGSN", "000000000000000", NULL },   /* request model version */

您可以轻松地使用以下代码获取值:



adb shell dumpsys iphonesubinfo

因此,使用TelephonyManager.getDeviceId()检查设备的IMEI应该足以确定您是在模拟器还是真实设备上。


为了绝对确定,您可以结合其他帖子所述的检查型号名称。

public static boolean isRunningOnEmulator(final Context inContext) {

    final TelephonyManager theTelephonyManager = (TelephonyManager)inContext.getSystemService(Context.TELEPHONY_SERVICE);
    final boolean hasEmulatorImei = theTelephonyManager.getDeviceId().equals("000000000000000");
    final boolean hasEmulatorModelName = Build.MODEL.contains("google_sdk")
            || Build.MODEL.contains("Emulator")
            || Build.MODEL.contains("Android SDK");

    return hasEmulatorImei || hasEmulatorModelName;
}

这种方法的缺点是需要上下文才能访问这些信息,并且每次检查都需要实例化一个TelephonyManager。

1
这是标准的Google Flutter模拟器检查:
public 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("vbox86p")
            || Build.PRODUCT.contains("emulator")
            || Build.PRODUCT.contains("simulator");
}

0

以下是正确检测我的模拟器

if (Build.BRAND.equalsIgnoreCase("generic")) {
              //"YES, I am an emulator"
       } else {
              //"NO, I am NOT an emulator"
       }

0
截至撰写本文时,除了尝试检查传感器外,此线程中的任何内容都无法在Bluestacks 4模拟器上运行。因此,我使用gist检查了电池温度。它应该返回0.0,这意味着它没有电池温度(因此是模拟器)。
public float getCpuTemp() {
    Process process;
    try {
        process = Runtime.getRuntime().exec("cat sys/class/thermal/thermal_zone0/temp");
        process.waitFor();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = reader.readLine();
        float temp = Float.parseFloat(line) / 1000.0f;
        return temp;
    } catch (Exception e) {
        e.printStackTrace();
        return 0.0f;
    }
}

我还应该指出,这包括所有模拟器。对于Bluestacks 4,您可以创建一个函数来检测sdcard中的“windows”文件夹。 - Sha-agi

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