android 16进制 全透明_你有几种实现方案Android 设备唯一标识?

c34eaa51252856415864c32f673b5942.png

2edcd1407cf6cbf3e49282a4cb9ecb20.png

前言

项目开发中,多少会遇到这种需求:获得设备唯一标识DeviceId,用于:
1.标识一个唯一的设备,做数据精准下发或者数据统计分析;
2.账号与设备绑定;
3.....

分析

这类文章,网上有许多资料,例如:使用IMEI、MAC等作为设备标识使用。
不过,看过这些文章或者深入调研的同学应该都清楚,这些数据均存在缺陷:有的因为权限无法获取到,有的获取出来是重复的,有的完全获取不到,也就是说不能完美的解决设备唯一的问题。

那么,用什么数据才能表示设备唯一呢?

方案

方案1:UUID + SharePreference(存取)

APP首次使用时,创建UUID,并保存到SharePreference中。
以后再次使用时,直接从SharePreference取出来即可;优点:数据唯一、不需要权限;
缺点:会随APP一起删除,即:重新安装APP,DeviceId值会改变(新UUID);

方案2:UUID + SD卡(存取)

APP首次使用时,创建UUID,并保存到SD卡中。
以后再次使用时,直接从SD卡取出来即可;
很多APP就是这么做的;优点:数据唯一、不随APP一起删除;
缺点:需要SD卡读写权限;防不住用户手动删除SD卡的文件;

方案3:imei + android_id + serial + 硬件uuid(自生成)

如果,又想唯一,又不想因用户的删除而重新生成UUID,该怎么做呢?

 不依赖随机的UUID,咱们根据硬件标识来创建唯一的数据;我们可以将多个可获得的硬件标识拼接起来(尽可能不依赖权限),最大程度上降低重复性。
以imei、android_id、serial为例,如果能取到值,每个数据几乎可以代表唯一。
如果这些数据都能获取到,拼起来的数据重复性降到极低(UUID也存在重复性,重复性极低而已)

那么,哪些硬件标识合适呢?

AndroidId : 如:df176fbb152ddce,无需权限,极个别设备获取不到数据或得到错误数据;
serial:如:LKX7N18328000931,无需权限,极个别设备获取不到数据;
IMEI : 如:23b12e30ec8a2f17,需要权限;
Mac: 如:6e:a5:....需要权限,高版本手机获得数据均为 02:00.....(不可使用)
Build.BOARD  如:BLA  主板名称,无需权限,同型号设备相同
Build.BRAND  如:HUAWEI  厂商名称,无需权限,同型号设备相同
Build.HARDWARE  如:kirin970  硬件名称,无需权限,同型号设备相同
Build......更多硬件信息,略

分析了这么多硬件标识,我们就使用imei + android_id + serial + 硬件UUID(使用Build属性生成,如果硬件信息不变,则UUID值不变)。这是我们项目的实际方案,大家也可根据自己的需要自由组合硬件标识。

那么,问题又来了,不同设备的硬件标识长度不同,拼接处理的DeviceId字符串长度不同,怎么才能统一长度呢?

 也很简单,我们先拼接好DeviceId数据,取其SHA1值,再转16进制即可(统一40位长度)

实现

import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;import java.security.MessageDigest;
import java.util.Locale;
import java.util.UUID;/*** @author xc* @date 2018/11/16* @desc*/
public class DeviceIdUtil {/*** 获得设备硬件标识** @param context 上下文* @return 设备硬件标识*/public static String getDeviceId(Context context) {StringBuilder sbDeviceId = new StringBuilder();//获得设备默认IMEI(>=6.0 需要ReadPhoneState权限)String imei = getIMEI(context);//获得AndroidId(无需权限)String androidid = getAndroidId(context);//获得设备序列号(无需权限)String serial = getSERIAL();//获得硬件uuid(根据硬件相关属性,生成uuid)(无需权限)String uuid = getDeviceUUID().replace("-", "");//追加imeiif (imei != null && imei.length() > 0) {sbDeviceId.append(imei);sbDeviceId.append("|");}//追加androididif (androidid != null && androidid.length() > 0) {sbDeviceId.append(androidid);sbDeviceId.append("|");}//追加serialif (serial != null && serial.length() > 0) {sbDeviceId.append(serial);sbDeviceId.append("|");}//追加硬件uuidif (uuid != null && uuid.length() > 0) {sbDeviceId.append(uuid);}//生成SHA1,统一DeviceId长度if (sbDeviceId.length() > 0) {try {byte[] hash = getHashByString(sbDeviceId.toString());String sha1 = bytesToHex(hash);if (sha1 != null && sha1.length() > 0) {//返回最终的DeviceIdreturn sha1;}} catch (Exception ex) {ex.printStackTrace();}}//如果以上硬件标识数据均无法获得,//则DeviceId默认使用系统随机数,这样保证DeviceId不为空return UUID.randomUUID().toString().replace("-", "");}//需要获得READ_PHONE_STATE权限,>=6.0,默认返回nullprivate static String getIMEI(Context context) {try {TelephonyManager tm = (TelephonyManager) 
context.getSystemService(Context.TELEPHONY_SERVICE);return tm.getDeviceId();} catch (Exception ex) {ex.printStackTrace();}return "";}/*** 获得设备的AndroidId** @param context 上下文* @return 设备的AndroidId*/private static String getAndroidId(Context context) {try {return Settings.Secure.getString(context.getContentResolver(), 
Settings.Secure.ANDROID_ID);} catch (Exception ex) {ex.printStackTrace();}return "";}/*** 获得设备序列号(如:WTK7N16923005607), 个别设备无法获取** @return 设备序列号*/private static String getSERIAL() {try {return Build.SERIAL;} catch (Exception ex) {ex.printStackTrace();}return "";}/*** 获得设备硬件uuid* 使用硬件信息,计算出一个随机数** @return 设备硬件uuid*/private static String getDeviceUUID() {try {String dev = "3883756" +Build.BOARD.length() % 10 +Build.BRAND.length() % 10 +Build.DEVICE.length() % 10 +Build.HARDWARE.length() % 10 +Build.ID.length() % 10 +Build.MODEL.length() % 10 +Build.PRODUCT.length() % 10 +Build.SERIAL.length() % 10;return new UUID(dev.hashCode(), 
Build.SERIAL.hashCode()).toString();} catch (Exception ex) {ex.printStackTrace();return "";}}/*** 取SHA1* @param data 数据* @return 对应的hash值*/private static byte[] getHashByString(String data){try{MessageDigest  messageDigest = MessageDigest.getInstance("SHA1");messageDigest.reset();messageDigest.update(data.getBytes("UTF-8"));return messageDigest.digest();} catch (Exception e){return "".getBytes();}}/*** 转16进制字符串* @param data 数据* @return 16进制字符串*/private static String bytesToHex(byte[] data){StringBuilder sb = new StringBuilder();String stmp;for (int n = 0; n < data.length; n++){stmp = (Integer.toHexString(data[n] & 0xFF));if (stmp.length() == 1)sb.append("0");sb.append(stmp);}return sb.toString().toUpperCase(Locale.CHINA);}
}

调用

String deviceId = DeviceIdUtil.getDeviceId(application);结果输出:FE00DDE9298310CDFEEFE69229B8DB248534710F

总结

方案1局限性较大,不建议使用;
方案2是很多软件采用的方案,因很少有人删除SD卡文件;但需要注意权限;
方案3相较于前两种方案,限制性较小,只要硬件信息不变结果就不变。而且该方案可自行定制组合。
究竟哪种方案合适,大家应根据自己的项目需求,合理选择才是。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/244438.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

链表的数据域怎么使用结构体_一步一步教你从零开始写C语言链表

为什么要学习链表&#xff1f;链表主要有以下几大特性&#xff1a;1、解决数组无法存储多种数据类型的问题。2、解决数组中&#xff0c;元素个数无法改变的限制(C99的变长数组&#xff0c;C也有变长数组可以实现)。3、数组移动元素的过程中&#xff0c;要对元素进行大范围的移动…

python计算bmi_Python编程语言:如何用Python编程来判断体重指数BMI是否健康

上一篇小编分享了自己学习Python语言有关字符串和模块time使用的相关知识&#xff0c;这一篇小编分享给大家的是比较有趣的运用&#xff0c;那就是如何用Python编程来表示自己体重BMI。 用Python程序来算出我们自己的BMI指数&#xff0c;来判断我们自己的健康情况&#xff0c;首…

drbd实现mysql地热备_heartheartbeat+drbd+mysql主库热备

1 环境主机名网卡磁盘mastereth0 桥接模式 eth0(192.168.1.10) 自定义模式(VMnet2)(192.168.2.10)VIP 192.168.1.200/210系统盘20G外接磁盘slaveeth0 桥接模式(192.168.1.20) eth1 自定义模式(VMnet2)(192.168.2.20)VIP 192.168.1.200/210系统盘20G外接磁盘server3eth0 桥接模式…

dba的前景_运维、测试、程序员,这些技术岗位哪个更有前景?

在一个初具规模的互联网公司&#xff0c;从业务方面出发&#xff0c;有很多岗位类型&#xff0c;比如运营、客服、市场、产品、设计、技术等等。在这些大类下面&#xff0c;还要细分各种小类&#xff0c;以技术为例&#xff0c;可分为前端(客户端)、后端、测试、运维、DBA等等&…

免安装版的mysql步骤_mysql免安装版的安装方法及步骤

mysql免安装版的安装方法及步骤发布时间&#xff1a;2020-07-15 14:07:18来源&#xff1a;亿速云阅读&#xff1a;83作者&#xff1a;清晨小编给大家分享一下mysql免安装版的安装方法及步骤&#xff0c;希望大家阅读完这篇文章后大所收获&#xff0c;下面让我们一起去探讨吧&am…

java spring框架 注解_spring框架之注解的使用

原标题&#xff1a;spring框架之注解的使用今天是刘小爱自学Java的第122天。感谢你的观看&#xff0c;谢谢你。学习内容安排如下&#xff1a;Spring注解的使用。JavaWeb项目的搭建。Spring的Web集成。本来还计划学Spring的junit测试集成的&#xff0c;结果又没时间了。一、Spri…

idea 代码格式化插件_IDEA非常棒的插件,阿里巴巴约定成文的代码公约规范

无规矩&#xff0c;不方圆。每个人都有自己的编码风格&#xff0c;每个公司也有自己的代码规范。规范的代码&#xff0c;无论是自己日常维护&#xff0c;还是以后接盘者来接盘&#xff0c;都能快速定位上手&#xff0c;大大提高效率。作为一个IDEA万年爱好者&#xff0c;这些最…

switch最大选项数目_随时随地学习C语言之3—if和switch哪个效率高?

之前学习C语言的时候&#xff0c;我经常有一个疑问&#xff0c;既然有if-else if-else结构的多分支选择语句&#xff0c;C语言为何还要制定switch这种多分支选择语句呢&#xff1f;直到两年前在分析ARM平台C语言反汇编代码的时候&#xff0c;才终于明白了switch-case这种结构存…

onclick 获取img 里面的id_红魔5S游戏手机如何解锁bl获取第三方面具root权限

自从苹果阵营发布S系列&#xff0c;安卓系列也学习苹果的步伐&#xff0c;开始发布S系列&#xff0c;这不努比亚也发不了今年夏天的游戏手机&#xff0c;红魔5S游戏手机。红魔5S相对于红魔5其实从配置来看&#xff0c;三大件配置变化并不大&#xff0c;主要还是细节上的一些提升…

java 工厂模式的写法_设计模式-工厂模式

一、概述什么是工厂模式&#xff1f;工厂模式(Factory Pattern)是最常见的一种设计模式之一。它主要是提供一种创建对象的最佳方法&#xff01;为什么要学习工厂模式&#xff1f;与通过new来创建对象不同&#xff0c;使用工厂模式创建对象不会对客户端暴露创建逻辑&#xff0c;…

jackson 序列化_jackson序列化与反序列化的应用实践

作者 | zhouweixin 来源 | urlify.cn/iEbiAz66套java从入门到精通实战课程分享1 相关概念序列化: 把对象转换为字节序列的过程称为对象的序列化反序列化: 把字节序列恢复为对象的过程称为对象的反序列化2 序列化的作用用于把内存中的对象状态保存到一个文件中或者数据库中用于网…

java mqtt客户端_基于 t-io 实现一个 mqtt5 协议之 mica-mqtt

一、简介 MQTT 全称为 Message Queuing Telemetry Transport&#xff08;消息队列遥测传输&#xff09;是一种基于发布/订阅范式的“轻量级”消息协议&#xff0c;由 IBM 发布。目前使用比较广泛的就是 mqtt 3.1.1&#xff08;2014年制定&#xff09;&#xff0c;mqtt 5.0&…

java自我介绍_JAVA面试技巧之自我介绍

【如何进行自我介绍】自我介绍这个问题&#xff0c;不用多说了&#xff0c;面试必定会问&#xff01;如果想要在自我介绍的时候就能够打动面试官&#xff0c;吸引面试官对我们的兴趣&#xff0c;那么像我们这种接受过Java培训的程序员的自我介绍当然不能和应届生或者其他非技术…

java excel 操作 poi_Java使用apache poi进行excel相关操作

一.基本介绍1.1、Apache POI介绍Apache POI是一个可以进行微软的文档进行开源库&#xff0c;可以操作的文档类型包括word、ppt、excel、visio、outlook....本文主要针对Apache POI对excel的操作进行介绍&#xff0c;主要包括如何创建一个excel、录入数据、读取excel数据的方式。…

程序解析excel中的图片_产品日志丨支持导入Excel中的图片amp;批量修改后期实体字段...

本次安捷秀又迎来了一个大版本更新&#xff0c;除了大家呼声很高的「实体模块导入 Excel 」外&#xff0c;还有「批量编辑实体」&#xff0c;「支持右击修改」以及针对海外用户的「全页面支持英文」等功能的新增与优化&#xff0c;一起来看看吧。导入功能优化AGILESHOT&#xf…

java不同垃圾回收器_细述 Java垃圾回收机制→Types of Java Garbage Collectors

本文非原创&#xff0c;翻译自Types of Java Garbage Collectors在Java中为对象分配和释放内存空间都是由垃圾回收线程自动执行完成的。和C语言不一样的是Java程序员不需要手动写垃圾回收相关的代码。这是使得Java如此流行&#xff0c;同时也是Java能帮助程序员写出更好的Java应…

js修改地址栏url_不同寻常的地址栏过渡

前几天&#xff0c;我在推特上看到这样一张图。原来地址栏还能这么玩&#xff0c;瞬间就觉得自己弱爆了。然后我决定去实现一下这个效果&#xff0c;然后做成一个库。画了一个晚上&#xff0c;终于做好了。这是最后的成果。这个库使用非常的简单。你只需要&#xff0c;yarn add…

kaggle数据集_ArXiv170万篇论文数据集上线Kaggle!

大数据文摘出品学术圈的朋友对ArXiv肯定都不陌生。在将近30年的时间里&#xff0c;ArXiv通过公开访问学术文章为公众和研究社区提供了一个更高效的学术成果沟通平台&#xff0c;从物理学到计算机科学的许多子学科&#xff0c;以及介于两者之间的所有内容&#xff0c;包括数学&a…

万能驱动xp离线版_教你用SC封装软件来封装XP系统

今天我们来讲解一下如何用SC软件来封装XP系统。今天的讲解只演示基本的SC封装软件&#xff0c;具体的封装前的准备工具&#xff0c;我们不进行讲解&#xff0c;当然前期的准备工作也是有很多&#xff0c;首先我们先要安装虚拟机软件&#xff0c;并在虚拟机上面安装好原版的XP系…

java dispo lock_java实现文件上传和下载(1)

原理&#xff1a; 使用html 的 标签&#xff0c;提交form 的几个属性必须为&#xff1a; methodpost encTypemultipart/form-data;组件:smartUpload或者commons fileuploadsmartUpload代码实现1。文件预览function showImage(obj){var strobj.value;$("#id").html(&q…