如何使获取设备IMEI的函数兼容所有API?求getDeviceId()替代方案
解决Android API≥26下getDeviceId()废弃的兼容问题
嘿,这个问题我之前做项目时也踩过坑!从Android 8.0(API 26)开始,getDeviceId()确实被官方废弃了,而且后续Android版本对设备标识的权限限制越来越严,得根据不同API级别和需求来调整你的函数。
核心替代方案说明
首先得明确:如果不是必须要IMEI(比如运营商相关业务),尽量用不需要敏感权限的替代方案,因为IMEI属于用户敏感信息,合规成本高,而且高版本系统已经限制普通应用获取了。
1. 官方推荐的IMEI/MEID替代(仅API 26-28可用,需权限)
API 26及以上,官方提供了更细分的方法:
getImei():针对GSM制式的设备,获取IMEIgetMeid():针对CDMA制式的设备,获取MEID
但注意:Android 10(API 29)及以上,普通应用即使申请了READ_PHONE_STATE权限也无法获取这些值,只有系统应用或拥有特殊权限的应用才能拿到。
2. 无权限的通用设备标识
如果只是需要一个唯一标识来区分设备,推荐这两个方案:
Settings.Secure.ANDROID_ID:系统生成的64位字符串,不需要任何权限,但设备重置、刷机后会改变,部分厂商设备可能存在重复值。- 自定义UUID:自己生成一个UUID并存储在
SharedPreferences里,只要用户不卸载应用,这个标识就一直有效,完全不需要权限,是大多数场景的最优解。
修改后的兼容函数示例
下面是兼顾不同API级别和权限情况的完整函数:
import android.content.Context; import android.content.SharedPreferences; import android.os.Build; import android.provider.Settings; import android.telephony.TelephonyManager; import java.util.UUID; public String getDeviceIdentifier(Context context) { // 优先使用自定义UUID(无权限,稳定) SharedPreferences sp = context.getSharedPreferences("device_identifier", Context.MODE_PRIVATE); String customId = sp.getString("custom_device_id", null); if (customId != null) { return customId; } // 其次尝试ANDROID_ID String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); if (androidId != null && !androidId.isEmpty()) { return androidId; } // 最后尝试获取IMEI/MEID(仅低版本可用,需权限) TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (telephonyManager == null) { customId = generateAndSaveCustomId(sp); return customId; } try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+ 无法获取IMEI,返回自定义ID customId = generateAndSaveCustomId(sp); return customId; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // API 26-28 尝试获取IMEI/MEID String imei = telephonyManager.getImei(); if (imei != null) { return imei; } String meid = telephonyManager.getMeid(); if (meid != null) { return meid; } } else { // API <26 使用旧方法 String deviceId = telephonyManager.getDeviceId(); if (deviceId != null) { return deviceId; } } } catch (SecurityException e) { // 没有READ_PHONE_STATE权限,返回自定义ID e.printStackTrace(); } // 所有方案都失败,生成并保存自定义ID customId = generateAndSaveCustomId(sp); return customId; } private String generateAndSaveCustomId(SharedPreferences sp) { String customId = UUID.randomUUID().toString(); sp.edit().putString("custom_device_id", customId).apply(); return customId; }
关键注意事项
- 权限处理:如果要调用
getImei()/getMeid(),API 23及以上需要动态申请READ_PHONE_STATE权限,但API 29+申请了也没用,所以没必要在高版本系统里申请这个权限。 - 隐私合规:IMEI属于敏感个人信息,使用前必须遵守地区隐私法规(比如GDPR、国内的个人信息保护法),需要告知用户并获得明确同意。
- 多SIM卡场景:如果设备有多SIM卡,可以给
getImei()传入卡槽索引(比如getImei(0)获取第一张卡的IMEI),但同样受权限和系统版本限制。
内容的提问来源于stack exchange,提问作者Ciprian Ivan




