likes
comments
collection
share

【FlutterUtilCode】Flutter工具篇之DeviceUtils

作者站长头像
站长
· 阅读数 20

前言

FlutterUtilCode 是一个 Flutter 工具类集合插件,封装了常用的工具类函数,方便开发者调用。

本篇是 Flutter工具篇之DeviceUtils,系列文章内容主要介绍插件中工具类的功能、用法和代码实现等,感兴趣的同学可以持续关注。

FlutterUtilCode 系列(一)—— Flutter工具篇之LogUtils、SharedPerfsUtils

FlutterUtilCode 系列(二)—— Flutter工具篇之ToastUtils

FlutterUtilCode 系列(三)—— Flutter工具篇之UuidUtils

FlutterUtilCode 系列(四)—— Flutter工具篇之DeviceUtils

Device工具类-DeviceUtils

在 App 开发中,获取安装设备的信息是非常必要的。这些信息不仅可以协助开发者了解用户群体、优化应用程序并提供更好的用户体验和服务,还可以帮助开发者更好地定位和修复应用程序中的漏洞、错误或其他技术问题,从而确保应用程序始终保持高质量和可靠性。

一般来说,我们需要获取设备的设备ID设备品牌设备型号系统版本号系统版本名称等,我们的 DeviceUtils 就围绕这些信息来实现。

这里我们用到了 pub 上的 device_info_plus 。device_info_plus 是一个获取当前设备信息的插件,其 LIKES 数达到了 1.6K 也是非常收欢迎,目前已支持全平台。

【FlutterUtilCode】Flutter工具篇之DeviceUtils

一、代码实现

DeviceUtils 目前暂只支持 Android /iOS 平台设备信息获取,后续会加上其他平台。 我们通过 getDeviceData() 方法获取当前平台的设备信息并缓存下来,方便多次调用。实现代码如下:

  /// 获取设备信息
  static Future<Map<String, dynamic>> getDeviceData() async {
    if (_deviceData != null) {
      return _deviceData!;
    }
    if (kIsWeb) {
      _deviceData = _readWebBrowserInfo(await deviceInfoPlugin.webBrowserInfo);
    } else {
      _deviceData = switch (defaultTargetPlatform) {
        TargetPlatform.android => _readAndroidDeviceInfo(await deviceInfoPlugin.androidInfo),
        TargetPlatform.iOS => _readIosDeviceInfo(await deviceInfoPlugin.iosInfo),
        TargetPlatform.linux => _readLinuxDeviceInfo(await deviceInfoPlugin.linuxInfo),
        TargetPlatform.windows => _readWindowsDeviceInfo(await deviceInfoPlugin.windowsInfo),
        TargetPlatform.macOS => _readMacOsDeviceInfo(await deviceInfoPlugin.macOsInfo),
        TargetPlatform.fuchsia => <String, dynamic>{'Error:': 'Fuchsia platform isn\'t supported'},
      };
    }
    return _deviceData!;
  }

对于设备型号,由于 iOS 和 Android 两个平台的设备型号是同一个字段,所以我们只需要通过 model 字段获取即可。

	/// 获取设备型号
  static Future<String> getModel() async {
    Map<String, dynamic> deviceData = await getDeviceData();
    return deviceData['model'] ?? '';
  }
  

对于设备品牌,iOS 中是通过 brand 字段获取, Android 中则是通过 name 字段获取。

	/// 设备品牌
  static Future<String> getBrand() async {
    Map<String, dynamic> deviceData = await getDeviceData();
    if (kIsWeb) {
      return '';
    } else {
      return switch (defaultTargetPlatform) {
        TargetPlatform.android => deviceData['brand'],
        TargetPlatform.iOS => deviceData['name'],
        TargetPlatform.linux => '',
        TargetPlatform.windows => '',
        TargetPlatform.macOS => '',
        TargetPlatform.fuchsia => '',
      };
    }
  }

对于系统版本号,在 iOS 中通过 systemVersion 字段获取,在 Android 中通过 version.sdkInt 获取。注意 version.sdkInt 获取的是 Android系统的 SDK_INT,而不是我们平常称呼的 AndroidXX

对于系统名称,在 iOS 中通过 systemName 字段获取,在 Android 中通过 display 获取。在 iOS 中获取的是 iOS,而在 Android 中获取的是厂商的系统名称,例如:CH6J-H6211V-Q-OP-201215V091。

 	/// 获取设备系统版本号
  static Future<String> getSystemVersion() async {
    Map<String, dynamic> deviceData = await getDeviceData();
    if (kIsWeb) {
      return '';
    } else {
      return switch (defaultTargetPlatform) {
        TargetPlatform.android => '${deviceData['version.sdkInt']}',
        TargetPlatform.iOS => '${deviceData['systemVersion']}',
        TargetPlatform.linux => '',
        TargetPlatform.windows => '',
        TargetPlatform.macOS => '',
        TargetPlatform.fuchsia => '',
      };
    }
  }
  
   /// 获取设备系统名称
  static Future<String> getSystemName() async {
    Map<String, dynamic> deviceData = await getDeviceData();
    if (kIsWeb) {
      return deviceData['browserName'];
    } else {
      return switch (defaultTargetPlatform) {
        TargetPlatform.android => deviceData['display'],
        TargetPlatform.iOS => deviceData['systemName'],
        TargetPlatform.linux => '',
        TargetPlatform.windows => '',
        TargetPlatform.macOS => '',
        TargetPlatform.fuchsia => '',
      };
    }
  }

对于最重要的 设备唯一Id 获取,由于其获取方式不同于上面的情况,这里我们需要着重讲解下。

对于 iOS 来说,获取设备唯一Id 非常简单,只需要调用系统API 获取即可。但是,对于 Android 来说,由于其复杂的系统版本、高版本系统对权限的收紧以及第三方厂商的定制化,导致获取设备唯一Id 的方式更加复杂。

针对 Android 系统获取设备的复杂性,我们采用目前行业内比较通用的方式:获取AnroidID + UUID + 本地缓存 来获取设备唯一ID。

  /// 获取唯一设备 ID
  /// 若没有从设备获取到,则生成一个 UUID 作为设备 ID
  static Future<String> getDeviceId() async {
    // 如果已经获取过设备ID,则直接返回
    if (_deviceId != null && _deviceId!.isNotEmpty) {
      return _deviceId!;
    }
    // 从本地获取设备ID
    String? deviceId = await SharedPrefsUtil.getString(SPConstants.deviceId);
    if (deviceId != null && deviceId.isNotEmpty) {
      _deviceId = deviceId;
      return _deviceId!;
    }
    // 获取设备ID
    Map<String, dynamic> deviceData = await getDeviceData();
    if (kIsWeb) {
      _deviceId = '';
    } else {
      _deviceId = switch (defaultTargetPlatform) {
        TargetPlatform.android => await _getAndroidId(),
        TargetPlatform.iOS => deviceData['identifierForVendor'],
        TargetPlatform.linux => '',
        TargetPlatform.windows => '',
        TargetPlatform.macOS => '',
        TargetPlatform.fuchsia => '',
      };
    }
    // 如果获取不到,则生成一个UUID
    _deviceId = _getUniqueDeviceId(_deviceId);
    // 保存到本地
    SharedPrefsUtil.putString(SPConstants.deviceId, _deviceId!);
    return _deviceId!;
  }

对于已获取过的设备Id 直接取出缓存返回,若没有获取过设备Id 的则通过 _getAndroidId() 方法获取 AndroidId。我们引入 android_id 库,并针对部分系统版本获取的 AndroidId 重复问题进行处理。

  /// 获取 AndroidId
  static Future<String?> _getAndroidId() async {
    String? androidId = await _androidIdPlugin.getId();
    if ("9774d56d682e549c" == androidId) return null;
    // 根据 AndroidId 生成 UUID
    if (androidId != null && androidId.isNotEmpty) {
      return UuidUtils.getUuidV5(androidId);
    }
    return null;
  }

若是 AndroidId 没有获取到,则在 _getUniqueDeviceId() 方法中进行判断,并生成 UUID 代替。

  /// 获取 设备唯一 ID
  static String _getUniqueDeviceId(String? deviceId) {
    if (deviceId == null || deviceId.isEmpty) {
      return UuidUtils.getUuid();
    } else {
      return deviceId;
    }
  }

最后,将获取的设备Id 通过 SharedPrefsUtil 缓存,确保在用户没有卸载应用的情况下,获取到的设备ID 是唯一的。

...
// 如果获取不到,则生成一个UUID
_deviceId = _getUniqueDeviceId(_deviceId);
// 保存到本地
SharedPrefsUtil.putString(SPConstants.deviceId, _deviceId!);
return _deviceId!;

总结一下:

  1. iOS :通过 identifierForVendor 字段,能够直接获取到设备唯一ID。
  2. Android :先获取 AndroidID ,若获取到则生成设备唯一ID。若获取不到,则生成 UUID 字符串作为唯一标识。
  3. 设备ID是否唯一 :iOS 获取到的设备ID 一定是唯一的。Android 在没有卸载重装的情况下也是唯一的,若是卸载重装,若获取到设备的 AndroidID 则也是唯一的,只有在没有获取到 AndoridID 并且有卸载重装行为的,才会出现不一致的问题。

二、使用案例

DeviceUtils 使用起来非常简单,只需要一行代码调用即可:

/// 获取设备唯一ID
await DeviceUtils.getDeviceId();
/// 获取设备型号
await DeviceUtils.getModel();
/// 获取设备品牌
await DeviceUtils.getBrand();
/// 获取操作系统版本号
await DeviceUtils.getSystemVersion();
/// 获取系统版本名称
await DeviceUtils.getSystemName();

运行结果:

【FlutterUtilCode】Flutter工具篇之DeviceUtils

结语

好了,今天的工具类整理文章就到这里,目前插件已发布到 Pub 中,欢迎大家体验。

如果觉得这篇文章对你有所帮助的话,不要忘记一键三连哦,大家的点赞是我更新的动力🥰。

Pub: flutter_util_code

项目源码:FlutterUtilCode

使用案例:Example