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 作为一款非常优秀的免费数据库被广泛的使用&#xff0c;平时我们开发的项目数据过百万的时候不多。最近花了大量的时间来深入的研究mysql百万级数据情况下的优化。 遇到了很多问题并解决了他们&#xff0c;特此分享给大家。欢迎提出您的宝贵意见&#xff01;一、百万级数…

python异步实现方式_Python通过yield实现异步

改写程序first函数等待long函数返回值的方式为yield,代码如下&#xff1a; import _thread import time gen None def long(): print (long execute) def fun(): time.sleep(5) result long end gen.send(result) _thread.start_new_thread(fun,()) def first(): print (firs…

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

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

jupyter notebook怎么写python代码_如何在Jupyter Notebook中使用Python虚拟环境?

如何在使用Jupyter Notebook时&#xff0c;解决Python虚拟环境间的切换问题&#xff1f;本文一步步帮你拆解。希望你能够避免踩坑的痛苦&#xff0c;把更多的时间花在愉快的编程上。 痛点 Python目前有两个主版本并存&#xff0c;这很让人苦恼。 一般人对于软件&#xff0c;总是…

cmd检查java_如何通过cmd查看java环境

展开全部JAVA环境变量设置一、下载JDK。62616964757a686964616fe4b893e5b19e31333363376561下载后是一个可执行程序&#xff0c;双击安装&#xff0c;安装路径为C:\Program Files\Java\jdk1.6.0_22\(当然&#xff0c;其他路径也可以)&#xff0c;如下图&#xff1a;二、设置环境…

ios 检测是否联网_秋招|阿里 iOS 五轮面经分享,已收到阿里的意向书

作者&#xff1a;aaaaaazzzz链接&#xff1a;https://www.nowcoder.com/discuss/302113来源&#xff1a;牛客网感觉牛客很少看到iOS的面经了&#xff0c;今天收到了阿里的意向书&#xff0c;来分享下面经&#xff0c;希望大家都Offer~&#xff01;总体感觉面试官都非常好&#…

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;这些最…

java der pem_JAVA解析各种编码密钥对(DER、PEM、openssh公钥)

一、DER编码密钥对先说下DER编码&#xff0c;是因为JCE本身是支持DER编码密钥对的解析的&#xff0c;可以参见PKCS8EncodedKeySpec和X509EncodedKeySpec.DER编码是ASN.1编码规则中的一个子集&#xff0c;具体格式如何编排没有去了解&#xff0c;但最终呈现肯定的是一堆有规律的…

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

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

android java 退出程序_android开发两种退出程序方式(killProcess,System.exit)

KillProcess&#xff1a;在android中我们如果想要程序的进程结束可以这样写&#xff1a;android.os.Process.killProcess(android.os.Process.myPid());这样就可以从操作系统中结束掉当前程序的进程。注意&#xff1a;android中所有的activity都在主进程中&#xff0c;在Androi…

java 验证码_Java - 验证码 - 由Kaptcha组件实现

本文是基于SpringBoot整合Kaptcha验证码实现Kaptcha 是一个可高度配置的实用验证码生成工具&#xff0c;在项目开发中能够非常方便实现验证码先来看一个由 Kaptcha 制作的验证码效果图快速进入如何进行配置与实现的第1步&#xff1a;配置 Kaptcha 的依赖库com.github.penggle …

bat关闭java_通过bat脚本停止java进程的三个方法

通过bat脚本停止java进程的三个方法1.拷贝java.exe启动时复制java.exe程序修改进程映像名&#xff0c;然后通过映像名杀进程。栗子过程操作拷贝程序拷贝一份java.exe程序&#xff0c;重名为tstop.exe,放在PATH下。修改启动脚本echo offtitle tstoptstop -jar …/lib/laucher.ja…

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 序列化的作用用于把内存中的对象状态保存到一个文件中或者数据库中用于网…