android 教你如何判断是否是模拟器还是真机

最近有一些业务需求要判断是否在真机上运行还是在模拟器上运行两种不同的情况下做不同的业务逻辑操作。上网查了查还真有不少的资源。接下来老于接给大家们来总结一下。主要思路:

1.  首先要明白 不要采用IMEI的方式。模拟器的IMEI可以修改的。而且平板是没有IMEI的,可以检测设备的MAC地址,模拟器的MAC地址是固定的几种。

2.通过调用公开或者隐藏的系统API判断并不靠谱,因为调用结果可以轻易被修改,比如直接修改Android的源代码或者借助Xposed Framework进行修改。
3.有基于模拟器特征和api返回值的检测方法都可以通过修改安卓源码的方式轻松绕过。模拟器与真机的本质区别在于运行载体。鉴于大多数的安卓模拟器基于qemu,qemu在执行程序时实际上是将其翻译成宿主机的指令,比如将安卓的arm指令翻译成PC的x86指令。为了效率上的考虑,qemu在翻译执行arm指令时并没有实时更新模拟的pc寄存器值,只会在一段代码翻译执行完之后再更新,而真机中pc寄存器是一直在更新的。基于这一点,可以设计一段CPU任务调度程序来检测模拟器 。具体的你可以参鉴DexLab上的一篇文章。当然,这个方法也是可以被绕过的,可以在理解qemu源码的基础上,修改qemu源码,但很明显这个门槛很高 。

 // 系统在启动的时候会做一次判断,看看SystemServer.java里是怎么判断的:
    private void initData(){
        boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
        if (isEmulator) {
            Slog.i(TAG, "No Bluetooh Service (emulator)");
        } else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            Slog.i(TAG, "No Bluetooth Service (factory test)");
        } else if (!context.getPackageManager().hasSystemFeature
                (PackageManager.FEATURE_BLUETOOTH)) {
            Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
        } else if (disableBluetooth) {
            Slog.i(TAG, "Bluetooth Service disabled by config");
        } else {
            Slog.i(TAG, "Bluetooth Service");
            mSystemServiceManager.startService(BluetoothService.class);
        }

    }
  系统在启动的时候会做一次判断,看看SystemServer.java里是怎么判断的:
  private void initData(){
        boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
        if (isEmulator) {
            Slog.i(TAG, "No Bluetooh Service (emulator)");
        } else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            Slog.i(TAG, "No Bluetooth Service (factory test)");
        } else if (!context.getPackageManager().hasSystemFeature
                (PackageManager.FEATURE_BLUETOOTH)) {
            Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
        } else if (disableBluetooth) {
            Slog.i(TAG, "Bluetooth Service disabled by config");
        } else {
            Slog.i(TAG, "Bluetooth Service");
            mSystemServiceManager.startService(BluetoothService.class);
        }

    }
 //第二种方式   读取文件内容,然后检查已知QEmu的驱动程序的列表
private static String[]known_qemu_drivers = {"goldfish" };
public static Boolean CheckQEmuDriverFile(){
    File driver_file =new File("/proc/tty/drivers");
    if(driver_file.exists()&& driver_file.canRead()){
        byte[]data =new byte[(int)driver_file.length()];
        try{
            InputStream inStream =new FileInputStream(driver_file);
            inStream.read(data);
            inStream.close();
        }catch (FileNotFoundException e){
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }catch (IOException e){
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        String driver_data =new String(data);
        for(String known_qemu_driver : known_qemu_drivers){
            if(driver_data.indexOf(known_qemu_driver)!= -1){
                Log.v("Result:","Find known_qemu_drivers!");
                return true;
            }
        }
    }
    Log.v("Result:","Not Find known_qemu_drivers!");
    return false;
}
第三种方式检测模拟器上特有的几个文件
private static String[]known_numbers = {"15555215554","15555215556",
        "15555215558","15555215560","15555215562","15555215564",
        "15555215566","15555215568","15555215570","15555215572",
        "15555215574","15555215576","15555215578","15555215580",
        "15555215582","15555215584",};

public static Boolean CheckPhoneNumber(Context context){
    TelephonyManager telephonyManager =(TelephonyManager)context
            .getSystemService(Context.TELEPHONY_SERVICE);

    String phonenumber =telephonyManager.getLine1Number();

    for(String number :known_numbers){
        if(number.equalsIgnoreCase(phonenumber)){
            Log.v("Result:","Find PhoneNumber!");
            return true;
        }
    }
    Log.v("Result:","Not Find PhoneNumber!");
    return false;
}
//第五中方式 ----》 检测设备IDS 是不是 “000000000000000”
private static String[]known_device_ids = {"000000000000000" // 默认ID
};

public static Boolean CheckDeviceIDS(Context context){
    TelephonyManager telephonyManager = (TelephonyManager)context
            .getSystemService(Context.TELEPHONY_SERVICE);

    String device_ids =telephonyManager.getDeviceId();

    for(String know_deviceid : known_device_ids){
        if(know_deviceid.equalsIgnoreCase(device_ids)){
            Log.v("Result:","Find ids: 000000000000000!");
            return true;
        }
    }
    Log.v("Result:","Not Find ids: 000000000000000!");
    return false;
}
//第六种方式 检测imsi id是不是“310260000000000”
private static String[]known_imsi_ids = {"310260000000000" // 默认的 imsi id
    };
    public static Boolean CheckImsiIDS(Context context){
        TelephonyManager telephonyManager =(TelephonyManager)
                context.getSystemService(Context.TELEPHONY_SERVICE);

        String imsi_ids =telephonyManager.getSubscriberId();

        for(String know_imsi :known_imsi_ids){
            if(know_imsi.equalsIgnoreCase(imsi_ids)){
                Log.v("Result:","Find imsi ids: 310260000000000!");
                return true;
            }
        }
        Log.v("Result:","Not Find imsi ids: 310260000000000!");
        return false;
    }
//第七种方式   检测手机上的一些硬件信息
public static Boolean CheckEmulatorBuild(Context context){
    String BOARD =android.os.Build.BOARD;
    String BOOTLOADER =android.os.Build.BOOTLOADER;
    String BRAND =android.os.Build.BRAND;
    String DEVICE =android.os.Build.DEVICE;
    String HARDWARE =android.os.Build.HARDWARE;
    String MODEL =android.os.Build.MODEL;
    String PRODUCT =android.os.Build.PRODUCT;
    if(BOARD== "unknown"|| BOOTLOADER== "unknown"
            ||BRAND =="generic" ||DEVICE =="generic"
            ||MODEL =="sdk" ||PRODUCT =="sdk"
            ||HARDWARE =="goldfish")
    {
        Log.v("Result:","Find Emulator by EmulatorBuild!");
        return true;
    }
    Log.v("Result:","Not Find Emulator by EmulatorBuild!");
    return false;
}
//第八种方式 ---》   检测手机运营商家
public static boolean CheckOperatorNameAndroid(Context context){
    String szOperatorName = ((TelephonyManager)
            context.getSystemService(Context.TELEPHONY_SERVICE)).getNetworkOperatorName();

    if(szOperatorName.toLowerCase().equals("android")== true){
        Log.v("Result:","Find Emulator by OperatorName!");
        return true;
    }
    Log.v("Result:","Not Find Emulator by OperatorName!");
    return false;
}
最后要注意的是在PC操作系统上的某些恶意软件会判断是不是在虚拟机上运行。
老于的博客地址: http://blog.youkuaiyun.com/androidstarjack
另外你觉得此篇文章对您有所帮助 请加 QQ交流群 :232203809    
此外还可以关注终端研发部


<think>根据用户的问题,用户希望了解在Flutter中如何判断运行环境是模拟器还是真实设备。在Flutter中,我们可以通过平台通道(Platform Channel)调用原生代码(Android和iOS)来获取设备信息,进而判断是否模拟器。 对于Android平台,我们可以通过检查设备特征(如硬件名称、品牌等)来判断是否模拟器。常见的模拟器特征包括硬件名称中包含"goldfish"或"ranchu",品牌为"generic"等。 对于iOS平台,模拟器通常具有特定的标识符,如"i386"或"x86_64"(表示Intel模拟器)或"arm64"(表示Apple Silicon模拟器),而真实设备通常是ARM架构(如"armv7"、"arm64"等)。 我们可以通过创建一个Flutter插件或直接在Dart代码中通过平台通道调用原生代码来实现。以下是一个简单的实现方案: 1. 在Dart层面,我们使用`MethodChannel`来调用原生方法。 2. 在Android原生代码中,我们编写一个方法检测模拟器。 3. 在iOS原生代码中,同样编写一个方法检测模拟器。 下面是一个示例代码: ### Dart代码 (lib/main.dart) ```dart import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('Device Check')), body: Center( child: FutureBuilder<bool>( future: isSimulator(), builder: (context, snapshot) { if (snapshot.hasData) { return Text('Is Simulator: ${snapshot.data}'); } else { return CircularProgressIndicator(); } }, ), ), ), ); } Future<bool> isSimulator() async { const platform = MethodChannel('device_info'); try { final bool result = await platform.invokeMethod('isSimulator'); return result; } on PlatformException catch (e) { print("Failed to get device info: '${e.message}'."); return false; } } } ``` ### Android原生代码 (android/app/src/main/kotlin/.../MainActivity.kt) 在Android的MainActivity中,我们添加一个方法`isSimulator`来检测模拟器: ```kotlin package com.example.myapp // 替换为你的包名 import androidx.annotation.NonNull import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity: FlutterActivity() { private val CHANNEL = "device_info" override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "isSimulator") { result.success(isEmulator()) } else { result.notImplemented() } } } private fun isEmulator(): Boolean { return (android.os.Build.FINGERPRINT.startsWith("generic") || android.os.Build.FINGERPRINT.startsWith("unknown") || android.os.Build.MODEL.contains("google_sdk") || android.os.Build.MODEL.contains("Emulator") || android.os.Build.MODEL.contains("Android SDK built for x86") || android.os.Build.MANUFACTURER.contains("Genymotion") || android.os.Build.BRAND.startsWith("generic") && android.os.Build.DEVICE.startsWith("generic") || "google_sdk" == android.os.Build.PRODUCT) } } ``` ### iOS原生代码 (ios/Runner/AppDelegate.swift) 在iOS的AppDelegate中,我们添加一个方法`isSimulator`来检测模拟器: ```swift import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let deviceInfoChannel = FlutterMethodChannel(name: "device_info", binaryMessenger: controller.binaryMessenger) deviceInfoChannel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in guard call.method == "isSimulator" else { result(FlutterMethodNotImplemented) return } self?.isSimulator(result: result) }) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func isSimulator(result: FlutterResult) { #if targetEnvironment(simulator) result(true) #else result(false) #endif } } ``` ### 说明 1. 在Android中,我们通过检查一些特定的构建属性来判断设备是否模拟器。这些属性在模拟器上通常具有特定的值(如`generic`、`google_sdk`等)。 2. 在iOS中,我们使用编译宏`#if targetEnvironment(simulator)`来检查当前运行环境是否模拟器。如果是模拟器,返回`true`,否则返回`false`。 这样,当我们在Flutter应用中调用`isSimulator`方法时,就会根据当前平台返回相应的结果。 ### 注意事项 - 上述代码只是一个基本示例,实际使用中可能需要根据不同的模拟器或设备进行更多的测试和调整。 - 在Android上,由于设备种类繁多,可能需要根据实际情况调整判断逻辑。 ### 相关问题 1. 在Flutter中如何获取设备信息(如设备型号、操作系统版本等)? 2. 如何创建一个Flutter插件来封装平台特定的功能? 3. 在Flutter中如何处理平台通道(Platform Channel)的异常?</think>在Flutter中判断运行环境是模拟器还是真实设备,需要通过平台通道(Platform Channel)调用原生代码实现。以下是具体实现方案: ### 解决方案(Dart代码) ```dart import 'package:flutter/services.dart'; Future<bool> isSimulator() async { const platform = MethodChannel('device_info'); try { final bool result = await platform.invokeMethod('isSimulator'); return result; } on PlatformException catch (e) { print("获取设备信息失败: ${e.message}"); return false; } } // 使用示例 void checkDevice() async { if (await isSimulator()) { print("当前运行模拟器"); } else { print("当前运行在真实设备"); } } ``` ### 平台原生代码实现 #### 1. **Android原生代码** (Kotlin) 在 `MainActivity.kt` 中添加: ```kotlin import android.os.Build import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL = "device_info" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "isSimulator") { result.success(isEmulator()) } else { result.notImplemented() } } } private fun isEmulator(): Boolean { 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" == Build.PRODUCT) } } ``` #### 2. **iOS原生代码** (Swift) 在 `AppDelegate.swift` 中添加: ```swift import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let channel = FlutterMethodChannel(name: "device_info", binaryMessenger: controller.binaryMessenger) channel.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in if call.method == "isSimulator" { result(self?.isSimulator()) } else { result(FlutterMethodNotImplemented) } } GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func isSimulator() -> Bool { #if targetEnvironment(simulator) return true #else return false #endif } } ``` ### 实现原理 1. **Android检测逻辑**: - 通过检查 `Build` 类的特定字段(如 `FINGERPRINT`, `MODEL`, `PRODUCT` 等) - 模拟器通常包含关键词:`generic`, `Emulator`, `google_sdk`, `Android SDK built for x86` 2. **iOS检测逻辑**: - 使用Swift编译宏 `#if targetEnvironment(simulator)` - 该宏仅在Xcode模拟器环境下生效 3. **跨平台通信**: - 通过 `MethodChannel` 实现Dart与原生代码双向通信 - 统一返回布尔值标识设备类型 ### 注意事项 1. Android真机可能返回假阳性(某些定制ROM可能包含模拟器关键词) 2. iOS模拟器检测100%准确,无需额外配置 3. 需要分别在Android/iOS目录实现原生代码(路径:`android/app/.../MainActivity.kt` 和 `ios/Runner/AppDelegate.swift`) ### 相关引用 Flutter平台通道的官方实现可参考文档:[Writing custom platform-specific code](https://docs.flutter.dev/platform-integration/platform-channels)[^1] --- ### 相关问题 1. 如何在Flutter中获取设备的其他硬件信息(如CPU架构、内存大小)? 2. Flutter如何实现只在真机环境下调用特定功能(如摄像头、GPS)? 3. 在混合开发中,Flutter与原生平台通信还有哪些常用方式?
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

androidstarjack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值