Android 设备唯一标识(适配Android版本)

相信在看这篇文章之前你已经看过一些类似的文章了,那么你肯定知道自己想要的是什么。


正文

===============================================================

首先要知道设备唯一标识的重要性,它可以做什么?

① 大数据统计,比如采集这个APP的安装量,那么一个唯一标识就代表一个Android设备

② 放置多设备重复登录,比如QQ、微信,你在A手机登录了,如果又到B手机上登录,这时候A手机就会下线。

③ 有一些APP的资源是每天限量免费的,它不需要你登录,但是你只能看几个,而且卸载重装也是一样的,次数不会刷新,这就是因为再后台添加了你的设备唯一标识。

④ 网络安全,比如银行类APP,第一次登录会麻烦一些,后面就比较的容易了。

而在实际开发中用的最多的就是防止重复登录了。

1. 唯一标识的含义


唯一标识简单来说就是一串符号(或者数字),映射现实中硬件设备。这些符号和设备是一一对应的,可称之为“唯一设备ID(Unique Device Identifier)”。这就是概念,也就是说你要拿到的唯一标识是独一无二的才行。

可惜的是Android平台并没有提供稳定的API来让我们获取到唯一设备ID。你可能要说IMEI和Mac地址可以获取到,但是它并不会适配Android的所有版本。在高版本中这个已经被弃用了,比如Android9.0、Android10.0、Android11.0。虽然现在Android11.0还没有正式投产,但是已经有Beta版本可以提供给开发者进行开发了,因此我们的应用如果要适配高版本就要另谋出路。

由于Android的碎片化很严重,而版本又很多,导致你要在获取设备唯一标识的同时还是兼容Android的各个版本,这一点就比较难受了,而我看网络上的一些文章,好像都是类似的内容,重复的排版,有的甚至是标题都不换,就跟粘贴复制的一样,故此自己写一篇,起码以后我在获取唯一标识的时候可以看看,就当是做个笔记了。

2. 新建项目


熟悉我写博客思路的读者会明白,通常我会重新建一个项目来演示文章的内容和细节,而不是简单的丢几行代码随便解释一下就完事,那样是不负责任的。那么下面新建一个项目,命名为OnlyPhoneID。如下图所示

在这里插入图片描述

3. 项目配置


这里需要对Android的以往版本进行适配,可以选取几个有代表性的版本,那就是Android5.0、Android6.0、Android8.0、Android10.0。为了掩饰方便我会下载对应版本的模拟器来测试。

下面先配置这个项目,在上面我说过IMEI在Android9.0时就被弃用了,说是弃用实际上是禁止第三方应用获取IMEI,这么一说,那它在Android9.0以下就是可以用的,那么在Android的1.0至8.0都是可以通过获取IMEI来作为唯一标识的。

而IMEI要获取需要在AndroidManifest.xml中注册静态权限。下面进行添加

<uses-permission android:name=“android.permission.READ_PRIVILEGED_PHONE_STATE”

tools:ignore=“ProtectedPermissions” />

我习惯了图文并茂。

在这里插入图片描述

因为我现在的项目编译版本比较高,我当前的目标版本是Android11.0,最低适配到Android5.0。Android的高版本会自动适配低版本。

在这里插入图片描述

4. Android 5.0


那么首先在Android5.0中来尝试获取IMEI。

修改一下activity_main.xml的布局代码:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

tools:context=“.MainActivity”>

<TextView

android:id=“@+id/tv_device_id”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_centerInParent=“true”

android:text=“Hello World!”

android:textColor=“#000”

android:textSize=“16sp” />

很简单的相对布局中放了一个用于显示设备id的文本控件。

然后进入到MainActivity,修改代码之后如下:

package com.llw.onlyphoneid;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import android.telecom.TelecomManager;

import android.telephony.TelephonyManager;

import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

private TextView tvDeviceId;

private TelephonyManager telephonyManager;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

tvDeviceId = findViewById(R.id.tv_device_id);

//获取系统电话服务

telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

//显示设备Id

tvDeviceId.setText(telephonyManager.getDeviceId());

}

}

在这里插入图片描述

看到图中画横线这个方法,你把鼠标放上去,它会说已经过时了,也就是弃用的意思,因为在build.gradle中当前的版本是Android11.0,而我之前说过,在Android9.0时就已经弃用了,使用过时的方法会很容易出问题,当然这个问题,你在可以使用的Android版本设备中运行是不会出现的。

下面运行一下:

在这里插入图片描述

可以看到在Android5.0上是可以正常获取到IMEI的。

刚才我是通过获取IMEI号,下面来试试获取序列号、设备序列号以及WIFI 模块的MAC地址。

下面修改一下activity_main.xml。

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

tools:context=“.MainActivity”>

<Button

android:id=“@+id/btn_get_imei”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:text=“获取IMEI” />

<Button

android:id=“@+id/btn_get_sn”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_below=“@+id/btn_get_imei”

android:text=“获取序列号” />

<Button

android:id=“@+id/btn_get_device_sn”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_below=“@+id/btn_get_sn”

android:text=“获取设备序列号” />

<TextView

android:id=“@+id/tv_device_id”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_centerInParent=“true”

android:text=“Hello World!”

android:textColor=“#000”

android:textSize=“16sp” />

<TextView

android:id=“@+id/tv_android_version”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_alignParentBottom=“true”

android:layout_centerHorizontal=“true”

android:layout_marginBottom=“20dp”

android:textColor=“#000”

android:textSize=“16sp” />

MainActivity

package com.llw.onlyphoneid;

import androidx.appcompat.app.AppCompatActivity;

import android.content.pm.PackageInfo;

import android.content.pm.PackageManager;

import android.net.wifi.WifiInfo;

import android.net.wifi.WifiManager;

import android.os.Build;

import android.os.Bundle;

import android.telecom.TelecomManager;

import android.telephony.TelephonyManager;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

/**

  • @author llw

*/

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

public static final String TAG = “MainActivity”;

private TextView tvDeviceId;

private TextView tvAndroidVersion;

private TelephonyManager telephonyManager;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initView();//初始化

}

/**

  • 初始化

*/

private void initView() {

tvDeviceId = findViewById(R.id.tv_device_id);

tvAndroidVersion = findViewById(R.id.tv_android_version);

Button btnGetIMEI = findViewById(R.id.btn_get_imei);

Button btnGetSN = findViewById(R.id.btn_get_sn);

Button btnGetDeviceSN = findViewById(R.id.btn_get_device_sn);

btnGetIMEI.setOnClickListener(this);

btnGetSN.setOnClickListener(this);

btnGetDeviceSN.setOnClickListener(this);

//获取系统电话服务

telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

Log.d(TAG,"Android " + android.os.Build.VERSION.RELEASE);

tvAndroidVersion.setText("Android " + android.os.Build.VERSION.RELEASE);

}

/**

  • 页面控件点击事件

  • @param v

*/

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_get_imei://获取IMEI

//显示设备Id

Log.d(TAG, "IMEI: " + telephonyManager.getDeviceId());

tvDeviceId.setText(telephonyManager.getDeviceId());

break;

case R.id.btn_get_sn://获取序列号

Log.d(TAG, "序列号: " + telephonyManager.getSimSerialNumber());

tvDeviceId.setText(telephonyManager.getSimSerialNumber());

break;

case R.id.btn_get_device_sn://获取设备序列号

Log.d(TAG, "设备序列号: " + Build.SERIAL);

tvDeviceId.setText(Build.SERIAL);

break;

default:

break;

}

}

}

运行之后,三个按钮分别点击一下。

在这里插入图片描述

OK,下面在6.0中运行试一下。

5. Android 6.0


Android6.0推出了动态权限,规定危险权限需要动态申请,而用户需要通过才可以使用。

下面修改一下app的build.gradle。

android闭包下

compileOptions {//指定使用的JDK1.8

sourceCompatibility = 1.8

targetCompatibility = 1.8

}

dependencies闭包下

//权限

implementation ‘com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar’

implementation ‘io.reactivex.rxjava2:rxandroid:2.0.2’

implementation “io.reactivex.rxjava2:rxjava:2.0.0”

在这里插入图片描述

然后点击Sync同步一下。

同步好了之后回到MainActiivty,修改一下代码。

/**

  • 初始化

*/

private void initView() {

tvDeviceId = findViewById(R.id.tv_device_id);

tvAndroidVersion = findViewById(R.id.tv_android_version);

Button btnGetIMEI = findViewById(R.id.btn_get_imei);

Button btnGetSN = findViewById(R.id.btn_get_sn);

Button btnGetDeviceSN = findViewById(R.id.btn_get_device_sn);

btnGetIMEI.setOnClickListener(this);

btnGetSN.setOnClickListener(this);

btnGetDeviceSN.setOnClickListener(this);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

//Android6.0以上,请求动态权限

RxPermissions rxPermissions = new RxPermissions(this);

rxPermissions.request(Manifest.permission.READ_PHONE_STATE)

.subscribe(granted -> {

if (granted) {

//获取系统电话服务

telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

} else {

Toast.makeText(this,“权限未通过”,Toast.LENGTH_SHORT).show();

}

});

} else {

//获取系统电话服务

telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

}

Log.d(TAG, "Android " + android.os.Build.VERSION.RELEASE);

tvAndroidVersion.setText("Android " + android.os.Build.VERSION.RELEASE);

}

实际上只要修改一下initView中对于Android版本的判断即可。当用户通过权限之后你点击获取IMEI就可以获取到。否则程序ANR。

下面运行在Android6.0的模拟器上面,

在这里插入图片描述

点击ALLOW,然后三个按钮都点一下:

在这里插入图片描述

然后你会发现一个问题,那就是Android5.0和6.0打印的内容,除了版本不一样,其他的都一样,这是为什么?这是因为虚拟机是不存在的,所以Google就给你重复的数据,你想要真正获取到不一样的标识,还是要通过真机来操作,如果你不信的话,可以用自己电脑上的虚拟机试试,说不定你得到的数据和我这里也是一模一样的。不过我已经采购了两台低版本的Android手机,分别是5.0和6.0的,到时候我还是要用真机来试试。

下面用Android8.0来进行运行

6. Android 8.0


其实Android8.0的在获取唯一标识这个方面的变化不大,所以你都不需要做什么改动,你可以直接运行刚才的代码到8.0的虚拟机上面。

在这里插入图片描述

各个按钮都点一下,你会发现和Android5.0、6.0是一样的。

在这里插入图片描述

不过不用担心,这是在虚拟机上面,真机上不会这样的。

7. Android 10.0


结尾

最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。

首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)

泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

接下来是资料清单:(敲黑板!!!


1.数据结构和算法

2.设计模式

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
ity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)

代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

[外链图片转存中…(img-wldWaORn-1725669583162)]

接下来是资料清单:(敲黑板!!!


1.数据结构和算法

[外链图片转存中…(img-POfXp3hw-1725669583163)]

2.设计模式

[外链图片转存中…(img-Ncj9pSzZ-1725669583164)]

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

[外链图片转存中…(img-oPFDipsU-1725669583164)]

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

[外链图片转存中…(img-HeJ73wS7-1725669583165)]

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中…(img-3LpnoGtE-1725669583165)]

加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值