Android 项目必备(四)--> 获取设备唯一标识

2023-05-16

在这里插入图片描述

文章目录

      • 一、设备ID的作用
      • 二、获取设备ID的API
      • 三、设备ID的特性分析
      • 四、具体实现

设备唯一标识对于 app 开发是很重要的一个点,主要应用于统计,有时也应用于业务。
Android 平台提供了很多获取唯一标识的 API,但都不是很稳定。

一、设备ID的作用

关于设备ID的作用,大概可以分为下面几点:

  • 统计需求
    统计需求是设备ID最常见的用途,包括DAU, MAU的统计,行为统计,广告激活的统计等。

  • 业务需求
    设备ID通常也用于业务中。
    比如结合行为统计做用户画像,以为用户提供个性化的服务,大家感受比较明显的就是新闻类和电商类的APP了。
    这类操作,有利有弊,仁者见仁智者见智。
    又如,定向推送,不一定是广告推送,错误修复,内测推送等也会用到设备ID。
    还有是一些和特定业务结合的用途,比如构造分布式ID等。

  • 风控需求
    设备ID还可用于防刷单,反作弊等。
    当然,风控需求仅靠设备ID是无法完成的,通常需要建立一套反作弊系统。
    关于这方面的内容,难以一言以蔽之,这里我们不多作展开。

二、获取设备ID的API

IMEI

IMEI 本该最理想的设备 ID,具备唯一性,恢复出厂设置不会变化(真正的设备相关)。

然而,获取IMEI需要 READ_PHONE_STATE 权限,估计大家也知道这个权限有多麻烦了。

尤其是Android 6.0以后, 这类权限要动态申请,很多用户可能会选择拒绝授权。

我们看到,有的APP不授权这个权限就无法使用, 这可能会降低用户对APP的好感度。

而且,Android 10.0 将彻底禁止第三方应用获取设备的IMEI, 即使申请了 READ_PHONE_STATE 权限。
所以,如果是新 APP,不建议用IMEI作为设备标识;

如果已经用 IMEI 作为标识,要赶紧做兼容工作了,尤其是做新设备标识和IMEI的映射。

MAC 地址

一般是指 wifi 模块的 mac 地址。
此处分析 wifi 模块:

  • 优点:
    1、硬件标识,刷机和恢复出厂设置不擦除;
    2、大多android设备都有wifi模块。
  • 缺点:
    1、基于隐私考虑,官方不建议获取;6.0之后通过WifiManager 获取不到真正的mac地址,7.0之后访问不了/sys/class/net/wlan0/address
    2、不同的厂商有不同的限制,比如同样是7.0,一加3可以访问,小米6不可以访问。

如今,还是可以从NetworkInterface中获取到MAC的,但说不好后面也不可用了。

public static String getWifiMac() {
    try {
        Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
        if (enumeration == null) {
            return "";
        }
        while (enumeration.hasMoreElements()) {
            NetworkInterface netInterface = enumeration.nextElement();
            if (netInterface.getName().equals("wlan0")) {
                return formatMac(netInterface.getHardwareAddress());
            }
        }
    } catch (Exception e) {
        Log.e("tag", e.getMessage(), e);
    }
    return "";
}

设备序列号

通过android.os.Build.SERIAL获得,由厂商提供。

如果厂商比较规范的话,设备序列号+Build.MANUFACTURER应该能唯一标识设备。

但现实是并非所有厂商都按规范来,尤其是早期的设备。

最致命的是,Android 8.0 以上,android.os.Build.SERIAL 总返回 “unknown”;

若要获取序列号,可调用Build.getSerial() ,但是需要申请 READ_PHONE_STATE 权限。

到了Android 10.0以上,则和IMEI一样,也被禁止获取了。

总体来说,设备序列号有点鸡肋:食之无味,弃之可惜。

ANDROID_ID

Android ID 是获取门槛最低的,不需要任何权限,64bit 的取值范围,唯一性算是很好的了。
但是不足之处也很明显:

  • 刷机、root、恢复出厂设置等会使得 Android ID 改变;
  • Android 8.0 之后,Android ID 的规则发生了变化:

对于升级到8.0之前安装的应用,ANDROID_ID 会保持不变。如果卸载后重新安装的话,ANDROID_ID将会改变。

对于安装在8.0系统的应用来说,ANDROID_ID根据应用签名和用户的不同而不同。ANDROID_ID的唯一决定于应用签名、用户和设备三者的组合。

两个规则导致的结果就是:
第一,如果用户安装APP设备是8.0以下,后来卸载了,升级到8.0之后又重装了应用,Android ID不一样;

第二,不同签名的APP,获取到的Android ID不一样。

其中第二点可能对于广告联盟之类的有所影响。

三、设备ID的特性分析

唯一性

唯一性: 两台不同的设备获取到的设备ID不相同。
分析唯一性,我们可以从ID的分配来入手:

1、按规则构造
比如自增ID(包括分步自增),分段构造的ID(如snowflake算法)等,此类ID能保证唯一性。
设备ID中的IMEI,设备序列号,MAC等,都是按照规则构造的,理论上能保证唯一性。
设备序列号是对厂商本身唯一,全局唯一需要在加上 Build.MANUFACTURER。
不过,设备序列号和MAC的唯一要打个问号,因为要看厂商是否遵守规则。
但随着手机产业的日渐成熟,传统意义上的山寨设备已越来越少,所以大多数情况下还是唯一的。

2、随机生成
比如UUID和Android ID,这类ID有一定的概率会重复,关键是看ID的长度(有多少bit)。

稳定性

稳定性:同一台设备在不同的时间, 获取到设备ID相同。

稳定性有两个层面:
1、ID的生命周期
IMEI,序列号,MAC等都是硬件相关,即使刷机也不会改变;
Android ID则稳定性较弱,恢复出厂设置和刷机都会改变Android ID。

2、受版本的变化的影响
随着Android版本的提升,Google对权限是越收越紧了。
获取设备ID的API,要么收起不给用(IMEI), 要么获取变得困难(SERIAL ),要么不同签名的APP获取的值不一样(Android ID)。
同时,Android 10中存储权限也收缩了,之前的那种生成唯一ID写到SD卡的某个角落的,以求卸载重装后读之前的ID等方法也不奏效了。
加强隐私方面的权限,对用户而言是好事,但对开发者而言就比较难受了。
尤其是有的API本来可以用,升级后就获取不到了,这种断崖式的变化,可能会对数据统计造成影响。

四、具体实现

private fun matchDeviceId(deviceIdList: List<DeviceId>, r: DeviceId): DeviceId? {
	if (deviceIdList.isEmpty()) {
		return null
	}
	var maxPriorityDid : DeviceId? = null
	var priority = 0
	deviceIdList.forEach { did ->
		val s = idMatch(did.serial_no, r.serial_no)
		val a = idMatch(did.android_id, r.android_id)
		val m = idMatch(did.mac, r.mac)
		if (s && m && a) {
			return did
		}

		if(priority == 3) return@forEach
		if ((s && (a || m)) || (a && m)) {
			priority = 3
			maxPriorityDid = did
		}

		if(priority >= 2) return@forEach
		val p = idMatch(did.physics_info, r.physics_info)
				|| idMatch(did.dark_physics_info, r.dark_physics_info)
		if (p && a) {
			priority = 2
			maxPriorityDid = did
		}

		if(priority >= 1) return@forEach
		if (p && m) {
			priority = 1
			maxPriorityDid = did
		}
	}
	return maxPriorityDid
}

解释说明:

  • 如果设备序列号、Android ID、MAC全都不等,则前面的SQL查询不会返回记录(也就是没有匹配的设备)。
  • 如果设备序列号,Android ID 和 MAC 全部相同,直接返回。
  • 否则,遍历列表,取优先级最高的deviceId返回。
  • 如果只有Android ID 或 MAC 之一相等,但是设备信息都匹配不上的话,也认为不是同一个设备。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 项目必备(四)--> 获取设备唯一标识 的相关文章

  • ContentResolver的notifyChange方法是否也通知详细的Uri?

    在应用数据期间我使用notifyChange与Uri 假设我通知content com package my items 我还有详细的Activity显示数据来自content com package my items 1 是否通知 一般
  • 适用于 IOS 和 Android 的支付网关 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在开发一个应用程序 用户必须在澳大利亚餐馆通过应用程序 android ios 付款 有两种付款方式 通过 PayPal 或 Visa
  • 在我的 Android 应用程序中使用 ServerValue.TIMESTAMP

    我读过很多相关的 stackoverflow 问题 ServerValue TIMESTAMP 但我不知道如何在我的应用程序中使用它 我需要获取帖子创建的时间戳 时间戳应该添加到与帖子的 uid 作者等相同的位置 代码片段其中写这篇文章Fi
  • 我在哪里可以获得可靠的熵来源(真正的随机性字节[])?

    目前 我正在寻找一种方法来增加随机性的质量 in my Android应用程序 纸牌游戏 之前 估计对于我的情况 52 排列 至少需要 226 位熵 226 个随机位 我打算用这个byte 作为种子SecureRandom SecureRa
  • 让协程等待之前的调用

    我还没有完全掌握 Kotlin 协程 基本上我希望协程在执行之前等待任何先前的调用完成 下面的代码似乎可以工作 但它正在做我认为它正在做的事情吗 private var saveJob Job null fun save saveJob s
  • 使用 HttpClient 时,为什么服务器响应中省略了 Content-Length 标头?

    我正在使用这个问题的源代码如何异步执行httprequest并显示下载响应的进度 https stackoverflow com questions 9594318 how to asynchronous perform a httpreq
  • Android 中的 Sugar ORM:更新 SQLite 中保存的对象

    我是在 Android 上使用 SQLite 和 Sugar ORM 进行应用程序开发的新手 并尝试阅读 Sugar ORM 文档 但没有找到有关如何更新 SQLite 中保存的对象的任何信息 更改对象属性后还可以保存对象吗 就像是 Cus
  • Android PhoneGap 插件,UI 选项卡栏,调整 WebView 大小

    我正在创建一个美味的 PhoneGap 插件 希望一旦它能被打开 准备好了 插件基本完成了 我只需要一个漂亮的用户界面 相互作用 简而言之 我想创建一个 本机 android 工具栏组件 如果您实现 PhoneGap UIControls
  • 更改卡片高度即更改 Jetpack 中与 Material 3 组合的卡片颜色

    我正在使用 Card 可组合项 我希望它的颜色为白色 但是当我向它添加一些高度时 它的颜色会更改为更像主要容器颜色 我看过文档 其中有一种称为高程覆盖的东西 但找不到说明如何使用它的示例 这是我的代码 Card modifier Modif
  • Google 地图删除标记路线上下文菜单

    我使用 Android Studio 的 Google 地图模板启动了一个新项目 并在地图上添加了一个标记 LatLng location new LatLng lat lng Marker marker mMap addMarker ne
  • 具有自定义源集的 Android Gradle 风格 - gradle 文件应该是什么样子?

    我有一个旧的 eclipse 项目 我已经转移到 android studio 并设置为使用flavor 它似乎工作得很好 直到我开始尝试在我的风格之间使用不同的 java 文件 我的项目设置是这样的 ProjectRoot acitonb
  • Android中如何检测WIFI连接何时建立?

    我需要检测何时通过 WIFI 建立网络连接 发送什么广播来确定已建立有效的网络连接 我需要验证是否存在有效的 HTTP 网络连接 我应该监听什么以及需要进行哪些额外测试才能知道是否存在有效连接 您可以注册一个BroadcastReceive
  • Android:使 Dialog 周围的所有内容都比默认值更暗

    我有一个具有以下样式的自定义对话框 它显示了一个无边框对话框 后面的任何内容都会 稍微 变暗 我的设计师希望背后的一切都比 Android 的默认设置更暗 但不是完全黑色 有这样的设置吗 我能想到的唯一解决方法是使用全屏活动而不是对话框 只
  • 来自相机的 MediaCodec 视频流方向和颜色错误

    我正在尝试流式传输视频捕获直接从相机适用于 Android 设备 到目前为止 我已经能够从 Android 相机捕获每一帧预览帧 byte data Camera camera 函数 对数据进行编码 然后成功解码数据并显示到表面 我用的是安
  • android textview 有字符限制吗?

    我正在尝试在 android TextView 中输入超过 2000 3000 个字符 它不显示任何内容 任何一份指南是否对 android textview 有字符限制或什么 我在G3中做了一些小测试 我发现 如果activtiy布局中有
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • 屏幕开/关检测

    在这里 我试图确定屏幕是否打开 但按下电源锁定 解锁按钮时它似乎不起作用 应用程序运行没有错误 但 if else 中的代码似乎没有效果 Edited现在代码可以工作了 谢谢Olgun 但媒体播放器播放不会停止 并且每次在屏幕上 离屏时都会
  • 我在 PopupMenu 中使用 ShareActionProvider,但显示两个 PopupMenu?

    我在 PopupMenu 中使用 ShareActionProvider 但是当我单击共享菜单项时 它会在屏幕上显示两个 PopupMenus 一个被另一个覆盖 一个显示应用程序图标和名称 另一个仅显示应用程序名称 除了这个问题之外 它工作
  • 如何在Android中创建一个简洁的两栏输入表单?

    我想创建一个整洁的两列输入表单 如下所示 到目前为止我的 xml 布局代码
  • 如何在布局编辑器中模拟沉浸式模式

    我想在布局编辑器中全屏查看我的布局 我正在使用 eclipse 插件 我已经通过选择隐藏了 ActionBar NoActionBar组合中的主题 但导航栏是一个不同的故事 AFAIK 它只能使用代码中的标志来隐藏 我需要在活动 xml 文

随机推荐

  • opencv学习之(五)-直方图计算和绘制图像直方图

    1 直方图 灰度直方图的定义 灰度直方图是灰度级的函数 xff0c 描述图像中该灰度级的像素个数 xff08 或该灰度级像素出现的频率 xff09 xff1a 其横坐标是灰度级 xff0c 纵坐标表示图像中该灰度级出现的个数 xff08 频
  • 学习opencv之(六)-图像切割,使用ROI

    一 ROI介绍 在OpenCV中我们能够非常方便地获取指定ROI区域的子图像 如果你对图像设置了ROI xff0c 那么 xff0c Opencv的大多数函数只在该ROI区域内运算 xff08 只处理该ROI区域 xff09 xff0c 如
  • 虚拟机Ubuntu蓝屏闪屏解决方法

    问题分析 启动 Ubuntu 可以进入登录界面 xff0c 但是系统界面蓝屏 xff0c 说明系统是可以运行起来的 证明系统是没有问题的 应该是系统插件发生了错误 没有发生大块的核心数据损坏 xff0c linux 系统一般都以修复 xff
  • meta标签清理缓存

    meta标签清理缓存 如果需要在html页面上设置不缓存 xff0c 这在 lt head gt 标签中加入如下语句 xff1a lt meta http equiv 61 34 Pragma 34 content 61 34 no cac
  • Go语言入门(二)——Ubuntu系统中Go的安装

    下载并安装 安装包地址 xff1a https golang google cn dl 选择这个下载 xff0c 稍等几秒即可 下载下来的压缩包在Downloads文件夹中 解压二进制文件 将下载的二进制包解压至 usr local目录 x
  • /etc/apt/sources.list" E212: Can't open file for writing解决方案

    w sudo tee gt dev null 解决 转载于 https www cnblogs com tangyouwei p 10109090 html
  • poj 1947 树形dp(得到含P个节点联通块的最小切边数)

    题意 xff1a 给定一颗树 xff0c 通过删除边要得到一个含有P个节点的连通块 xff0c 问最小的删边数 思路 xff1a 树形DP dp x i 记录x结点 xff0c 要得到一棵i个节点的子树去掉的最少边数 搜索到x节点时 xff
  • BMC相关

    BMC基本概念介绍 xff1a BMC xff1a 基板管理控制器 Baseboard Management Controller BMC xff08 Baseboard Management Controller xff0c 基板管理控制
  • ARM ASPEED 2500 uboot openbmc linux 启动记录

    支持原创 xff0c 转载请注明出处 ARM ASPEED 2500 uboot openbmc linux 启动记录 前言 其实openbmc 官方推荐的方法是使用Yocto poky方法来定制aspeed 2500相关的组件 xff0c
  • Serdes 原理及调试学习

    Serdes原理与设计实践之一 xff1a Serdes简介 1 Serdes简介 为了提高接口传输带宽 xff0c 设计中经常采用并行总线设计 并行总线通过提高时钟速率和数据位宽来提高传输带宽 限制接口传输带宽主要有2个方面 xff1a
  • 查看当前Linux系统的内核编译config文件,生成编译驱动所需的内核头文件

    1 xff09 查看内核配置选项 有时候我们需要查看Linux系统的内核是否在编译的时候的编译选项 xff0c 从而确定某个模块是否已经加入到内核以及参数设置等等 xff0c 方法有两种 xff1a zcat proc config gz
  • objdump adr2line定位驱动问题

    https linux kernel labs github io refs heads master labs kernel modules html https linux kernel labs github io refs head
  • debian查询命令所属软件包的方法

    在debian系统中 xff0c 类似centos的yum whatprovides这条查询系统中某个命令属于哪个安装包的命令 xff0c 有如下两类方法 xff08 离线查找和非离线查找 xff09 xff1a 第一种 xff0c 查本机
  • warning: function declaration isn’t a prototype(函数声明不是原型)的解决办法

    linux驱动中定义一个无参的函数 int probe num 警告 xff1a 函数声明不是一个原型 Wstrict prototypes 应对方法 xff1a 改成 int probe num void 警告消失
  • fstat函数

    stat系统调用系列包括了fstat stat和lstat xff0c 它们都是用来返回 相关文件状态信息 的 xff0c 三者的不同之处在于设定源文件的方式不同 1 首先隆重介绍的是一个非常重要的 VIP 人物 xff0c 他是fstat
  • c语言中用宏定义一个常量,数字后面带个U, L, F的含义

    c语言中数字后面带个U是什么意思 xff1f define F CPU 12000000U 答 xff1a U表示该常数用无符号整型方式存储 xff0c 相当于unsigned int L表示该常数用长整型方式存储 xff0c 相当于lon
  • 解决sudo: npm: command not found

    sudo ln s opt node v11 4 0 bin npm usr bin npm sudo ln s opt node v11 4 0 bin node usr bin node 转载于 https www cnblogs co
  • python 安装本地包的方法

    pip install whl 直接在pip install命令后添加whl包的全路径名就能本地安装成功了 下载需要的包 xff0c 一般为zip tar gz等的压缩包 xff0c 解压后 xff0c 打开命令行 xff0c 进入解压目录
  • 光模块规范下载网站 (sff 8472 sff 8436)

    https www snia org technology communities sff specifications field doc status value 61 All amp combine 61 8472 amp items
  • Android 项目必备(四)--> 获取设备唯一标识

    文章目录 一 设备ID的作用二 获取设备ID的API三 设备ID的特性分析四 具体实现 设备唯一标识对于 app 开发是很重要的一个点 xff0c 主要应用于统计 xff0c 有时也应用于业务 Android 平台提供了很多获取唯一标识的