我希望我的代码在模拟器上运行时与在设备上运行时略有不同。(例如,使用10.0.2.2代替公共URL在开发服务器上自动运行。)检测Android应用程序何时在模拟器中运行的最佳方法是什么?
当前回答
无论您使用哪种代码来进行模拟器检测,我都强烈建议编写单元测试来覆盖所有构建。指纹,构建。硬件和构建。您所依赖的制造商值。下面是一些测试示例:
@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.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
如果应用程序在模拟器上运行,这应该返回true。
我们应该注意的是不要检测所有的模拟器,因为只有几个不同的模拟器。这很容易检查。 我们必须确保实际的设备不会被检测为模拟器。
我使用名为“Android设备信息共享”的应用程序来检查这一点。
在这个应用程序上,你可以看到许多设备的各种信息(可能是世界上大多数设备;如果您正在使用的设备不在列表中,它将自动添加)。
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);
}
您可以检查deviceId (IMEI)是否为“000000000000000”(15个零)
另一种选择是查看ro。硬件属性,看看它是否设置为金鱼。不幸的是,在Java中似乎没有一个简单的方法来做到这一点,但在C中使用property_get()是很简单的。
这个解决方案怎么样(SystemProperties的类实现在这里可用):
val isProbablyRunningOnEmulator: Boolean by lazy {
// Android SDK emulator
return@lazy ((Build.MANUFACTURER == "Google" && Build.BRAND == "google" &&
((Build.FINGERPRINT.startsWith("google/sdk_gphone_")
&& Build.FINGERPRINT.endsWith(":user/release-keys")
&& Build.PRODUCT.startsWith("sdk_gphone_")
&& Build.MODEL.startsWith("sdk_gphone_"))
//alternative
|| (Build.FINGERPRINT.startsWith("google/sdk_gphone64_")
&& (Build.FINGERPRINT.endsWith(":userdebug/dev-keys") || Build.FINGERPRINT.endsWith(":user/release-keys"))
&& Build.PRODUCT.startsWith("sdk_gphone64_")
&& Build.MODEL.startsWith("sdk_gphone64_"))))
//
|| 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")
//bluestacks
|| "QC_Reference_Phone" == Build.BOARD && !"Xiaomi".equals(Build.MANUFACTURER, ignoreCase = true)
//bluestacks
|| Build.MANUFACTURER.contains("Genymotion")
|| Build.HOST.startsWith("Build")
//MSI App Player
|| Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
|| Build.PRODUCT == "google_sdk"
// another Android SDK emulator check
|| SystemProperties.getProp("ro.kernel.qemu") == "1")
}
请注意,一些模拟器伪造真实设备的精确规格,因此可能无法检测到它。我已经添加了我能添加的东西,但我不认为有100%的方法来检测它是否真的是一个模拟器。
这里有一个你可以在APK中创建的小片段来展示关于它的各种事情,所以你可以添加自己的规则:
textView.text = "FINGERPRINT:${Build.FINGERPRINT}\n" +
"MODEL:${Build.MODEL}\n" +
"MANUFACTURER:${Build.MANUFACTURER}\n" +
"BRAND:${Build.BRAND}\n" +
"DEVICE:${Build.DEVICE}\n" +
"BOARD:${Build.BOARD}\n" +
"HOST:${Build.HOST}\n" +
"PRODUCT:${Build.PRODUCT}\n"
推荐文章
- 我如何在一个片段中访问getSupportFragmentManager() ?
- 调试在哪里。Android Studio中的密钥存储库
- 从资产中读取文件
- 在不活动的地方调用getLayoutInflater()
- 设置Android布局元素的背景颜色
- 错误:'keytool'不能被识别为内部或外部命令、可操作程序或批处理文件
- 在应用程序本身中更改Locale
- apk (.apk)和应用程序包(.aab)的区别
- 如何设置超时在改造库?
- Android - SPAN_EXCLUSIVE_EXCLUSIVE跨度不能为零长度
- TextView的字体大小在Android应用程序改变字体大小从本机设置
- 如何模拟Android杀死我的进程
- 禁用EditText闪烁光标
- Android Eclipse -无法找到*.apk
- 设置TextView文本从html格式的字符串资源在XML