如何生成唯一的Android设备ID?

前几天接到个任务,旧的那套获取设备标识的方法经常返回相同的ID,所以需要我重新寻找一套可靠的区分设备的方法。经过几天的研究,大致有了一些想法。

先从需求出发分析出我的主要目标——为每一台设备生成唯一、软件无关的标识ID。
其中关键词是“唯一”和“软件无关”,“唯一”要求不同设备有不同的ID,“软件无关”是考虑到软件重装后或者不同软件都能根据相同的方法获取到相同的ID。
  根据多年被坑的经验,对于一个运行在设备系统上的App,获取的每一个硬件的标识都不完全可靠,可能因为用户修改了硬件信息,或者厂商定制系统有漏洞,所以我们在寻找一套符合主要目标的的方案的同时,还要尽可能减少这些不可靠因素的影响。
  翻阅各种资料,最后得出比较有价值的硬件标识有以下几个:

  • Device ID
    开发者可以使用系统提供的TelephonyManager服务来获取Device ID,GSM设备返回的是IMEI码,CDMA设备返回的是MEID码或者ESN码。
    使用Device ID的优点在于重复率低,如果返回了Device ID,可以保证每个ID都是唯一的,但缺点也很明显,首先平板电脑等非手机设备无法提供Device ID,其次6.0以后需要用户动态授予READ_PHONE_STATE权限,如果用户拒绝就无法获得Device ID了。
    public static String getDeviceId(Context context) {
    TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    String deviceId = telephonyManager.getDeviceId().toString();
    return deviceId;
    }

  • Android ID
    在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被wipe后该值会被重置。
    Android ID是一个不错的选择,64位的随机数重复率不高,而且不需要申请权限,但也有些小问题,比如有个很常见的Bug会导致设备产生相同的Android ID: 9774d56d682e549c,另外Android ID的生成不依赖硬件,刷机或者升级系统(这个没验证过)都会改变Android ID。
    public static String getAndroidId(Context context) {
    return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
    }

  • Mac地址
    WLAN Mac地址和Bluetooth Mac地址都是与硬件相关的唯一号码,分别需要ACCESS_WIFI_STATE和BLUETOOTH权限,其中WLAN Mac地址经常被作为参数来生成设备标识,但是在Android 6.0(iOS 7)以后,官方以保护用户隐私为由关闭了获取Mac地址的接口,调用获取的方法会统一返回 02:00:00:00:00:00,虽然还是有间接获取Mac地址的方法,但既然官方不再支持使用,有更好选择的情况下就暂时不考虑使用Mac地址了。此外获取这两个Mac地址需要设备拥有Wifi和蓝牙硬件。

    6.0获取方法参考:
    Android M 如何获取 Wifi MAC地址
    [Get Bluetooth local mac address in Marshmallow

  • Build信息
    android.os.Build类包含了很多设备信息,包括系统版本号、手机型号等硬件软件信息,具体内容参考Android获取手机制作商,系统版本等
    Build信息无需任何权限可以直接调用获取,同时使用多个信息的话一般不会都为空,缺点是重复率很高,同型号手机的Build信息很可能完全相同,而且系统升级等不属于更换设备的操作可能会修改到其中的内容,所以只考虑作为生成设备ID 的辅助参数。
    public static String getBuildInfo() {
    //这里选用了几个不会随系统更新而改变的值
    StringBuffer buildSB = new StringBuffer();
    buildSB.append(Build.BRAND).append("/");
    buildSB.append(Build.PRODUCT).append("/");
    buildSB.append(Build.DEVICE).append("/");
    buildSB.append(Build.ID).append("/");
    buildSB.append(Build.VERSION.INCREMENTAL);
    return buildSB.toString();
    // return Build.FINGERPRINT;
    }

综上所述,Device ID和Mac地址都需要额外的权限,而且并非适用于所有Android设备,所以不考虑使用。最终方案选用Android ID和Build信息做种子来生成设备ID,如果只需要做用户统计等不关心到具体设备的功能,可以只使用Android ID做种子,在获取到的Android ID等于9774d56d682e549c时,就随机生成一个ID代替;如果要实现推送消息等需要精确区分设备的功能,可以用Android ID和Build的部分设备信息做种子。

还需要注意的是,以上全部标识都可能被用户或者系统修改的,应用每次获取的Android ID或者Device ID等种子可能并不相同,生成的设备ID也会不一样,为了解决这问题,可以把生成的设备ID保存起来,每次使用时先检查有没有已经保存的设备ID,如果没有才生成一个并保存,保存的位置可以是应用的私有空间或者公共空间,具体选择视乎是否需要多个应用使用同一个设备ID。

最终实现代码如下:

  /*** 获取Android Id** @param context* @return*/public static String getAndroidId(Context context) {return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);}/*** 获取Build的部分信息** @return*/public static String getBuildInfo() {//这里选用了几个不会随系统更新而改变的值StringBuffer buildSB = new StringBuffer();buildSB.append(Build.BRAND).append("/");buildSB.append(Build.PRODUCT).append("/");buildSB.append(Build.DEVICE).append("/");buildSB.append(Build.ID).append("/");buildSB.append(Build.VERSION.INCREMENTAL);return buildSB.toString();
//        return Build.FINGERPRINT;}/*** 最终方案,获取设备ID** @param context* @return*/public static String getDeviceUUID(Context context) {String uuid = loadDeviceUUID(context);if (TextUtils.isEmpty(uuid)) {uuid = buildDeviceUUID(context);saveDeviceUUID(context, uuid);}return uuid;}private static String buildDeviceUUID(Context context) {String androidId = getAndroidId(context);if (!"9774d56d682e549c".equals(androidId)) {Random random = new Random();androidId = Integer.toHexString(random.nextInt())+ Integer.toHexString(random.nextInt())+ Integer.toHexString(random.nextInt());}return new UUID(androidId.hashCode(), getBuildInfo().hashCode()).toString();}private static void saveDeviceUUID(Context context, String uuid) {context.getSharedPreferences("device_uuid", Context.MODE_PRIVATE).edit().putString("uuid", uuid).apply();}@Nullableprivate static String loadDeviceUUID(Context context) {return context.getSharedPreferences("device_uuid", Context.MODE_PRIVATE).getString("uuid", null);}

源码地址:
Lib-DeviceId

参考:

  1. 唯一标识符最佳做法
  2. Is there a unique Android device ID?
  3. Identifying App Installations
  4. 如何唯一的标识一台Android设备?
  5. 谈谈 Android 中的各种设备标识符

如何生成唯一的Android设备ID?相关推荐

  1. 是否有唯一的Android设备ID?

    Android设备是否具有唯一的ID,如果是,则使用Java访问它的简单方法是什么? #1楼 有许多不同的方法可以解决这些ANDROID_ID问题(有时可能为null或特定模型的设备总是返回相同的ID ...

  2. 是否有唯一的 Android 设备 ID?

    问: Android 设备是否有唯一 ID,如果有,使用 Java 访问它的简单方法是什么? 答1: 保持自己快人一步,享受全网独家提供的一站式外包任务.远程工作.创意产品订阅服务–huntsbot. ...

  3. android设备id完美解决方法,如何在Android中获取唯一的设备硬件ID?

    您可以在下面的链接中查看此博客 [http://android-developers.blogspot.in/2011/03/identifying-app-installations.html] A ...

  4. java 如何获取设备号_java – 如何获取android设备ID?

    如何获取Android设备ID?我不知道什么是"背景". import android.content.Context; import android.provider.Setti ...

  5. jsb调用java_cocos2d-js | JSB 调用Java函数 | Android设备ID

    引擎版本:cocos2d-x-3.13 语言:cocos2d-js 几乎所有的游戏项目都有获取玩家设备ID的需求,这里记录一下使用cocos2d-js时的Android设备获取方式. 用JS获取And ...

  6. Android 设备Id 唯一不重复,Redmi

    1.(唯一)不重复类: package com.xxx.xxx.util;import android.annotation.SuppressLint; import android.content. ...

  7. android设备id完美解决方法,安卓获取渠道名渠道id Android获取设备唯一标识的终极解决方法,防止安卓7.0时崩溃问题...

    一,先说获取渠道名(这里以友盟为例) /* * 4.5.1新加渠道名字段,用来传给后台去统计各个渠道下载量 * */ public static String getSource() { //获取渠道 ...

  8. android 获取设备id 崩溃,获取Android设备ID时出错

    我正在尝试在我的Android应用中检索我的adroid设备的设备ID.但是,在我的程序中添加以下行后,错误存在并且程序无法启动: String ts = Context.TELEPHONY_SERV ...

  9. Android设备的ID

    Android的开发者在一些特定情况下都需要知道手机中的唯一设备ID.例如,跟踪应用程序的安装,生成用于复制保护的DRM时需要使用设备的唯一ID.在本文档结尾处提供了作为参考的示例代码片段. 范围 本 ...

  10. Android设备新型恶意软件,融合银行木马、键盘记录器和移动勒索软件等功能

    2019独角兽企业重金招聘Python工程师标准>>> 网络犯罪分子目前正在开发一种针对Android设备的新型恶意软件,它融合了银行木马.键盘记录器和移动勒索软件的功能. 根据来自 ...

最新文章

  1. obs可以推到中转服务器吗,[经验分享]OBS 如何实现多路推流
  2. 博客开通了....激动 呵呵
  3. 走向.NET架构设计—第四章—业务层分层架构(前篇)
  4. HDU2066:一个人的旅行(Dijkstra)
  5. java 翻转句子_Java编程-句子反转
  6. Python单元测试介绍及单元测试理解,单元测试的自动生成(对函数进行测试)
  7. MySQL日期与时间函数
  8. python字符串转成数组_python将字符串转换成数组的方法
  9. 深度学习解释:Precision、Recall、IoU、Ap/mAp
  10. 安卓开发 在oncreate显示对话框 hide 之后 点不动_微信小程序云开发教程微信小程序的API入门常用API...
  11. linux重启tomcat命令
  12. 雨林木风推出高仿Windows操作系统
  13. EasyRecovery易恢复文件数据恢复软件详解介绍安装
  14. 韩寒式的幽默-屌丝回忆录
  15. 如何将div拼接成html代码,给div拼接html 拼接字符串
  16. 算术平方根的整数部分(简单)*求平方根的三种方法**整数与小数取绝对值*
  17. MySQL事务之脏读问题
  18. 软件工程第二次作业——git的使用
  19. fiash星空动画制作_Flash8实现动态星空的通用方法
  20. 触觉智能分享-RK3568 Android11修改默认输入法

热门文章

  1. matlab人口增长模型(指定函数拟合)
  2. 使用eclipse导入spring-framework-5.0.x源码
  3. 计算机网络原理恺撒密码/列置密码总结
  4. 结构张量 matlab 图像,图像处理中 结构张量(structure tensor)
  5. sqldr load 以及extract data 的中文问题--
  6. jdk6-jdk9常用版本下载地址整理
  7. 《Unix编程艺术》pdf
  8. 项目的三种组织结构形式分析与比较
  9. 专访时速云|容器云“老兵”与云原生“新战场”
  10. 时速云与炎黄盈动强强联手,打造企业 IT 变革新未来