如何检测Android应用程序在模拟器中运行的时间?

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

一个常见的就是Build.FINGERPRINT.startsWith("generic")

这个解决scheme如何:

 public static boolean isEmulator() { return 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") || Build.MANUFACTURER.contains("Genymotion") || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) || "google_sdk".equals(Build.PRODUCT); } 

那么Android的ID不适合我,我目前正在使用:

 "google_sdk".equals( Build.PRODUCT ); 

根据其他答案的提示,这可能是最稳健的方法:

isEmulator = "goldfish".equals(Build.HARDWARE)

那么下面的代码如何判断您的应用是否使用了debugging密钥进行了签名? 它没有检测到模拟器,但它可能适合你的目的?

 public void onCreate Bundle b ) { super.onCreate(savedInstanceState); if ( signedWithDebugKey(this,this.getClass()) ) { blah blah blah } blah blah blah } static final String DEBUGKEY = "get the debug key from logcat after calling the function below once from the emulator"; public static boolean signedWithDebugKey(Context context, Class<?> cls) { boolean result = false; try { ComponentName comp = new ComponentName(context, cls); PackageInfo pinfo = context.getPackageManager().getPackageInfo(comp.getPackageName(),PackageManager.GET_SIGNATURES); Signature sigs[] = pinfo.signatures; for ( int i = 0; i < sigs.length;i++) Log.d(TAG,sigs[i].toCharsString()); if (DEBUGKEY.equals(sigs[0].toCharsString())) { result = true; Log.d(TAG,"package has been signed with the debug key"); } else { Log.d(TAG,"package signed with a key other than the debug key"); } } catch (android.content.pm.PackageManager.NameNotFoundException e) { return false; } return result; } 

这段代码适用于我

 TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); String networkOperator = tm.getNetworkOperatorName(); if("Android".equals(networkOperator)) { // Emulator } else { // Device } 

如果该设备没有SIM卡,则回退空string:“”

由于Android模拟器总是回收“Android”作为networking运营商,我使用上面的代码。

以下都设置为“google_sdk”:

 Build.PRODUCT Build.MODEL 

所以它应该足以使用以下任一行。

 "google_sdk".equals(Build.MODEL) 

要么

 "google_sdk".equals(Build.PRODUCT) 

我尝试了几种技巧,但是稍微修改了一下检查Build.PRODUCT的版本。 这似乎从模拟器到模拟器有很大的不同,这就是为什么我现在有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”,有些模拟器没有networking运营商的“Android”。

我只是寻找_sdk_sdk_sdk_ ,或者甚至只是在Build.PRODUCT sdk一部分:

 if(Build.PRODUCT.matches(".*_?sdk_?.*")){ //-- emulator -- }else{ //-- other device -- } 

我从来没有find一个好方法来告诉你是否在模拟器中。

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

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

希望这个帮助….

使用这个function:

  public static final boolean isEmulator() { int rating = 0; if ((Build.PRODUCT.equals("sdk")) || (Build.PRODUCT.equals("google_sdk")) || (Build.PRODUCT.equals("sdk_x86")) || (Build.PRODUCT.equals("vbox86p"))) { rating++; } if ((Build.MANUFACTURER.equals("unknown")) || (Build.MANUFACTURER.equals("Genymotion"))) { rating++; } if ((Build.BRAND.equals("generic")) || (Build.BRAND.equals("generic_x86"))) { rating++; } if ((Build.DEVICE.equals("generic")) || (Build.DEVICE.equals("generic_x86")) || (Build.DEVICE.equals("vbox86p"))) { rating++; } if ((Build.MODEL.equals("sdk")) || (Build.MODEL.equals("google_sdk")) || (Build.MODEL.equals("Android SDK built for x86"))) { rating++; } if ((Build.HARDWARE.equals("goldfish")) || (Build.HARDWARE.equals("vbox86"))) { rating++; } if ((Build.FINGERPRINT.contains("generic/sdk/generic")) || (Build.FINGERPRINT.contains("generic_x86/sdk_x86/generic_x86")) || (Build.FINGERPRINT.contains("generic/google_sdk/generic")) || (Build.FINGERPRINT.contains("generic/vbox86p/vbox86p"))) { rating++; } return rating > 4; } 

不知道是否有更好的方法来检测emu,但是模拟器的根目录下会有init.goldfish.rc文件。

这是仿真器特定的启动脚本,它不应该在非仿真器版本上。

这是我的解决scheme(只有在debugging机器上运行Web服务器时才有效):我创build了一个后台任务,在应用程序启动时启动。 它寻找http://10.0.2.2 ,如果它存在,它将全局参数(IsDebug)更改为true。 这是一个沉默的方式来找出你在哪里运行。

 public class CheckDebugModeTask extends AsyncTask<String, Void, String> { public static boolean IsDebug = false; public CheckDebugModeTask() { } @Override protected String doInBackground(String... params) { try { HttpParams httpParameters = new BasicHttpParams(); int timeoutConnection = 1000; HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); int timeoutSocket = 2000; HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); String url2 = "http://10.0.2.2"; HttpGet httpGet = new HttpGet(url2); DefaultHttpClient client = new DefaultHttpClient(httpParameters); HttpResponse response2 = client.execute(httpGet); if (response2 == null || response2.getEntity() == null || response2.getEntity().getContent() == null) return ""; return "Debug"; } catch (Exception e) { return ""; } } @Override protected void onPostExecute (String result) { if (result == "Debug") { CheckDebugModeTask.IsDebug = true; } } 

从主要活动onCreate:

 CheckDebugModeTask checkDebugMode = new CheckDebugModeTask(); checkDebugMode.execute(""); 

那么,如果你想要硬性规定,而不是使用任何可以很容易修改的指纹,我已经看到这个博客文章中的概念实际上是编码和工作。

另一个select是看ro.hardware财产,看看它是否设置为金鱼。 不幸的是,似乎没有一个简单的方法来从Java执行此操作,但使用property_get()从C中调用它很简单。

上述build议的解决scheme检查ANDROID_ID为我工作,直到我今天更新到Android 2.2发布的最新的SDK工具。

因此,我现在切换到下面的解决scheme,迄今为止工作的缺点,但您需要把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); } } 

你可以检查IMEI#, http://developer.android.com/reference/android/telephony/TelephonyManager.html#getDeviceId%28%29

如果我记得在模拟器上这返回0.但是,没有任何文件,我可以find保证。 虽然模拟器可能不总是返回0,但似乎很安全,注册的手机不会返回0.在非手机的Android设备,或没有安装SIM卡或一个目前没有注册的networking?

似乎这是一个坏主意,要依靠这个。

这也意味着你需要请求获得阅读电话状态的权限,如果你不需要其他的东西,这是不好的。

如果不是这样的话,那么在你最终生成签名的应用程序之前,总会有一些翻转的地方。

 Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic") 

如果应用程序在模拟器上运行,这应该返回true。

我们应该小心的是没有检测到所有的仿真器,因为只有几个不同的仿真器。 这很容易检查。 我们必须确保实际的设备不被检测为模拟器。

我使用名为“ Android设备信息共享 ”的应用程序来检查这个。

在这个应用程序,你可以看到许多设备的各种信息(可能是世界上大多数设备;如果你正在使用的设备是从列表中缺less,它会自动添加)。

实际上,2.2上的ANDROID_ID总是等于9774D56D682E549C (根据这个线程 +我自己的实验)。

所以,你可以检查这样的东西:

 String androidID = ...; if(androidID == null || androidID.equals("9774D56D682E549C")) do stuff; 

不是最漂亮的,但它做的工作。

这对我有用

 public boolean isEmulator() { return Build.MANUFACTURER.equals("unknown"); } 

将文件放入模拟器的文件系统中; 由于该文件不会在真实设备上存在,因此该文件应该稳定,可靠并且易于修复。

从电池,模拟器:电源始终是AC充电器。 温度始终为0。

你可以使用Build.HOST来logging主机值,不同的仿真器有不同的主机值。

所有的答案在一个方法

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

我发现新的模拟器Build.HARDWARE = "ranchu"

参考: https : //groups.google.com/forum/#!topic/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来检查是否模拟器。

由于Android API级别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来检查是否模拟器。

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

但这也许向我们表明,官方将提供官方的方式来检查是否模拟器。

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

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

并等待官方开放的SDK。

 if ("sdk".equals( Build.PRODUCT )) { // Then you are running the app on the emulator. Log.w("MyAPP", "\n\n Emulator \n\n"); } 
 if (Build.BRAND.equalsIgnoreCase("generic")) { // Is the emulator } 

所有的BUILD引用都是build.prop值,所以你必须考虑如果你打算把它放到发布代码中,你可能会有一些root用户因为某种原因修改了它们。 除非专门模拟仿真器,否则几乎没有需要使用generics作为品牌的修改。

指纹是构build编译和内核编译签名。 有一些使用通用的版本,通常直接从Google获得。

在已修改的设备上,IMEI也有可能被清零,除非您完全阻止修改的设备,否则不可靠。

金鱼是所有其他设备扩展的基础安卓版本。 每个Android设备都有一个init.goldfish.rc文件,除非因未知原因被黑客入侵并删除。

我已经收集了关于这个问题的所有答案,并提出了检测Android是否在vm / emulator上运行的函数:

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

testing仿真器,Genymotion和Bluestacks(2015年10月1日)。

无论您使用哪种代码来执行模拟器检测,我都强烈build议编写unit testing,以覆盖您所依赖的所有Build.FINGERPRINTBuild.HARDWAREBuild.MANUFACTURER值。 以下是一些示例testing:

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

…这里是我们的代码(debugging日志和评论删除简洁):

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

检查答案,使用LeapDroid,Droid4x或安迪模拟器时,他们都没有工作,

什么工作适用于所有情况如下:

  private static String getSystemProperty(String name) throws Exception { Class systemPropertyClazz = Class.forName("android.os.SystemProperties"); return (String) systemPropertyClazz.getMethod("get", new Class[]{String.class}).invoke(systemPropertyClazz, new Object[]{name}); } public boolean isEmulator() { boolean goldfish = getSystemProperty("ro.hardware").contains("goldfish"); boolean emu = getSystemProperty("ro.kernel.qemu").length() > 0; boolean sdk = getSystemProperty("ro.product.model").equals("sdk"); return goldfish || emu || sdk; } 

由于Genymotion的底层仿真引擎是VirtualBox,并且不会马上改变,我发现下面的代码是最可靠的:

  public static boolean isGenymotion() { return Build.PRODUCT != null && Build.PRODUCT.contains("vbox"); }