android 10+(api 29起)和ios 7+均系统级禁用imei获取,plus.device.imei等调用返回空、伪码或抛异常;合规替代方案为oaid→android_id→本地uuid三级兜底。
android 10+(api 29 起)普通应用已无法获取真实 imei,调用 plus.device.imei 或 getimei 会返回空字符串、"000000000000000",或直接抛出 securityexception;ios 全系自 ios 7 起就彻底移除了公开访问 imei 的能力。别再试了,这不是你代码的问题,是系统级封禁。
为什么 plus.device.imei 在真机上总是 undefined 或 ""
这是 Android 系统底层拦截的结果:从 API 29 开始,即使你在 manifest.json 中声明了 android.permission.READ_PHONE_STATE,也完成了动态申请,系统仍会静默屏蔽硬件 IMEI 读取路径。HBuilderX 自带的 plus.device.getInfo().imei 同样走原生接口,一样被拦。模拟器或 Android 9 及以下设备可能返回值,但上线不可依赖。
- 双卡机型调用
getImei(0)/getImei(1)行为不一致,部分返回null - targetSdkVersion ≥ 29 的 APK 若在 Google Play 发布,声明该权限会被直接拒审
- 华为、小米等厂商 ROM 还可能额外返回固定伪码(如
"000000000000000"),无法区分真假
uni.getSystemInfoSync() 里根本找不到 IMEI 字段
uni.getSystemInfoSync() 只返回只读系统信息(如 model、platform、system),不涉及任何需权限的设备标识。IMEI 属于敏感硬件信息,Android 6.0 起就需要运行时权限,10+ 直接移除 API 接口;iOS 更早在系统层剥离,框架层压根没暴露入口。
- 试图从
res.deviceId或res.imei取值,结果一定是undefined - 不要封装 Native.js 去硬调
TelephonyManager.getImei()—— 它在 API 29+ 上会直接 crash - 某些“绕过”方案(如反射调用)在主流厂商新系统(EMUI 12+/MIUI 14+)中已被加固拦截
替代方案必须按优先级链式 fallback
合规且实用的做法不是死磕 IMEI,而是分层兜底:OAID → android_id → 本地持久化 UUID。三者覆盖不同场景和限制,缺一不可。
-
首选 OAID:调用
uni.getOAID()(需先安装uni-plugin-oaid插件,并在 HBuilderX 中使用「启用 OAID 支持」的自定义基座);注意首次调用有 200–500ms 延迟,务必在onLaunch预热并缓存 -
次选 android_id:用 Native.js 调用
Settings.Secure.getString(cr, "android_id"),兼容 Android 5.0+,无需权限,卸载重装不变;但恢复出厂或刷机会重置,vivo/FuntouchOS 某些版本可能返回null -
兜底 UUID:生成 v4 格式字符串(非
Math.random()拼接),存入uni.setStorageSync('device_id');清除 App 数据后会重置,如需长期绑定,必须和服务端设备表联动
真正容易被忽略的是:OAID 不是开箱即用的,它依赖厂商 SDK 初始化完成才可用;而本地 UUID 的生成逻辑一旦写错(比如用了 Date.now() + Math.random()),碰撞概率会在万级设备量下明显上升。别省那几行代码,老实用标准 UUID v4 模板。











