首页 文章

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

提问于
浏览
271

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

30 回答

  • 3

    不知道是否有更好的方法来检测emu,但模拟器将在根目录中包含文件 init.goldfish.rc .

    它是模拟器特定的启动脚本,它不应该在非模拟器构建中存在 .

  • 64

    这个解决方案怎么样:

    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);
    }
    
  • 1

    一个常见的sems是 Build.FINGERPRINT.contains("generic")

  • 10

    好的Android ID对我不起作用,我目前正在使用:

    "google_sdk".equals( Build.PRODUCT );
    
  • 7

    基于其他答案的提示,这可能是最强大的方式:

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

  • 31

    下面的代码如何判断您的应用程序是否使用调试密钥签名?它没有检测到模拟器,但它可能适用于您的目的?

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

    这段代码适合我

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

    如果该设备没有SIM卡,它会返回空字符串:“”

    由于Android模拟器总是将“Android”作为网络运营商回归,我使用上面的代码 .

  • 3

    以下两项都设置为“google_sdk”:

    Build.PRODUCT
    Build.MODEL
    

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

    "google_sdk".equals(Build.MODEL)
    

    要么

    "google_sdk".equals(Build.PRODUCT)
    
  • 5

    我尝试了几种技术,但确定了稍微修改后的版本,检查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”,而且有些模拟器没有网络运营商的“Android” .

  • 126

    我只是寻找 _sdk_sdk_sdk_ ,甚至只是 Build.PRODUCT 中的 sdk

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

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

    但如果您只是需要检测您是否在开发环境中,则可以执行以下操作:

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

    希望这有帮助....

  • 9

    这是我的解决方案(仅当您在调试计算机上运行Web服务器时才有效):我创建了一个后台任务,该任务在应用程序启动时启动 . 它查找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("");
    
  • 1

    使用此功能:

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

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

    如果我在模拟器上回忆这返回0.但是,没有文件我能找到保证 . 虽然模拟器可能并不总是返回0,但是看起来非常安全,注册的手机不会返回0.在非手机Android设备上会发生什么,或者没有安装SIM卡或者当前未注册的设备网络?

    这似乎是一个坏主意,依赖于此 .

    这也意味着你需要请求获得阅读电话状态的许可,如果你还没有要求其它的话,那就不好了 .

    如果不是这样,那么在您最终生成已签名的应用程序之前,总会在某处稍微翻一下 .

  • 1

    另一个选择是查看ro.hardware属性并查看它是否设置为goldfish . 不幸的是,似乎没有一种简单的方法可以从Java中做到这一点,但是使用property_get()从C中做到这一点很简单 .

  • 5

    上面提到的检查 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);
        }
    }
    
  • 2

    从电池,仿真器:电源始终是AC充电器 . 温度始终为0 .

    并且您可以使用 Build.HOST 来记录主机值,不同的模拟器具有不同的主机值 .

  • 7

    一种方法的所有答案

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

    我找到了新的模拟器 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 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 来检查是否是模拟器 .

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

    But this maybe show us that the official will provide the way of official to check whether emulator or not.

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

    How to access the com.android.internal package and @hide

    并等待官方开放SDK .

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

    如果应用程序在模拟器上运行,则应返回true .

    我们应该注意的是没有检测到所有的仿真器,因为只有几个不同的仿真器 . 这很容易检查 . 我们必须确保不会将实际设备检测为仿真器 .

    我使用名为“Android Device Info Share”的应用来检查这个 .

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

  • 111

    实际上,2.2上的ANDROID_ID总是等于 9774D56D682E549C (根据this thread我自己的实验) .

    所以,你可以检查这样的事情:

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

    不是最漂亮,但它确实起作用 .

  • 1

    这适合我

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

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

  • 11

    我收集了关于这个问题的所有答案,并提出了检测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;
        }
    

    在Emulator,Genymotion和Bluestacks上测试(2015年10月1日) .

  • 3

    My recommendation:

    从github尝试this .

    易于检测Android模拟器

    检查Device Farm中的真实设备(https://aws.amazon.com/device-farm/)BlueStacks Genymotion Android模拟器Andy 46.2.207.0 MEmu玩Nox App Player Koplayer .....

    How to use with an Example:

    EmulatorDetector.with(this)
                    .setCheckTelephony(true)
                    .addPackageName("com.bluestacks")
                    .setDebug(true)
                    .detect(new EmulatorDetector.OnEmulatorDetectorListener() {
                        @Override
                        public void onResult(boolean isEmulator) {
    
                        }
                    });
    
  • 3

    检查答案,使用LeapDroid,Droid4x或Andy仿真器时没有一个工作,

    对所有情况有效的是:

    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;
    }
    
  • 12
    if ("sdk".equals( Build.PRODUCT )) {
     // Then you are running the app on the emulator.
            Log.w("MyAPP", "\n\n  Emulator \n\n"); 
    }
    
  • 11
    if (Build.BRAND.equalsIgnoreCase("generic")) {
        // Is the emulator
    }
    

    所有BUILD引用都是build.prop值,因此您必须考虑如果您要将其放入发布代码中,您可能会有一些root用户因任何原因修改了它们 . 除非特别试图模拟仿真器,否则几乎不需要使用generic作为品牌的修改 .

    指纹是构建编译和内核编译签名 . 有些版本使用泛型,通常直接来自Google .

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

    Goldfish是所有其他设备扩展的基础android构建 . 每个Android设备都有一个init.goldfish.rc,除非因未知原因被黑客入侵和删除 .

  • 7

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

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

    无论您使用哪种代码进行仿真器检测,我强烈建议您编写单元测试以涵盖您所依赖的所有 Build.FINGERPRINTBuild.HARDWAREBuild.MANUFACTURER 值 . 以下是一些示例测试:

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

相关问题