DeviceUuidFactory【获取设备唯一标识码的UUID(加密)】【需要运行时权限的处理的配合】
版权声明:本文为HaiyuKing原创文章,转载请注明出处!
前言
有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码。虽然Android系统中提供了这样设备识别码,但是由于Android系统版本、厂商定制系统中的Bug等限制,稳定性和唯一性并不理想。而通过其他硬件信息标识也因为系统版本、手机硬件等限制存在不同程度的问题。
下面收集了一些“有能力”或“有一定能力”作为设备标识的串码。
DEVICE_ID
Sim Serial Number
ANDROID_ID
Installtion ID
详细介绍见参考资料。
因为需要用到android.permission.READ_PHONE_STATE权限,所以依赖《Android6.0运行时权限(基于RxPermission开源库)》。【20191124,Android Q机型上该权限对于普通APP已经无法申请,所以该权限对于普通APP来讲申请无用】
效果图
暂不需要
代码分析
以 DEVICE_ID为基础,在获取DEVICE_ID失败时以获取SimSerialNumber为备选方法,如果再失败,使用ANDROID_ID,如果再失败使用UUID的生成策略。
DEVICE_ID
String deviceId = ((TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE )).getDeviceId();
缺陷:
1、非手机设备: 如果只带有Wifi的设备或者音乐播放器没有通话的硬件功能的话(如平板电脑、电子书、电视、音乐播放器等)就没有这个DEVICE_ID
2、权限: 获取DEVICE_ID需要READ_PHONE_STATE权限,但如果我们只为了获取它,没有用到其他的通话功能,那这个权限有点大才小用
3、bug:在少数的一些手机设备上,该实现有漏洞,会返回垃圾,如:zeros或者asterisks的产品
Sim Serial Number
String SimSerialNumber = ((TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE )).getSimSerialNumber();
缺陷:
装有SIM卡的设备,可以获取到数值;对于CDMA设备,返回的是一个空值!
ANDROID_ID
String androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
缺陷:
厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID:9774d56d682e549c。
厂商定制系统的Bug:有些设备返回的值为null。
设备差异:对于CDMA设备,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
在Android <=2.1 or Android >=2.3的版本是可靠、稳定的,但在2.2的版本并不是100%可靠的
ANDROID_ID是设备第一次启动时产生和存储的64bit的一个数,当设备被wipe(刷机)后该数重置
Installtion ID
new Installation().id(context)
缺陷:
该方法无需访问设备的资源,也跟设备类型无关。
这种方式的原理是在程序安装后第一次运行时生成一个ID,该方式和设备唯一标识不一样,不同的应用程序会产生不同的ID,同一个程序重新安装也会不同。
所以这不是设备的唯一ID,但是可以保证每个用户的ID是不同的。可以说是用来标识每一份应用程序的唯一ID(即Installtion ID),可以用来跟踪应用的安装数量等。
使用步骤
一、项目组织结构图

注意事项:
1、 导入类文件后需要change包名以及重新import R文件路径
2、 Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖
二、导入步骤
将DeviceUuidFactory复制到项目中【20191124,Android Q适配】
package com.hxzk.som.base.utils; import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.UUID; /**
* Used 获取设备唯一标识码的UUID(加密)【需要运行时权限的处理的配合】
* 以 DEVICE_ID为基础,在获取DEVICE_ID失败时以获取SimSerialNumber为备选方法,如果再失败,使用ANDROID_ID,如果再失败使用UUID的生成策略
* @参考资料 获取Android设备唯一标识码:http://www.cnblogs.com/lvcha/p/3721091.html
* [Android] 获取Android设备的唯一识别码|设备号|序号|UUID:http://www.cnblogs.com/xiaowenji/archive/2011/01/11/1933087.html
* UUID(通用唯一标识符)http://blog.csdn.net/fanxiaobin577328725/article/details/51711062
*/
public class DeviceUuidFactory { //用于存储生成的设备的唯一标识码的UUID(加密)
protected static final String PREFS_FILE = "device_id.xml";
protected static final String PREFS_DEVICE_ID = "device_id"; /**
* 表示一个不变的通用唯一标识符(UUID)。以下是有关UUID的要点:
* 一个UUID表示一个128位的值。
* 它是用于创建随机文件名、Web应用程序的会话ID,事务ID等。
* UUID有四种不同的基本类型:(1)基于时间,(2)DCE安全性,(3)基于名称,(4)伪随机生成的UUID。*/
protected UUID uuid; public DeviceUuidFactory(Context context) {
if(uuid ==null ) {
synchronized (DeviceUuidFactory.class) {
if(uuid == null) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_FILE,Context.MODE_PRIVATE);
String oldId = prefs.getString(PREFS_DEVICE_ID, null); if (oldId != null) {//如果存在存储的唯一标识码的UUID值,则直接读取
uuid = UUID.fromString(oldId);
} else {
//Android Q,所以使用该方法20191124
if (Build.VERSION.SDK_INT >= 29) {
try {
uuid = UUID.nameUUIDFromBytes(getUUID().getBytes("utf8"));
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// Write the value out to the prefs file
prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString()).commit();
} else {
/*===============================================(1)获取DEVICE_ID===============================================================*/
/* 缺陷:
* 1、非手机设备: 如果只带有Wifi的设备或者音乐播放器没有通话的硬件功能的话(如平板电脑、电子书、电视、音乐播放器等)就没有这个DEVICE_ID
* 2、权限: 获取DEVICE_ID需要READ_PHONE_STATE权限,但如果我们只为了获取它,没有用到其他的通话功能,那这个权限有点大才小用
* 3、bug:在少数的一些手机设备上,该实现有漏洞,会返回垃圾,如:zeros或者asterisks的产品
*/
String deviceId = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();//根据不同的手机设备返回IMEI,MEID或者ESN码
Log.w("DeviceUuidFactory", "deviceId=" + deviceId);
try {
if (deviceId != null) {
uuid = UUID.nameUUIDFromBytes(deviceId.getBytes("utf8"));//获取一个类型3(基于名称的),根据指定的字节数组的UUID。
} else {
/*===============================================(2)获取SimSerialNumber====================================================*/
/*缺陷:
* 装有SIM卡的设备,可以获取到数值;对于CDMA设备,返回的是一个空值!
* */
String SimSerialNumber = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getSimSerialNumber(); if (SimSerialNumber != null) {
uuid = UUID.nameUUIDFromBytes(SimSerialNumber.getBytes("utf8"));
} else {
/*===============================================(3)获取ANDROID_ID====================================================*/
/*缺陷:
* 厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID:9774d56d682e549c。
* 厂商定制系统的Bug:有些设备返回的值为null。
* 设备差异:对于CDMA设备,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
* 在Android <=2.1 or Android >=2.3的版本是可靠、稳定的,但在2.2的版本并不是100%可靠的
* ANDROID_ID是设备第一次启动时产生和存储的64bit的一个数,当设备被wipe(刷机)后该数重置
* */
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); if (!"9774d56d682e549c".equals(androidId)) {
uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));
} else {
/*===============================================(4)Installtion ID : UUID====================================================*/
/*该方法无需访问设备的资源,也跟设备类型无关。
* 这种方式的原理是在程序安装后第一次运行时生成一个ID,该方式和设备唯一标识不一样,不同的应用程序会产生不同的ID,同一个程序重新安装也会不同。
* 所以这不是设备的唯一ID,但是可以保证每个用户的ID是不同的。可以说是用来标识每一份应用程序的唯一ID(即Installtion ID),可以用来跟踪应用的安装数量等。
* */
uuid = UUID.nameUUIDFromBytes(new Installation().id(context).getBytes("utf8"));
}
}
}
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} // Write the value out to the prefs file
prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString()).commit();
}
}
}
}
}
} /**获取UUID*/
public UUID getUuid() {
return uuid;
} /**这种方式是通过在程序安装后第一次运行后生成一个ID实现的
* 但该方式跟设备唯一标识不一样,不同的应用程序会产生不同的ID,同一个程序重新安装也会不同。所以这不是设备的唯一ID,但是可以保证每个用户的ID是不同的。
* 因此经常用来标识在某个应用中的唯一ID(即Installtion ID),或者跟踪应用的安装数量。
* 很幸运的,Google Developer Blog提供了这样的一个框架:*/
class Installation {
private String sID = null;
private final String INSTALLATION = "INSTALLATION"; public synchronized String id(Context context) {
if (sID == null) {
File installation = new File(context.getFilesDir(), INSTALLATION);
try {
if (!installation.exists())
{
writeInstallationFile(installation);
}
sID = readInstallationFile(installation);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return sID;
} private String readInstallationFile(File installation) throws IOException {
RandomAccessFile f = new RandomAccessFile(installation, "r");
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new String(bytes);
} private void writeInstallationFile(File installation) throws IOException {
FileOutputStream out = new FileOutputStream(installation);
String id = UUID.randomUUID().toString();
out.write(id.getBytes());
out.close();
}
} public static String getUUID() { String serial = null; String m_szDevIDShort = "35" +
Build.BOARD.length() % 10 + Build.BRAND.length() % 10 + Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 + Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 + Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 + Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 + Build.TAGS.length() % 10 + Build.TYPE.length() % 10 + Build.USER.length() % 10; //13 位 try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
serial = android.os.Build.getSerial();
} else {
serial = Build.SERIAL;
}
//API>=9 使用serial号
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
} catch (Exception exception) {
//serial需要一个初始化
serial = "serial"; // 随便一个初始化
}
//使用硬件信息拼凑出来的15位号码
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}
}
DeviceUuidFactory.java
在AndroidManifest.xml中添加权限
<!-- ======================授权获取设备ANDROID_ID========================== -->
<!-- 访问电话状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
添加运行时权限的处理(本demo中采用的是修改targetSDKVersion=22)
三、使用方法
package com.why.project.deviceuuidfactorydemo; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log; import com.why.project.deviceuuidfactorydemo.utils.DeviceUuidFactory; public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); String deviceIdStr = new DeviceUuidFactory(this).getUuid().toString();
Log.w("MainActivity","deviceIdStr="+deviceIdStr);
}
}
打印日志如下:

混淆配置
无
参考资料
获取Android设备唯一标识码
http://www.cnblogs.com/lvcha/p/3721091.html
[Android] 获取Android设备的唯一识别码|设备号|序号|UUID
http://www.cnblogs.com/xiaowenji/archive/2011/01/11/1933087.html
UUID(通用唯一标识符)
http://blog.csdn.net/fanxiaobin577328725/article/details/51711062
项目demo下载地址
https://github.com/haiyuKing/DeviceUuidFactoryDemo
DeviceUuidFactory【获取设备唯一标识码的UUID(加密)】【需要运行时权限的处理的配合】的更多相关文章
- Android 获取设备唯一标识码
概述 有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码.虽然Android系统中提供了这样设备识别码,但是由于Android系统版本.厂商定制系统中的Bug等限制,稳定性和唯一 ...
- 获取Android设备唯一标识码
概述 有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码.虽然Android系统中提供了这样设备识别码,但是由于Android系统版本.厂商定制系统中的Bug等限制,稳定性和唯一 ...
- 获取设备唯一标识 uuid(采用第三方库SSKeychain)
SSKeyChain 下载链接: http://pan.baidu.com/s/1booV3VD 密码: ivdi /** * 获取设备唯一标识 uuid */ +(NSString*) uuid ...
- 【转】 android获取设备唯一标识完美解决方案
<p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; color: rgb(51, 51, 51); ...
- Android 手机上获取物理唯一标识码
唯一标识码这东西在网络应用中非常有用,例如检测是否重复注册之类的. import android.provider.Settings.Secure;private String android_id ...
- Android 手机上获取物理唯一标识码[转]
所有添加有谷歌账户的设备可以返回一个 ANDROID_ID 所有的CDMA设备对于 ANDROID_ID 和 TelephonyManager.getDeviceId() 返回相同的值(只要在设置时添 ...
- iOS获取设备唯一标识的8种方法
8种iOS获取设备唯一标识的方法,希望对大家有用. UDID UDID(Unique Device Identifier),iOS 设备的唯一识别码,是一个40位十六进制序列(越狱的设备通过某些工具可 ...
- ios开发——实用技术篇OC篇&获取设备唯一标识
获取设备唯一标识 WWDC 2013已经闭幕,IOS7 Beta随即发布,界面之难看无以言表...,简直就是山寨Android. 更让IOS程序猿悲催的是,设备唯一标识的MAC Address在IOS ...
- IOS7如何获取设备唯一标识
WWDC 2013已经闭幕,IOS7 Beta随即发布,界面之难看无以言表...,简直就是山寨Android. 更让IOS程序猿悲催的是,设备唯一标识的MAC Address在IOS7中也失效了. I ...
随机推荐
- bzoj 2821 作诗 分块
基本思路和蒲公英一样 还是预处理出每两个块间的答案 询问时暴力跑两边的贡献 #include<cstdio> #include<cstring> #include<ios ...
- 斜率优化入门学习+总结 Apio2011特别行动队&Apio2014序列分割&HZOI2008玩具装箱&ZJOI2007仓库建设&小P的牧场&防御准备&Sdoi2016征途
斜率优化: 额...这是篇7个题的题解... 首先说说斜率优化是个啥,额... f[i]=min(f[j]+xxxx(i,j)) ; 1<=j<i (O(n^2)暴力)这样一个式子,首 ...
- 查看Linux下系统资源占用常用命令(top、free、uptime)
本文介绍下,在linux中查看系统资源占用的三个命令:top.free.uptime,通过实例学习下它们的用法,有需要的朋友参考下 一,top命令 1.作用top命令用来显示执行中的程序进程,使用权限 ...
- Applet web端对文件的读取方式
转载:http://www.exam8.com/computer/Java/zonghe/200708/659876.html ---- 我们知道,在Java Applet中出于安全性考虑,Apple ...
- Github泄露扫描系统
Github leaked patrol Github leaked patrol为一款github泄露巡航工具: 提供了WEB管理端,后台数据库支持SQLITE3.MYSQL和POSTGRES 双引 ...
- appium----【已解决】【Mac】环境配置提示“Xcode Command Line Tools are NOT installed!"
报错问题提示截图如下: 报错原因 :根据给出的信息很明显可以看到是"Xcode Command Line Tools"此工具没有安装 解决措施: 打开终端直接执行:xcode-se ...
- Docker最全教程——Redis容器化以及排行榜实战(十三)
前言 容器教程的路还很长,笔者尽量根据实践来不断地完善.由于在编写的过程中还会有完善和补充,后续可能会以番外来补充. 接下来会分享TeamCity.树莓派等内容,节奏可能会有点跳脱. 另外,长沙.NE ...
- 改造MIP获得搜索青睐,轻松完成SEO
搜索引擎目标及页面排序方法 搜索引擎作为互联网流量的入口,承担着流量分发的职责.但排序成千上万的网页,决定哪些网页在第一页,是由网页本身的用户体验决定的.权重算法会从内容优质性,广告多少,加载速度等多 ...
- vue.js - 奇怪的 event 对象
好久都没有写点东西了, 前段时间工作搞得头大,真的就是一起加班到死了.废话不多说,写这篇文章是因为这次因为 event 对象闹了一个乌龙,以此总结一下. 一.event 对象 (一)事件的 event ...
- 前端笔记之Vue(二)组件&案例&props&计算属性
一.Vue组件(.vue文件) 组件 (Component) 是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素,Vue.js 的编译器 ...