Android 获取 唯一GUID ,替换 IMEI (兼容 Android 10+获取IMEI问题)
Posted Tobey_r1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 获取 唯一GUID ,替换 IMEI (兼容 Android 10+获取IMEI问题)相关的知识,希望对你有一定的参考价值。
android 获取 唯一UUID ,替换 IMEI (兼容 Android 10+获取IMEI问题)
背景
谷歌在Android 10(API 级别 29)对不可重置的标识符(包括 IMEI 和序列号)添加了限制,导致我们无法获取到对应的IMEI值,不过给了我们其他方法就是获取GUID来作为唯一表示:
关于
最近项目上需求需要在用户登录的时候获取手机设备唯一标识,原来的代码里面有获取的方法:
手机调试版本 OPPO Android 11
/**
* 获取系统sn
*
* @return
*/
public static String getSn(Context context) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
return "";
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return Build.getSerial();
//也有用到ANDROID_ID,但是因为会变所以不合适,如果不是特别要求的话你们可以使用下面这个,这样就不用继续往下看去适配了
//Settings.System.getString( App.getInstance().getContentResolver(), Settings.Secure.ANDROID_ID);
} else {
String sn = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
sn = (String) get.invoke(c, "ro.serialno");
} catch (Exception e) {
e.printStackTrace();
}
return sn;
}
}
上面那段代码不会奔溃报错,但是返回的sn值在Android Q(10)+为null,即我们获取不到值,在谷歌开发者平台上给了我们适配方法,就是通过获取32位GUID来作为唯一表示,因为GUID在当前时间点是唯一的,所以可以作为标记设备唯一:
就是通过String u = UUID.randomUUID().toString();
来获取,当然了获取之后才是重点,因为这个UUID是根据一些时间等逻辑进行实时生成的,所以我们需要对它进行保存,以便作为唯一标识。
保存GUID方案
- 第一种,使用SharedPreferemces存储(缺点会随着app卸载移除)
- 第二种.使用android自带sqlite数据库存储(这里我用的郭老师的litepal存储,但是后面发现也会随着app卸载移除)
- 第三种,以外部存储方式存储文件(不存在app私有目录下)(缺点文件删除,UUID随之移除)
第一种,SharedPreferemces存储
存储uuid
// 通过判断isFirst的值来进行第一次uuid的存储,后续再打开app都不会刷新这个存储的uuid的值,app卸载或者重新安装会被重置
boolean isFirst;
SharedPreferences sharedPref = App.getInstance().getSharedPreferences(
"myuuid", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
isFirst = sharedPref.getBoolean("isFirst",true);
if (isFirst){
String u = UUID.randomUUID().toString();
//这里我将 5a5fbf50-b063-425b-88bb-fd04095d92f8 替换成 5a5fbf50b063425b88bbfd04095d92f8(32位)
u = u.replace("-","");
editor.putString("uuid",u).apply();
editor.putBoolean("isFirst",false).apply();
}
取出uuid:
/**
* 获取系统sn
*
* @return
*/
public static String getSn(Context context) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
return "";
}
if (Build.VERSION.SDK_INT >= 29) {
String u = UUID.randomUUID().toString();
u = u.replace("-","");
SharedPreferences sharedPref = App.getInstance().getSharedPreferences(
"myuuid", Context.MODE_PRIVATE);
String uuid = sharedPref.getString("uuid",u);
return uuid;
}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return Build.getSerial();
} else {
String sn = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
sn = (String) get.invoke(c, "ro.serialno");
} catch (Exception e) {
e.printStackTrace();
}
return sn;
}
}
第二种,sqlite数据库存储
配置参考Android简单使用Litepal 数据库学习笔记(Android与数据库 (一)),我这边直接上步骤,不做细致解释了:
添加build引用:
implementation 'org.litepal.android:java:3.0.0'
然后在个人的app下初始化:
LitePal.initialize(this);
新增一个UuidString.java
类:
public class UuidString extends LitePalSupport {
private String uuid;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
}
在src/main下新建一个assets文件夹,在文件夹下新建一个litepal.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<litepal>
<dbname value="uuiddb" ></dbname>
<version value="1"></version>
<list>
<mapping class="com.xxxxxx.config.UuidString"></mapping>
</list>
</litepal>
在登录界面添加如下:
String u = UUID.randomUUID().toString();
u = u.replace("-","");
LitePal.getDatabase();
//查询对应表第一条数据
UuidString uuid = LitePal.findFirst(UuidString.class);
if (uuid == null){
UuidString uuidString = new UuidString();
uuidString.setUuid(u);
uuidString.save();
}
然后再获取sn的方法里面添加如下:
UuidString uui2 = LitePal.findFirst(UuidString.class);
return uui2.getUuid();
第三种,新增文件方式存储(还是逃不过啊)
我这里没有将uuid写到text文本里,直接将他赋值命名这个text文本了,这就省去了读取流的操作,当然了需要读写权限,权限申请这里就不贴代码了:
思路首先我们新建一个文件夹在/storage/emulated/0/下面,免得App卸载也会删除对应文件夹,然后我们还要对这个文件夹/storage/emulated/0/netLog/下面进行是否存在文件的判断,有文件说明我们第一次运行程序的时候已经生成了唯一UUID的text文本,我们只需要取出来使用即可,没有那就新增一个UUID的文本即可,下面是详细步骤:
首先新增(为了代码看起来简洁)一个静态方法帮助类StaticMethodUtil.java
public class StaticMethodUtil {
/**
* 获取文件
*
* @return 文件
*/
public static String getLogFile(String uuid) {
File file;
//因为适用版本就是Android10+的,所以不必担心这里报错
file = new File(Environment.getExternalStorageDirectory()+"/netLog");
// 若目录不存在则创建目录
if (!file.exists()) {
file.mkdir();
}
if (!hasFile(file.getPath())){
File logFile = new File(file.getPath()+"/" +uuid+ ".txt");
if (!logFile.exists()) {
try {
logFile.createNewFile();
} catch (Exception e) {
Log.e("文件帮助类!", "Create log file failure !!! " + e.toString());
}
}
return logFile.getName();
}else {
return getFileName(file.getPath(), ".txt").get(0);
}
}
//判断文件夹下是否含有文件
public static boolean hasFile(String fileAbsolutePaht) {
File file = new File(fileAbsolutePaht);
return file.list().length==0?false:true;
}
}
修改获取sn的方法代码(一定要注意读写权限):
/**
* 获取系统sn
*
* @return
*/
public static String getSn(Context context) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
return "";
}
if (Build.VERSION.SDK_INT >= 29) {
String u = UUID.randomUUID().toString();
u = u.replace("-","");
return StaticMethodUtil.getLogFile(u).replace(".txt","");
}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return Build.getSerial();
} else {
String sn = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
sn = (String) get.invoke(c, "ro.serialno");
} catch (Exception e) {
e.printStackTrace();
}
return sn;
}
}
看下效果图(文件夹):
到此本篇文章结束,有问题欢迎批评指正,觉得不错的也请点个赞哦
以上是关于Android 获取 唯一GUID ,替换 IMEI (兼容 Android 10+获取IMEI问题)的主要内容,如果未能解决你的问题,请参考以下文章