在最近的开发工作中,有个功能点需要获取设备标识作为用户ID,理想的标识应该是不可重置的。然而,在Android系统版本的不断迭代中,Google对用户隐私的保护力度持续加强,针对获取设备标识方法的限制也越来越严格,官方建议尽量选用用户可重置的唯一标识,例如AAID。
为了尽量满足需求,对以往常见的四种获取设备标识的方法进行了测试,本文对测试结果进行记录。
获取设备标识
DeviceID(IMEI、MEID)
稳定的标识符,即使恢复出厂设置也不会被重置。在不同版本的Android设备上,获取DeviceID的方法及效果略有不同,下面分别介绍一下。
API 23-28
在AndroidManifest
中配置READ_PHONE_STATE
权限,代码如下:
"1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
......
>
......
application>
manifest>
在API 23(Android6.0)到API 28(Android 9.0)的设备上,可以通过TelephonyManager.getDeviceId()
方法获取DeviceID。
另外在API 26(Android8.0)或更高版本的设备上,还可以通过TelephonyManager.getImei()
或TelephonyManager.getMeid()
获取设备标识。需要在运行时向用户申请授予READ_PHONE_STATE
权限。示例代码如下:
class DeviceIdExampleActivity : AppCompatActivity() {
private lateinit var binding: LayoutDeviceIdExampleActivityBinding
private val deviceIdPermissionRequestLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission()) {
getDeviceID()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LayoutDeviceIdExampleActivityBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
binding.btnGetDeviceId.setOnClickListener {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
deviceIdPermissionRequestLauncher.launch(Manifest.permission.READ_PHONE_STATE)
} else {
getDeviceID()
}
}
}
private fun getDeviceID() {
val telephonyManager = getSystemService(TelephonyManager::class.java)
val deviceIDStrBuilder = StringBuilder()
try {
deviceIDStrBuilder.append("deviceId:").append(telephonyManager.deviceId)
} catch (e: Exception) {
e.printStackTrace()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
deviceIDStrBuilder.append("nIMEI:").append(telephonyManager.imei)
} catch (e: Exception) {
e.printStackTrace()
}
try {
deviceIDStrBuilder.append("nMEID:").append(telephonyManager.meid)
} catch (e: Exception) {
e.printStackTrace()
}
}
binding.tvResultValue.text = deviceIDStrBuilder
}
}
测试设备为Android Studio模拟器,API为28,测试效果如下:
API 29以上
从API 29(Android10.0)开始,即使用户授予READ_PHONE_STATE
权限,上述三种方法返回值均为空字符串。
测试设备为Android Studio模拟器,API为29,测试效果如下:
在API 29以上也有仍能获取Device ID的方法,需要与运营商或者手机厂商有深度合作,具体可以参考官方文档
ANDROID_ID
设备首次启动时生成,恢复出厂设置时会被重置,无需任何权限即可获取。获取代码如下:
class DeviceIdExampleActivity : AppCompatActivity() {
private lateinit var binding: LayoutDeviceIdExampleActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LayoutDeviceIdExampleActivityBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
binding.btnGetAndroidId.setOnClickListener {
binding.tvResultValue.text = "ANDROID_ID:${Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)}"
}
}
}
测试设备为Pixel 7,API为34,测试效果如下:
需要注意的是,根据查询到的资料来看,不同厂商的设备可能会出现返回空值或者返回值相同的情况。另外,ANDROID_ID可以手动进行修改
Serial(序列号)
设备硬件序列号,通常在没有root的情况下不会重置,在不同版本的Android设备上,获取Serial的方法及效果略有不同,下面分别介绍一下。
API 23-25
在API 23-25版本的设备上无需配置权限,获取代码如下:
class DeviceIdExampleActivity : AppCompatActivity() {
private lateinit var binding: LayoutDeviceIdExampleActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LayoutDeviceIdExampleActivityBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
binding.btnGetDeviceSerial.setOnClickListener {
try {
binding.tvResultValue.text = "deviceSerial:${Build.SERIAL}"
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
测试设备为Android Studio模拟器,API为25,测试效果如下:
API 26-28
从API 26(Android8.0)开始,android.os.Build.SERIAL
总是返回UNKNOWN,改为使用android.os.Build.getSerial()
方法获取,另外还需配置READ_PHONE_STATE
权限并且在运行时动态申请用户授权。示例代码如下:
class DeviceIdExampleActivity : AppCompatActivity() {
private lateinit var binding: LayoutDeviceIdExampleActivityBinding
private val serialPermissionRequestLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission()) {
getSerial()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LayoutDeviceIdExampleActivityBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
binding.btnGetDeviceSerial.setOnClickListener {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
serialPermissionRequestLauncher.launch(Manifest.permission.READ_PHONE_STATE)
} else {
getSerial()
}
}
}
private fun getSerial() {
val serialStrBuilder = StringBuilder()
try {
serialStrBuilder.append("Build.SERIAL:").append(Build.SERIAL)
} catch (e: Exception) {
e.printStackTrace()
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
serialStrBuilder.append("nBuild.getSerial():").append(Build.getSerial())
}
} catch (e: Exception) {
e.printStackTrace()
}
binding.tvResultValue.text = serialStrBuilder
}
}
测试设备为Android Studio模拟器,API为28,测试效果如下:
API 29以上
从API 29(Android10.0)开始,获取Serial和获取DeviceID一样,文中列举的方法无法正常获取,但官方也提供了相应的解决方法。
测试设备为Android Studio模拟器,API为29,测试效果如下:
MAC地址
用于标识网络接口的硬件地址,通常在设备出厂时分配。有root权限时可以进行修改,因此存在重复的可能性。在不同版本的Android设备上,获取MAC地址的效果略有不同,下面分别介绍一下。
API 23-28
使用NetworkInterface.getNetworkInterfaces()
方法来获取MAC地址,无需任何权限,示例代码如下:
class DeviceIdExampleActivity : AppCompatActivity() {
private lateinit var binding: LayoutDeviceIdExampleActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LayoutDeviceIdExampleActivityBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
binding.btnGetMacAddress.setOnClickListener {
val networkInterfaces = NetworkInterface.getNetworkInterfaces()
while (networkInterfaces.hasMoreElements()) {
val network = networkInterfaces.nextElement()
if (network.name == "wlan0") {
binding.tvResultValue.text = "macAddress:${network.hardwareAddress?.joinToString(":") { macAddress -> String.format("%02X", macAddress) }}"
break
}
}
}
}
}
测试设备为samsung Galaxy S8,API为28,测试效果如下:
API 29
获取代码和上面一致,但是从API 29(Android10.0)开始,连接不同的WI-FI时会获取到不同的MAC地址。
测试设备为samsung Galaxy S9,API为29,测试效果如下:
WI-FI | 热点 |
---|---|
API 30以上
从API 30(Android11.0)开始,已无法获取到MAC地址。
测试设备为Pixel 7,API为34,测试效果如下:
小结
文中测试的四种设备标识,确定不会重复的应该只有DeviceId和MAC地址,但是二者从API 29开始也都不可靠了。当然,在市面上仍然有不少API 29以下的设备,在选择唯一标识方案时,低版本的设备仍然可以采用DeviceID或MAC地址,高版本则采用官方推荐的AAID等方案。
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22112,转载请注明出处。
评论0