You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使获取设备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制式的设备,获取IMEI
  • getMeid():针对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

火山引擎 最新活动