3096

Android设备有唯一的ID吗?如果有,用Java访问它的简单方法是什么?

1

54个答案54

重置为默认值
2251

设置。安全#ANDROID_ID返回Android ID作为对每个用户都是唯一的64位十六进制字符串。

导入android.provider。设置。安全;private String android_id=安全.getString(getContext().getContentResolver(),安全。安卓ID_ID);

另请阅读唯一标识符的最佳实践:https://developer.android.com/training/articles/user-data-ids

12
1193

更新:对于Android的最新版本安卓ID_ID已经得到解决,我认为不再需要这种方法。请看一下安东尼的回答.

全面披露:我的应用程序最初使用了以下方法,但现在不再使用此方法,我们现在使用的是Android开发者博客那个条目emmby的回答链接到(即生成和保存UUID#randomUUID()).


这个问题有很多答案,其中大多数只在“一段时间”内有效,不幸的是,这还不够好。

根据我对设备的测试(所有手机,其中至少一部未激活):

  1. 所有测试的设备都返回了的值电话管理器.getDeviceId()
  2. 所有GSM设备(均使用SIM卡测试)都返回了电话管理器.getSimSerialNumber()
  3. 的所有CDMA设备均返回null获取SimSerialNumber()(如预期)
  4. 添加了Google帐户的所有设备都返回了的值安卓ID_ID
  5. 所有CDMA设备都返回了相同的值(或相同值的派生)安卓ID_ID电话管理器.getDeviceId()--只要安装过程中添加了一个谷歌帐户。
  6. 我还没有机会测试没有SIM卡的GSM设备、没有添加Google帐户的GSM设备,或者任何处于飞行模式的设备。

因此,如果您想要设备本身的独特功能,TM.getDeviceId() 应该足够了。显然,一些用户比其他用户更偏执,因此散列1个或多个这些标识符可能会很有用,这样字符串对于设备来说仍然是唯一的,但不会明确标识用户的实际设备。例如,使用String.hashCode(),结合UUID:

final TelephonyManager tm=(电话管理器)getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);最终字符串tmDevice,tmSerial,androidId;tmDevice=“”+tm.getDeviceId();tmSerial=“”+tm.getSimSerialNumber();androidId=“”+android.provider。设置。Secure.getString(getContentResolver(),android.provider。设置。安全。安卓ID_ID);UUID设备UUID=新的UUID(androidId.hashCode(),(长)tmDevice.hashCode()<<32)|tmSerial.hashCode[)];String设备Id=设备Uuid.toString();

可能会导致如下情况:00000000-54b3-e7c7-0000-000046bffd97

它对我来说足够好了。

正如Richard在下面提到的,不要忘记您需要获得许可才能阅读电话管理器属性,因此将其添加到清单中:

<uses-permission android:name=“android.permission.READ_PHONE_STATE”/>

导入库

导入android.content。语境;导入android.telephony。电话经理;导入android.view。视图;
12
  • 162
    基于电话的ID不会出现在平板设备上,不是吗? 评论 2010年6月23日14:27
  • 25
    因此,为什么我说大多数都不会一直工作:)我还没有找到对所有设备、所有设备类型和所有硬件配置都可靠的答案。这就是为什么这个问题要从这里开始。很明显,对于这个问题,没有最终的解决方案。个别设备制造商可能有设备序列号,但这些序列号不供我们使用,也不是必须的。因此,我们只剩下可用的了。
    – 
    评论 2010年6月29日19:40
  • 33
    代码示例工作得很好。记住添加<uses-permission android:name=“android.permission.READ_PHONE_STATE”/>到清单文件。如果存储在数据库中,则返回的字符串长度为36个字符。
    – 理查德
    评论 2011年2月27日22:28
  • 12
    请注意,此解决方案存在巨大的局限性:android-developers.blogspot.com/2011/03/… 评论 2011年4月7日20:09
  • 22
    @softarn:我相信你指的是emmby已经链接到的Android开发者博客,它解释了你是什么尝试可以这么说,也许你应该投票支持他的评论。不管怎样,正如emmby在回答中提到的那样,即使博客信息也存在问题。这个问题要求一个独特的设备标识符(不是安装标识符),所以我不同意您的说法。博客假设你想要什么不一定是跟踪设备,而问题只是要求这样。我同意博客的其他观点。
    – 
    评论 2011年7月22日0:45
473

#上次更新时间:6/2/15


在阅读了每一篇关于创建唯一ID的Stack Overflow帖子、谷歌开发者博客和Android文档之后,我觉得“伪ID”似乎是最好的选择。

主要问题:硬件与软件

硬件

  • 用户可以更改他们的硬件、Android平板电脑或手机,因此基于硬件的唯一ID对于跟踪用户
  • 对于跟踪硬件,这是个好主意

软件

  • 如果用户是root用户,则可以擦除/更改其ROM
  • 您可以跨平台(iOS、Android、Windows和Web)跟踪用户
  • 最好的愿望追踪个人用户和他们的同意就是让他们登录(使用OAuth无缝登录)

#Android的总体细分

###-保证API的唯一性(包括根设备)>=9/10(99.5%的Android设备)###-没有额外权限

Psuedo代码:

如果API>=9/10:(99.5%的设备)返回包含串行ID的唯一ID(根设备可能不同)其他的返回生成信息的唯一ID(可能与数据重叠-API<9)

感谢@stansult发布我们所有的选择(在这个堆栈溢出问题中)。

##选项列表-为什么/为什么不使用它们:

  • 用户电子邮件-软件

  • 用户可以更改电子邮件-极不可能

  • API 5标准+<使用权限android:name=“android.permission.GET_ACCOUNTS”/>

  • API 14标准+<uses-permission android:name=“android.permission.READ_PROFILE”/> <uses-permission android:name=“android.permission.READ_CONTACTS”/>(如何获取Android设备的主要电子邮件地址)

  • 用户电话号码-软件

  • 用户可以更改电话号码-极不可能

  • <uses-permission android:name=“android.permission.READ_PHONE_STATE”/>

  • IMEI-硬件(仅手机,需要android权限。阅读_语音_状态)

  • 大多数用户都讨厌权限中显示“Phone Calls”。一些用户给出了糟糕的评级,因为他们认为您只是在窃取他们的个人信息,而您真正想做的只是跟踪设备的安装。很明显,您正在收集数据。

  • <uses-permission android:name=“android.permission.READ_PHONE_STATE”/>

  • Android ID-硬件(可以为空,可以在出厂重置时更改,可以在根设备上更改)

  • 因为它可以是“null”,所以我们可以检查“null“并更改其值,但这意味着它将不再是唯一的。

  • 如果用户具有出厂重置设备,则根设备上的值可能已更改或更改,因此如果您正在跟踪用户安装,则可能会有重复条目。

  • WLAN MAC地址-硬件(需要android权限。访问_WIFI_STATE)

  • 这可能是第二个最佳选择,但您仍在收集和存储直接来自用户的唯一标识符。很明显,您正在收集数据。

  • <uses-permission android:name=“android.permission.ACCESS_WIFI_STATE”/>

  • 蓝牙MAC地址-硬件(具有蓝牙功能的设备,需要android权限。蓝牙)

  • 市场上的大多数应用程序都不使用蓝牙,因此如果您的应用程序不使用蓝牙并且您包含了蓝牙,用户可能会产生怀疑。

  • <uses-permission android:name=“android.permission.BLUETOOTH”/>

  • 伪Unique ID-软件(适用于所有Android设备)

  • 很可能,可能包含冲突-请参阅下面发布的我的方法!

  • 这允许您从用户那里获得一个“几乎唯一”的ID,而无需获取任何私密信息。您可以根据设备信息创建自己的匿名ID。


我知道没有任何“完美”的方法可以在不使用权限的情况下获得唯一的ID;然而,有时我们只需要跟踪设备安装。在创建唯一ID时,我们可以仅根据Android API提供的信息创建“伪唯一ID”,而无需使用额外的权限。这样,我们可以显示用户的尊重,并尝试提供良好的用户体验。

使用伪唯一id,您实际上只会遇到这样一个事实,即基于存在类似设备的事实,可能存在重复。您可以调整组合方法,使其更加独特;然而,一些开发人员需要跟踪设备安装,这将基于类似的设备实现技巧或性能。

##API>=9:

如果他们的Android设备是API9或更高版本,那么由于“Build”,这保证是唯一的。“串行”字段。

记得,从技术上讲,你只错过了大约0.5%的用户API<9所以你可以专注于剩下的部分:这是99.5%的用户!

##API<9:

如果用户的Android设备低于API 9;希望他们没有进行出厂重置和“安全”。ANDROID_ID将被保留或不为“null”。(请参见http://developer.android.com/about/dashboards/index.html)

##如果所有其他方法都失败了:

如果所有其他操作都失败,如果用户的API低于9(低于姜饼),则已重置设备,或“安全”。ANDROID_ID“返回”null“,则返回的ID将完全基于其ANDROID设备信息。这就是碰撞可能发生的地方。

变化:

  • 删除了“Android”。“SECURE_ID”,因为工厂重置可能导致值更改
  • 编辑代码以更改API
  • 更改了伪

请查看以下方法:

/***返回伪唯一ID*@返回ID*/公共静态字符串getUniquePsuedoID(){//如果所有其他都失败了,如果用户的API 9(更低//(比姜饼),已重置其设备或“安全”。安卓ID_ID’//返回“null”,则返回的ID将完全基于//关闭他们的Android设备信息。这就是碰撞的地方//可能发生。//谢谢http://www.pocketmagic.net/?p=1662!//尽量不要使用DISPLAY、HOST或ID-这些项目可能会发生变化。//如果存在冲突,将存在重叠数据String m_szDevIDShort=“35”+(Build.BARDE.length()%10)+(Build.BRAND.length()%10)+(Build.CPU_ABI.length()%10)+(Build.DEVICE.length()%10)+(Build.MANUFACTURER.length()%10)+(Build.MODEL.length()%10)+(Build.PRODUCT.length()%10);//感谢@Roman SL!// https://stackoverflow.com/a/4789483/950427//只有API>=9的设备才具有android.os。生成。串行的// http://developer.android.com/reference/android/os/Build.html#SERIAL//如果用户升级软件或根目录他们的设备,将有重复条目字符串序列=空;尝试{serial=android.os。Build.class.getField(“SERIAL”).get(null).toString();//继续并返回api=>9的序列return新的UUID(m_szDevIDShort.hashCode(),serial.hashCode()).toString();}catch(异常异常){//字符串需要初始化serial=“serial”;//一些价值}//谢谢@Joe!// https://stackoverflow.com/a/2853253/950427//最后,通过使用UUID类组合我们找到的值来创建唯一标识符return新的UUID(m_szDevIDShort.hashCode(),serial.hashCode()).toString();}

#新增(针对带有广告和Google Play Services的应用程序):

从Google Play Developer的控制台:

从2014年8月1日开始,谷歌游戏开发者计划政策需要全新的应用程序上传和更新才能在中使用广告ID代替任何其他用于广告目的的永久标识符。了解更多信息

实施:

许可:

<uses-permission android:name=“android.permission.INTERNET”/>

代码:

导入com.google.android.gms.ads.identifier。广告IdClient;导入com.google.android.gms.ads.identifier。广告IdClient。信息;导入com.google.android.gms.common。GooglePlayServicesAvailabilityException;导入com.google.android.gms.common。GooglePlayServicesNotAvailableException;导入java.io.IOException;...//不要从主线程调用此函数。否则,//将引发IllegalStateException。公共void getIdThread(){Info adInfo=空;尝试{adInfo=广告IdClient.getAdvertisingIdInfo(mContext);}catch(IOException异常){//连接到Google Play服务时发生不可恢复的错误(例如。,//旧版本的服务不支持获取AdvertisingId)。}catch(GooglePlayServicesAvailabilityException异常){//连接到Google Play服务时遇到可恢复的错误。}catch(GooglePlayServicesNotAvailableException异常){//Google Play服务并不完全可用。}final字符串id=adInfo.getId();最后一个布尔值isLAT=adInfo.isLimitAdTrackingEnabled();}

来源/文件:

http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/inidentifier/AdvertisingIdClient.html

##重要提示:

广告ID将完全替换现有的出于广告目的使用其他标识符(例如使用ANDROID_ID在“设置”中。安全)。案例Google Play Services不可用的位置由引发GooglePlayServicesNotAvailableException获取广告IdInfo()。

##警告,用户可以重置:

http://en.kioskea.net/faq/34732-android-reset-your-advertising-id

我试图引用我获取信息的每个链接。如果你失踪了,需要加入,请发表评论!

Google Player服务实例ID

https://developers.google.com/instance-id/

  • 7
    我在我的应用程序中使用了你的方法来发送评论。我有个坏消息。不幸的是,PsuedoID并不是完全唯一的。我的服务器记录了5个ID的100多个ID,将近30个ID的30多个ID。重复次数最多的ID是“ffffffff-fc8f-6093-ffffffd8”(159条记录)和“ffffff-fe99-b334-ffff-ffffef”(154次)。而且根据时间和评论,很明显存在不同的人。到目前为止,总记录是10000条。请告诉我为什么会这样。油箱。 评论 2015年3月17日16:36
  • 这是我1.5年多前写的。我不知道为什么它对你来说不是独一无二的。你可以试试广告ID。如果没有,你可以想出自己的解决方案。 评论 2015年3月17日17:35
  • 为什么很难在没有冲突的情况下获得简单的唯一id?只需使用伪Unique ID-软件+日期(hhmmss),不会发生冲突。组合时间越长,难度越高。
    – 古穆鲁
    评论 2022年4月25日3:41
355

正如Dave Webb提到的Android开发者博客上有一篇文章这涵盖了这一点。他们首选的解决方案是跟踪应用程序安装而不是设备,这对大多数用例都很有效。这篇博客文章将向您展示实现这一功能所需的代码,我建议您查看一下。

然而,如果您需要设备标识符而不是应用程序安装标识符,博客文章将继续讨论解决方案。我与谷歌的某位员工进行了交谈,希望在您需要的情况下,对一些项目进行进一步澄清。以下是我发现的关于设备标识符的信息,这些信息在上述博客帖子中没有提及:

  • ANDROID_ID是首选设备标识符。ANDROID_ID在ANDROID版本<=2.1或>=2.3上非常可靠。只有2.2有帖子中提到的问题。
  • 一些制造商的一些设备受到2.2中ANDROID_ID错误的影响。
  • 据我所知,所有受影响的设备相同的ANDROID_ID,这是9774d56d682e549c。这也是模拟器报告的相同设备id,顺便说一句。
  • 谷歌认为,原始设备制造商已经为他们的许多或大多数设备修复了这个问题,但我能够验证,至少到2011年4月初,仍然很容易找到ANDROID_ID坏了的设备。

根据谷歌的建议,我实现了一个类,该类将为每个设备生成唯一的UUID,在适当的情况下使用ANDROID_ID作为种子,必要时使用TelephonyManager.getDeviceId(),如果失败,则使用随机生成的唯一UUID在应用程序重启(但不是应用程序重新安装)时保持不变.

请注意,对于必须回退到设备ID上的设备,唯一ID威尔在工厂重置期间坚持。这是值得注意的。如果您需要确保出厂重置将重置您的唯一ID,您可能需要考虑直接返回到随机UUID,而不是设备ID。

同样,此代码用于设备ID,而不是应用程序安装ID。在大多数情况下,应用程序安装标识可能就是您要查找的。但如果您确实需要设备ID,那么以下代码可能适合您。

导入android.content。语境;导入android.content。共享首选项;导入android.provider。设置。安全;导入android.telephony。电话经理;导入java.io.UnsupportedEncodingException;导入java.util。UUID;公共类DeviceUuidFactory{受保护的静态最终字符串PREFS_FILE=“device_id.xml”;受保护的静态最终字符串PREFS_DEVICE_ID=“DEVICE_ID”;保护挥发性静态UUID UUID;公共DeviceUuidFactory(上下文上下文){if(uuid==空){同步(DeviceUuidFactory.class){if(uuid==空){final SharedPreferences首选项=上下文.getSharedPreferences(PREFS_FILE,0);最终字符串id=prefs.getString(prefs_DEVICE_id,null);if(id!=空){//使用以前计算并存储在//prefs文件uuid=uuid.fromString(id);}其他{final字符串androidId=Secure.getString(context.getContentResolver(),安全。安卓ID_ID);//使用Android ID,除非它坏了,在这种情况下//设备ID回退,//除非不可用,否则随机回退//我们存储到prefs文件中的数字尝试{if(!“9774d56d682e549c”.equals(androidId)){uuid=uuid.nameUUIDFromBytes(androidId.getBytes(“utf8”);}其他{最终字符串设备ID=((TelephonyManager)上下文.getSystemService(上下文.TELEPHONY_SERVICE).getDeviceId();uuid=设备ID!=无效的?UUID(统一用户识别码).nameUUIDFromBytes(设备ID.getBytes(“utf8”):UUID.randomUUID();}}catch(不支持的编码异常e){抛出新的RuntimeException(e);}//将值写入prefs文件首选编辑().putString(PREFS_DEVICE_ID,uuid.toString()).commit();}}}}}/***返回当前android设备的唯一UUID。与所有UUID一样,*这个唯一的ID“极有可能”在所有安卓系统中都是唯一的*设备。这比ANDROID_ID要多得多。* *如果合适,UUID是通过使用ANDROID_ID作为基键生成的,*如果已知ANDROID_ID,则返回TelephonyManager.getDeviceID()*不正确,并最终返回到持久化的随机UUID*如果getDeviceID()未返回可用值,则设置为SharedPreferences。* *在某些罕见的情况下,此ID可能会更改。特别是,如果*设备出厂重置后,可能会生成新的设备ID。此外,如果*用户从Android的某些错误实现中升级手机*2.2到更新的无bug版本的Android,设备ID可能会更改。*或者,如果用户在既没有合适的*Android ID或设备ID,此ID可能会在重新安装时更改。* *注意,如果代码返回到使用TelephonyManager.getDeviceId()时,*在工厂重置之后,所得到的ID将不会改变。有些事情要做*知道。* *在使用Android_ID时,可以解决许多设备的Android 2.2中的错误*直接。* *@参见http://code.google.com/p/android/issues/detail?id=10603* *@return一个UUID,可用于在大多数情况下唯一识别您的设备*目的。*/公共UUID getDeviceUuid(){返回uuid;}}
2
  • 10
    ANDROID_ID可以在出厂重置时更改,因此它也无法识别设备
    – 塞缪尔
    评论 2011年5月19日8:12
  • 2
    正如我在你的同一个答案上所注意到的那样在这里无法保证您将跨进程获得相同的生成UUID-SharedPrefs副本是地方的! 评论 2013年9月18日12:46
188

下面是Reto Meier在谷歌I/O在今年的演示中为用户获取唯一的id:

私有静态字符串uniqueID=null;private static final String PREF_UNIQUE_ID=“PREF_NUIQUE_ID”;公共同步静态字符串id(上下文上下文){if(uniqueID==空){SharedPreferences sharedPrefs=上下文.getSharedPreverses(PREF_UNIQUE_ID,上下文。模式_专用);uniqueID=sharedPrefs.getString(PREF_UNIQUE_ID,null);if(uniqueID==空){uniqueID=UUID.randomUUID().toString();编辑器编辑器=sharedPrefs.edit();editor.putString(PREF_UNIQUE_ID,uniqueID);editor.commit();}}return uniqueID;}

如果您将此与备份策略结合起来,以将首选项发送到云(在Reto的谈话,您应该有一个绑定到用户的id,并在擦除或更换设备后一直保留。我计划在以后的分析中使用它(换言之,我还没有这么做:)。

  • 4
    如果你在卸载和重新安装后不需要唯一的ID来保持,这是一个很好的选择(例如,促销活动/游戏,你有三次获胜的机会)。 评论 2012年5月15日21:52
  • Meier演示依赖于使用Android Backup Manager,而这又取决于用户选择打开该功能。这对于应用程序用户的首选项(Meier的使用)来说很好,因为如果用户没有选择该选项,她就不会备份这些功能。然而,最初的问题是为装置该ID是按应用程序生成的,甚至不是按安装生成的,更不用说按设备生成了,而且由于它依赖于用户选择备份选项,因此其超出用户首选项的使用受到了限制(例如,对于有时间限制的试用)。
    – 卡尔
    评论 2012年12月22日11:15
  • 17
    这对卸载或清除数据无效。 评论 2014年9月2日2:25
142

这是一个简单的问题,没有简单的答案。

此外,这里所有现有的答案要么已经过时,要么不可靠。

所以如果你在2020年后寻找解决方案.

以下是需要记住的几点:

所有基于硬件的标识符(IMEI、MAC、序列号等)对于非谷歌设备(Pixels和Nexuses除外)来说都是不可靠的,这在统计上是不可靠的全球大多数android活动设备.因此是官方的Android标识符最佳实践明确规定:

避免使用硬件标识符,例如IMEI、MAC地址等。。。

这使得这里的大多数答案无效。此外,由于不同的安卓安全更新,其中一些需要更新和更严格的运行时权限,用户可以简单地拒绝这些权限。

例如CVE-2018-9489影响上述所有基于WIFI的技术。

这使得这些标识符不仅不可靠,而且在许多情况下也无法访问。

所以用简单的话来说:不要使用那些技巧.

这里的许多其他答案建议使用广告IdClient,这也是不兼容的,因为它的设计仅用于广告分析。它也在官方参考

仅将广告ID用于用户分析或广告用例

它不仅不可靠,而且还必须遵循关于广告跟踪的用户隐私策略,该策略明确规定用户可以随时重置或阻止它。

所以也不要用它.

因为您无法获得所需的全局唯一且可靠的静态设备标识符。Android的官方参考建议:

使用Firebase安装ID(FID)或专用存储的GUID尽可能用于除支付欺诈预防和电话之外的所有其他用例。

它对于设备上的应用程序安装来说是独一无二的,所以当用户卸载应用程序时,它会被删除,所以它不是100%可靠的,但这是次佳的选择。

注释截至今天FirebaseInstanceId已弃用,应使用消防设施而不是。

要使用消防设施添加最新的firebase-messaging依赖项进入你的毕业典礼

实现'com.google.firebase:firebase消息传递:23.0.0'

并使用以下代码获取firebase ID:

FirebaseInstallations.getInstance().getId().addOnCompleteListener(任务->{if(task.isSuccessful()){字符串firebaseIdentifier=task.getResult();//使用firebaseIdentifier执行您需要的操作}});

如果需要将设备标识存储在远程服务器上,则不要按原样存储(纯文本),而是加盐的杂烩.

今天,这不仅是一种最佳实践,实际上你必须按照法律来做GDPR-标识符和类似法规。

10
  • 你能具体说明你发布的GDPR链接的哪一部分实际上提到了散列ID的要求吗? 评论 2020年7月21日9:55
  • 1
    @戴维·施耐德(DavidSchneider),网络内容本质上是动态的,GDPR只是其中一个例子,请注意我写过“GDPR和类似法规“,因为有许多地方和全球法规影响您的产品/系统/领域。在任何情况下,您需要的GDPR部分包括:识别,在线标识符数据保护原则 评论 2020年7月21日10:39
  • proandroiddev.com/… 评论 2021年1月3日19:54
  • @famfamfam因为跟踪设备对用户的隐私和安全构成威胁。没有合法的案例需要这样做,所以没有理由让它成为可能。Hw ID被证明是错误的,谷歌正在慢慢修复它们。 评论 2022年2月14日13:12
  • 1
    @这是一个单独的问题。根据stackoverflow的政策,应该单独询问。无论如何,请记住,管理演示/试用版本可能会对您的产品产生业务影响,所以我认为没有一种解决方案可以匹配所有情况。 评论 3月24日7:57
106

你也可以考虑Wi-Fi适配器的MAC地址。检索方式如下:

WifiManager wm=(WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);return wm.getConnectionInfo().getMacAddress();

需要权限android权限。访问_WIFI_STATE在舱单上。

据报道,即使未连接Wi-Fi,也可以使用。如果上面答案中的Joe在他的许多设备上尝试一下这个,那就太好了。

在某些设备上,当Wi-Fi关闭时,它不可用。

注:在Android 6.x中,它返回一致的伪mac地址:02:00:00:00:00:00

10
  • 9
    这是必需的android权限。访问_WIFI_STATE 评论 2010年11月1日4:41
  • 7
    我想你会发现,在几乎所有的安卓设备上,当WiFi关闭时,它都不可用。关闭WiFi将删除内核级别的设备。 评论 2012年5月21日23:38
  • 13
    @Sanandrea——让我们面对现实吧,在一个植根设备上,任何东西都可以被欺骗。
    – 可可豆
    评论 2013年9月4日9:53
  • 6
    Android M上已阻止访问WiFi MAC地址:stackoverflow.com/questions/31329733/…
    – 布尔兹
    评论 2015年12月22日12:37
  • 8
    在Android 6.x中,它返回一致的伪mac地址:02:00:00:00:00:00 评论 2016年7月10日9:14
93

有相当有用的信息在这里.

它包括五种不同的ID类型:

  1. IMEI公司(仅适用于使用手机的Android设备;需要android权限。读取电话状态)
  2. 伪唯一ID(适用于所有Android设备)
  3. Android ID(可以为空,可以在出厂重置时更改,可以在根电话上更改)
  4. WLAN MAC地址字符串(需要android权限。访问_WIFI_STATE)
  5. BT MAC地址字符串(具有蓝牙功能的设备,需要android权限。蓝牙)
4
  • 重要的一点被遗漏了(在这里和文章中):除非打开WLAN或BT MAC,否则您无法获得它们!否则,我认为WLAN MAC将是完美的标识符。你不能保证用户会打开他们的Wi-Fi,我也不认为自己打开它是“合适的”。
    – 汤姆
    评论 2012年10月7日22:44
  • 2
    @汤姆,你错了。即使关闭WLAN或BT MAC,您仍然可以读取它们。但是,无法保证设备具有可用的WLAN或蓝牙模块。 评论 2014年8月4日10:20
  • 4
    最值得注意的是,本地WiFi和蓝牙MAC地址不再可用。WifiInfo对象的getMacAddress()方法和BluetoothAdapter.getDefaultAdapter().getAddress( 评论 2016年3月30日6:03
  • 5
    @sarikakate只有在6.0及以上的棉花糖中才是正确的。。。它在低于6.0的棉花糖中仍能正常工作。
    – 涂抹
    评论 2016年4月13日10:22
55

官方Android开发者博客现在有一篇关于这个主题的完整文章,识别应用程序安装.

53

谷歌I/OReto Meier发布了一个关于如何实现这一点的可靠答案,它应该能够满足大多数开发人员跨安装跟踪用户的需求。安东尼·诺兰(Anthony Nolan)在他的回答中指明了方向,但我想我应该写出完整的方法,以便其他人能够轻松地看到如何做到这一点(我花了一段时间才弄清楚细节)。

此方法将为您提供一个匿名、安全的用户ID,该ID将在不同设备(基于主要的Google帐户)和安装过程中对用户保持不变。基本方法是生成一个随机用户ID,并将其存储在应用程序的共享首选项中。然后,您可以使用谷歌的备份代理将链接到谷歌帐户的共享偏好存储在云中。

让我们来看看完整的方法。首先,我们需要使用Android备份服务为SharedPreferences创建备份。首先通过注册应用程序http://developer.android.com/google/backup/signup.html.

谷歌将为您提供一个备份服务密钥,您需要将其添加到清单中。您还需要告诉应用程序使用BackupAgent,如下所示:

<application android:label=“MyApplication”android:backupAgent=“MyBackupAgent”>...<meta-data android:name=“com.google.android.backup.api_key”android:value=“your_backup_service_key”/></应用程序>

然后,您需要创建备份代理并告诉它使用sharedpreferences的助手代理:

公共类MyBackupAgent扩展了BackupAgentHelper{//SharedPreferences文件的名称static final String PREFS=“user_preferences”;//唯一标识备份数据集的密钥静态最终字符串PREFS_BACKUP_KEY=“PREFS”;//分配一个助手并将其添加到备份代理@覆盖公共void onCreate(){SharedPreferencesBackupHelper helper=新的SharedPreffeencesBackuphelper(this,PREFS);addHelper(PREFS_BACKUP_KEY,助手);}}

要完成备份,您需要在主“活动”中创建BackupManager实例:

BackupManager BackupManager=新的BackupManager(上下文);

最后创建一个用户ID(如果它还不存在),并将其存储在SharedPreferences中:

公共静态字符串getUserID(上下文上下文){私有静态字符串uniqueID=null;private static final String PREF_UNIQUE_ID=“PREF_NUIQUE_ID”;if(uniqueID==空){SharedPreferences sharedPrefs=上下文.getSharedPreverses(我的备份代理。PREFS,上下文。模式_专用);uniqueID=sharedPrefs.getString(PREF_UNIQUE_ID,null);if(uniqueID==空){uniqueID=UUID.randomUUID().toString();编辑器编辑器=sharedPrefs.edit();editor.putString(PREF_UNIQUE_ID,uniqueID);editor.commit();//备份更改BackupManager mBackupManager=新的BackupManager(上下文);mBackupManager.dataChanged();}}return uniqueID;}

现在,即使用户移动设备,此User_ID也将在整个安装中保持不变。

有关此方法的更多信息,请参见雷托的谈话.

有关如何实现备份代理的完整详细信息,请参阅数据备份。我特别推荐底部的测试部分,因为备份不会立即发生,所以要进行测试,必须强制备份。

2
  • 9
    当用户有多个设备时,这是否会导致多个设备具有相同的id?比如平板电脑和手机。
    – 托萨
    评论 2013年3月13日2:25
  • 如果用户禁用了手机上的备份怎么办?在这种情况下,不会备份任何应用程序数据! 评论 2022年8月23日6:59
45

我认为这肯定是为一个独特的ID构建骨架的最佳方式……看看吧。

伪Unique ID,适用于所有Android设备有些设备没有手机(例如平板电脑),或者由于某些原因,您不想包含READ_phone_STATE权限。您仍然可以阅读ROM版本、制造商名称、CPU类型和其他硬件详细信息,如果您想将ID用于串行密钥检查或其他一般用途,这些信息将非常适合。以这种方式计算的ID并不是唯一的:可以找到两个具有相同ID的设备(基于相同的硬件和ROM映像),但实际应用程序中的更改可以忽略不计。为此,您可以使用Build类:

字符串m_szDevIDShort=“35”+//我们使其看起来像有效的IMEI生成。BOARD.length()%10+内部版本。品牌长度()%10+生成。CPU_ABI.length()%10+内部版本。设备长度()%10+生成。DISPLAY.length()%10+内部版本。主机长度()%10+构建。ID长度()%10+内部版本。制造商长度()%10+生成。MODEL.length()%10+内部版本。产品长度()%10+生成。TAGS.length()%10+内部版本。TYPE.length()%10+构建。用户长度()%10//13位数

大多数Build成员都是字符串,我们在这里所做的是获取它们的长度并通过数字中的模进行转换。我们有13个这样的数字,我们在前面(35)再加上两个,使其与IMEI(15个数字)具有相同的大小ID。这里还有其他的可能性,看看这些字符串。返回类似以下内容355715565309247。无需特别许可,这使得这种方法非常方便。


(额外信息:上述技术是从上的一篇文章中复制的口袋魔术.)

9
  • 10
    有趣的解决方案。听起来,在这种情况下,您真的应该只对连接的所有数据进行散列,而不是尝试使用自己的“散列”函数。在许多情况下,即使每个值有不同的大量数据,也会发生冲突。我的建议是:使用散列函数,然后将二进制结果转换为十进制,并根据需要截断它。要做到这一点,您应该真正使用UUID或完整的散列字符串。 评论 2011年4月12日16:28
  • 25
    你应该相信你的消息来源。。。这是从以下文章中直接提出来的:pocketmagic.net/?p=1662 评论 2011年5月16日12:07
  • 11
    这个ID容易发生冲突,就像你不知道什么。实际上,在同一运营商的相同设备上,它保证是相同的。 评论 2011年5月26日20:21
  • 10
    如果设备升级,这也可能会改变。 评论 2012年1月25日12:25
  • 13
    非常非常糟糕的解决方案。在两个Nexus 5上测试…返回相同的数字。 评论 2015年3月4日16:53
43

以下代码使用隐藏的Android API返回设备序列号。但是,此代码在三星Galaxy Tab上不起作用,因为此设备上未设置“ro.serialno”。

字符串序列=空;尝试{类别<?>c=Class.forName(“android.os.SystemProperties”);方法get=c.getMethod(“get”,String.class);serial=(String)get.invoke(c,“ro.serialno”);}catch(忽略异常){}
33

使用下面的代码,您可以获得Android OS设备的唯一设备ID作为字符串。

deviceId=Secure.getString(getApplicationContext().getContentResolver(),安全。安卓ID_ID);
0
23

我要补充的一件事是,我有一种独特的情况。

使用:

deviceId=Secure.getString(this.getContext().getContentResolver(),安全。安卓ID_ID);

结果表明,即使我的Viewsonic G Tablet报告的DeviceID不为Null,每个G Tablet都报告相同的数字。

玩“Pocket Empires”很有趣,它可以根据“唯一”DeviceID即时访问某人的帐户。

我的设备没有手机收音机。

0
23

序列号字段已添加到生成API级别9中的类(Android 2.3-Gingerbread)。文档显示它代表硬件序列号。因此,如果它存在于设备上,那么它应该是唯一的。

我不知道API级别>=9的所有设备是否都支持(=not null)。

1
  • 5
    不幸的是,它是“未知”的。
    – m0短距0
    评论 2013年4月15日12:33
21

有关如何为安装应用程序的每个Android设备获取唯一标识符的详细说明,请参阅官方Android开发者博客帖子识别应用程序安装.

看起来最好的方法是在安装时自己生成一个,然后在重新启动应用程序时读取它。

我个人认为这是可以接受的,但并不理想。Android提供的任何一个标识符在所有情况下都不起作用,因为大多数标识符取决于手机的无线电状态(Wi-Fi开/关、手机开/关和蓝牙开/关)。其他人,比如设置。安全。安卓ID_ID必须由制造商实施,并且不保证是唯一的。

以下是将数据写入安装将与应用程序本地保存的任何其他数据一起存储的文件。

公共类安装{私有静态字符串sID=null;private static final String INSTALLATION=“安装”;公共同步静态字符串id(上下文上下文){if(sID==空){文件安装=新文件(context.getFilesDir(),安装);尝试{if(!installation.exists())writeInstallationFile(安装);sID=读取安装文件(安装);} catch(异常e){抛出新的RuntimeException(e);}}返回sID;}私有静态字符串readInstallationFile(文件安装)引发IOException{RandomAccessFile f=新的RandomAccess文件(安装,“r”);byte[]字节=新字节[(int)f.length()];f.readFully(字节);f.关闭();返回新字符串(字节);}private static void writeInstallationFile(文件安装)引发IOException{FileOutputStream out=新的FileOutput Stream(安装);字符串id=UUID.randomUUID().toString();out.write(id.getBytes());out.close();}}
4
  • 1
    如果你想跟踪应用程序安装,这是完美的。不过,跟踪设备要复杂得多,而且似乎没有一个完全的空中解决方案。 评论 2011年10月3日19:56
  • 根设备如何?他们可以很容易地更改此安装id,对吗? 评论 2012年5月9日21:24
  • 当然可以。根用户可以更改安装ID。您可以使用此代码块检查根用户:stackoverflow.com/questions/1101380/… 评论 2012年5月18日16:52
  • 如果出厂时重置并删除或格式化/data分区,则UUID不同。 评论 2015年1月12日2:08
16

在类文件中添加以下代码:

final TelephonyManager tm=(电话管理器)getBaseContext().getSystemService(SplashActivity.TELEPHONY_SERVICE);最终字符串tmDevice,tmSerial,androidId;tmDevice=“”+tm.getDeviceId();Log.v(“DeviceIMEI”,“”+tmDevice);tmSerial=“”+tm.getSimSerialNumber();Log.v(“GSM设备序列号[simcard]”,“”+tmSerial);androidId=“”+android.provider。设置。安全.getString(getContentResolver(),android.provider。设置。安全。安卓ID_ID);Log.v(“androidId CDMA设备”,“”+androidId);UUID设备UUID=新的UUID(androidId.hashCode(),((长)tmDevice.hashCode()<<32)|tmSerial.hashCode());String设备Id=设备Uuid.toString();Log.v(“deviceIdUUID通用唯一标识符”,“”+deviceId);String deviceModelName=android.os。生成。模型;Log.v(“型号名称”,“”+设备型号名称);String deviceUSER=android.os。生成。用户;Log.v(“Name USER”,“”+设备用户);String devicePRODUCT=android.os。生成。产品;Log.v(“PRODUCT”,“”+设备PRODUCT);String deviceHARDWARE=android.os。生成。硬件;Log.v(“硬件”,“”+设备硬件);字符串deviceBRAND=android.os。生成。品牌;Log.v(“BRAND”,“”+设备BRAND);字符串myVersion=android.os。生成。版本。发布;Log.v(“VERSION.RELEASE”,“”+myVersion);int sdkVersion=android.os。生成。版本。SDK_INT;Log.v(“版本.SDK_INT”,“”+sdkV版本);

加载项AndroidManifest.xml:

<使用权限android:name=“android.permission.READ_PHONE_STATE”/>
15

有很多不同的方法可以解决这些问题安卓ID_ID问题(可能是无效的有时,特定型号的或设备总是返回相同的ID)及其优缺点:

  • 实现自定义ID生成算法(基于假定为静态且不会更改的设备属性->谁知道)
  • 滥用其他ID,如IMEI公司、序列号、Wi-Fi/Bluetooth-MAC地址(它们不存在于所有设备上,也不需要其他权限)

我自己更喜欢使用现有的OpenUDID实现(请参见https://github.com/ylechelle/OpenUDID)对于Android(请参见https://github.com/vieux/OpenUDID). 它易于集成并利用安卓ID_ID为上述问题提供了退路。

13

我的两分钱-注意,这是一个设备(错误)唯一ID-不是如中所述的安装Android开发者博客.

值得注意的是解决方案@emmby提供的SharedPreferences不是跨进程同步的,因此返回到每个应用程序ID中(请参见在这里在这里). 所以我完全避免了这一点。

相反,我封装了在枚举中获取(设备)ID的各种策略-更改枚举常量的顺序会影响获取ID的各种方式的优先级。返回第一个非空ID或抛出异常(按照不给null赋值的良好Java实践)。例如,我首先使用TELEPHONY,但一个好的默认选择是ANDROID_ID贝塔:

导入android。许可证;导入android.bluetooth。蓝牙适配器;导入android.content。语境;导入android.content.pm.PackageManager;导入android.net.wifi。WifiManager;导入android.provider。设置。安全;导入android.telephony。电话经理;导入android.util。日志;//TODO:哈希公共最终类DeviceIdentifier{专用设备标识符(){}/**@参见http://code.google.com/p/android/issues/detail?id=10603 */private static final String ANDROID_ID_BUG_MSG=“设备遭受”+“Android ID bug-它的ID是模拟器ID:”+ID。BUGGY_ANDROID_ID;私有静态volatile字符串uuid;//需要挥发性物质-见EJ第71项//需要惰性初始化才能获得上下文/***返回此设备的唯一标识符。第一个(按顺序*枚举ID中定义的常量)非空标识符为*返回或引发DeviceIDException。DeviceIDException也是*如果ignoreBuggyAndroidID为false且设备具有Android ID,则引发*错误**@param ctx(参数ctx)*Android常量(用于检索系统服务)*@param ignoreBuggyAndroidID*如果为false,则在带有android ID错误的设备上,错误*不返回android ID,而是返回DeviceIDException*抛出的*@return a*设备*ID-从不返回null,而是返回*引发DeviceIDException*@throws DeviceIDException*如果没有枚举方法返回设备ID*/公共静态字符串getDeviceIdentifier(Context ctx,boolean ignoreBuggyAndroidID)引发DeviceIDException{字符串结果=uuid;if(结果==空){同步(DeviceIdentifier.class){结果=uuid;if(结果==空){for(id id:IDs.values()){尝试{result=uuid=id.getId(ctx);}catch(设备IDNotUniqueException e){if(!ignoreBuggyAndroidID)抛出新的DeviceIDException(e);}if(result!=null)返回结果;}抛出新的DeviceIDEException();}}}返回结果;}专用静态枚举ID{电话_ID{@覆盖字符串getId(上下文ctx){//TODO:添加基于SIM的机制?tm.getSimSerialNumber();final TelephonyManager tm=(电话管理器)ctx.getSystemService(上下文.TELEPHONY_SERVICE);如果(tm==空){w(“电话管理器不可用”);返回null;}资产权限(ctx,权限.READ_PHONE_STATE);返回tm.getDeviceId();}},安卓ID_ID{@覆盖字符串getId(Context ctx)引发DeviceIDException{//无需许可!final字符串andoidId=SecuregetString(ctx.getContentResolver(),android.provider。设置。安全。安卓ID_ID);if(BUGGY_ANDROID_ID.equals(andoidId)){e(ANDROID_ID_BUG_MSG);抛出新的DeviceIDNotUniqueException();}返回andoidId;}},WIFI_MAC公司{@覆盖字符串getId(上下文ctx){WifiManager wm=(WifiManager)ctx.getSystemService(上下文.WIFI_SERVICE);如果(wm==空){w(“Wifi管理器不可用”);返回null;}assertPermission(ctx,permission.ACCESS_WIFI_STATE);//我猜//getMacAddress()没有java文档!!!返回wm.getConnectionInfo().getMacAddress();}},蓝牙_MAC{@覆盖字符串getId(上下文ctx){蓝牙适配器ba=蓝牙适配器.getDefaultAdapter();if(ba==空){w(“蓝牙适配器不可用”);返回null;}assertPermission(ctx,permission.BLUETOOTH);return ba.getAddress();}}//TODO伪ID// http://www.pocketmagic.net/2011/02/android-unique-device-id/;静态最终字符串BUGGY_ANDROID_ID=“9774d56d682e549c”;private final static String TAG=IDs.class.getSimpleName();abstract String getId(Context ctx)抛出DeviceIDException;private static void w(字符串消息){Log.w(标签,消息);}private static void e(字符串消息){日志.e(标签,消息);}}私有静态void assertPermission(Context ctx,String perm){final int checkPermission=ctx.getPackageManager().checkPermission(perm,ctx.getPackageName());if(checkPermission!=包管理器.PERMISSION_GRANTED){throw new SecurityException(“Permission”+perm+“is required”);}}// =========================================================================//例外情况// =========================================================================公共静态类DeviceIDException扩展异常{private static final long serialVersionUID=-808369995384519417L;private static final String NO_ANDROID_ID=“无法检索”+“设备ID”;公共DeviceIDException(Throwable Throwable){super(NO_ANDROID_ID,可丢弃);}公共DeviceIDException(String detailMessage){super(详细消息);}公共设备IDException(){超级(NO_ANDROID_ID);}}公共静态最终类DeviceIDNotUniqueException扩展DeviceIDException(设备IDE异常){私有静态最终长序列VersionUID=-8940090896069484955L;公用设备IDNotUniqueException(){超级(ANDROID_ID_BUG_MSG);}}}
13

这里有30多个答案,有些是相同的,有些是独特的。这个答案是基于这些答案中的一些。其中之一就是@Lenn Dolling的答案。

它结合了3个ID并创建了一个32位十六进制字符串。它对我来说非常有效。

3个ID是:
伪ID-它是根据物理设备规范生成的
安卓ID_ID-设置。安全。安卓ID_ID
蓝牙地址-蓝牙适配器地址

它将返回如下内容:551F27C060712A72730B0A0F734064B1

注意:您始终可以向长Id字符串。例如,序列号。wifi适配器地址。IMEI。通过这种方式,您可以使其在每个设备上更加独特。

@禁止警告(“弃用”)@SuppressLint(“硬件ID”)公共静态字符串generateDeviceIdentifier(上下文上下文){字符串伪Id=“35”+构建。电路板长度()%10+生成。品牌长度()%10+生成。CPU_ABI.length()%10+生成。设备长度()%10+生成。显示长度()%10+生成。主机长度()%10+生成。ID长度()%10+生成。制造商长度()%10+生成。型号.长度()%10+生成。产品长度()%10+生成。标签长度()%10+生成。TYPE.length()%10+生成。用户长度()%10;String androidId=设置。Secure.getString(context.getContentResolver(),设置。安全。安卓ID_ID);BluetoothAdapter蓝牙适配器=蓝牙适配器.getDefaultAdapter();字符串btId=“”;if(bluetoothAdapter!=空){btId=蓝牙适配器.getAddress();}字符串longId=pseudoId+androidId+btId;尝试{MessageDigest MessageDigest=消息摘要.getInstance(“MD5”);messageDigest.update(longId.getBytes(),0,longId.length());//获取md5字节byte md5Bytes[]=消息摘要.摘要();//创建十六进制字符串字符串标识符=“”;for(字节md5Byte:md5Bytes){int b=(0xFF&md5字节);//如果是单个数字,请确保前面有0(正确的填充)如果(b<=0xF){标识符+=“0”;}//将数字添加到字符串标识符+=整数.toHexString(b);}//十六进制字符串到大写identifier=identifier.toUpperCase();返回标识符;}catch(异常e){Log.e(“标签”,e.toString());}返回“”;}
  • 添加UUID(统一用户识别码)长Id并将其存储在文件中,将使其成为最唯一的标识符:字符串uuid=uuid.randomUUID().toString(); 评论 2017年4月11日7:08
  • 如果所有这些都失败了,如果用户的API 9(低于Gingerbread),则重置手机或“安全”。ANDROID_ID’。如果返回“null”,那么返回的ID将仅基于他们的Android设备信息。这就是碰撞可能发生的地方。尽量不要使用DISPLAY、HOST或ID-这些项目可能会发生变化。如果存在冲突,将存在重叠数据。来源:gist.github.com/pedja1/fe69e8a80ed505500caa注册中心 评论 2017年4月11日7:17
  • 2
    @忍者由于BLE mac地址是唯一的,是的,生成的ID将始终是唯一的。然而,如果您真的想确定,我建议将UUID添加到长Id。将这一行改为:字符串longId=pseudoId+androidId+btId+UUID.randomUUID().toString();这将确保生成的ID是唯一的。 评论 2019年4月3日12:38
12

那么IMEI公司。这对于Android或其他移动设备来说是独一无二的。

2
  • 11
    我的平板电脑没有IMEI,因为它们没有连接到我的移动运营商。 评论 2012年1月5日16:54
  • 更不用说使用ESN而不是IMEI的CDMA设备了。 评论 2012年1月25日12:25
12

Android OS设备的唯一设备ID为String,使用电话管理器安卓ID_ID,通过以下方式获得:

字符串deviceId;最终电话管理器mTelephony=(电话管理器)getSystemService(Context.TELEPHONY_SERVICE);if(mTelephony.getDeviceId()!=null){设备Id=mTelephony.getDeviceId();}其他{deviceId=安全设置字符串(getApplicationContext().getContentResolver(),安全。安卓ID_ID);}

但我强烈建议使用谷歌建议的方法,请参阅识别应用程序安装.

0
11

以下是我如何生成唯一id:

公共静态字符串getDeviceId(上下文ctx){电话管理器tm=(电话管理器)ctx.getSystemService(Context.TELEPHONY_SERVICE);字符串tmDevice=tm.getDeviceId();String androidId=Secure.getString(ctx.getContentResolver(),安全。安卓ID_ID);字符串序列=空;if(Build.VERSION.SDK_INT>Build.VERTION_CODES.FROYO)serial=Build。系列;如果(tmDevice!=null)返回“01”+tmDevice;如果(androidId!=null)返回“02”+androidId;如果(serial!=null)返回“03”+serial;//其他替代方案(即Wi-Fi MAC、蓝牙MAC等)返回null;}
1
  • 如果我们在6.0版本中使用ReadPhoneState请求运行时权限
    – 哈沙
    评论 2016年11月17日14:06
10

另一种方法是使用/sys/class/android_usb/android0/iSerial系统在没有任何权限的应用程序中。

用户@爬行:~$adb shell ls-l/sys/class/android_usb/android0/iSerial-rw-r--r--根4096 2013-01-10 21:08 i序列用户@爬行:~$adb shell cat/sys/class/android_usb/android0/iSerial0a3xxxxxxxxxx5

要在Java中实现这一点,只需使用FileInputStream打开iSerial文件并读取字符。只需确保将其包装在异常处理程序中,因为并非所有设备都有此文件。

已知至少以下设备具有此文件的全球可读性:

  • 银河系Nexus
  • Nexus S公司
  • 摩托罗拉Xoom 3G
  • 东芝AT300
  • HTC一V
  • 迷你MK802
  • 三星Galaxy S II

你也可以看到我的博客帖子将Android硬件序列号泄露给未经授权的应用程序在这里,我讨论了可供参考的其他文件。

2
  • 我刚看了你的博客帖子。我相信这并不是唯一的:建造。SERIAL也可以使用,无任何权限,并且(理论上)是唯一的硬件序列号。
    – 汤姆
    评论 2013年4月30日21:14
  • 2
    你说得对。这只是跟踪设备的另一种方式,正如您所说,这两种方式都不需要应用程序权限。 评论 2014年3月14日17:16
10

对于特定Android设备的硬件识别,您可以检查MAC地址。

你可以这样做:

在AndroidManifest.xml中

<uses-permission android:name=“android.permission.INTERNET”/>

现在在代码中:

List<NetworkInterface>interfacesList=Collections.List(NetworkInterface.getNetworkInterfaces());for(NetworkInterface接口:interfacesList){//这将为您提供MAC ADDRESS接口接口.getHardwareAddress();}

在每一款Android设备中,它们至少有一个“wlan0”接口,即WI-FI芯片。即使WI-FI未打开,此代码也可以工作。

附笔。它们是一系列其他接口,您可以从包含MACS的列表中获得,但这可以在手机之间更改。

10

我使用以下代码获取IMEI公司或使用Secure。安卓ID_ID另外,当设备没有电话功能时:

字符串标识符=空;TelephonyManager tm=(TelephoneyManager)context.getSystemService(context.TELEPHONY_SERVICE));如果(tm!=空)标识符=tm.getDeviceId();if(identifier==null||identifier.length()==0)identifier=Secure.getString(activity.getContentResolver(),安全。安卓ID_ID);
9

电话管理器.getDeviceId()返回唯一的设备ID,例如,GSM的IMEI和CDMA手机的MEID或ESN。

最终TelephonyManager mTelephony=(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);字符串myAndroidDeviceId=mTelephony.getDeviceId();

但我建议使用:

设置。安全。安卓ID_ID它将Android ID作为唯一的64位十六进制字符串返回。

String myAndroidDeviceId=Secure.getString(getApplicationContext().getContentResolver(),安全。安卓ID_ID);

有时电话管理器.getDeviceId()将返回null,因此为了确保唯一的id,您将使用此方法:

公共字符串getUniqueID(){字符串myAndroidDeviceId=“”;电话管理器mTelephony=(电话管理器)getSystemService(Context.TELEPHONY_SERVICE);if(mTelephony.getDeviceId()!=null){myAndroidDeviceId=m电话.getDeviceId();}其他{myAndroidDeviceId=安全.getString(getApplicationContext().getContentResolver(),安全。安卓ID_ID);}return myAndroidDeviceId;}
1
  • 我最近发现,SM-G928F/Glaxy S6 edge+型客户端设备只能为Android ID提供15位而不是16位十六进制数字。 评论 2016年3月13日16:16
9

谷歌实例ID

2015年I/O发布;在Android上需要播放服务7.5。

https://developers.google.com/instance-id/
https://developers.google.com/instance-id/guides/android-implementation网站

InstanceID iid=实例ID.getInstance(上下文);//谷歌文档是错误的-这需要上下文字符串id=iid.getId();//阻塞呼叫

谷歌似乎打算使用这个ID来识别Android、Chrome和iOS上的安装。

它标识的是安装,而不是设备,但ANDROID_ID(这是公认的答案)现在也不再标识设备。使用ARC运行时,将为每个安装生成新的ANDROID_ID(此处显示详细信息),就像这个新的实例ID一样。此外,我认为识别安装(而不是设备)是我们大多数人真正需要的。

实例ID的优点

在我看来,谷歌打算将其用于此目的(识别您的安装),它是跨平台的,可以用于许多其他目的(请参阅上面的链接)。

如果您使用GCM,那么您最终将需要使用此实例ID,因为您需要它来获取GCM令牌(它替换了旧的GCM注册ID)。

缺点/问题

在当前的实现(GPS 7.5)中,当您的应用程序请求时,会从服务器检索实例ID。这意味着上述调用是一个阻塞调用-在我的非科学测试中,如果设备在线,则需要1-3秒,如果离线,则为0.5-1.0秒(大概是在放弃并生成随机ID之前等待的时间)。这是在北美Nexus 5上使用Android 5.1.1和GPS 7.5进行的测试。

如果你将ID用于他们想要的目的——例如应用程序身份验证、应用程序识别、GCM——我认为这1-3秒可能会很麻烦(当然,这取决于你的应用程序)。

2
  • instanceID的另一个重要缺点是,如果用户清除应用程序的数据,将为您生成一个新的instanceID。 评论 2015年7月8日16:13
  • 有趣,但我不认为它真的改变了潜在的用途:实例ID,像android_ID一样,不适合识别设备。因此,您的服务器将看到用户清除数据,就像用户卸载和重新安装您的应用程序一样,这并非不合理。
    – 汤姆
    评论 2015年7月8日20:11
9

获取D设备UUID,型号和品牌名称及其版本号借助以下功能。

在Android 10中工作完全没有必要允许读电话状态许可。

代码段:

私有void fetchDeviceInfo(){字符串uniquePseudoID=“35”+生成。电路板长度()%10+生成。品牌长度()%10+生成。设备长度()%10+生成。显示长度()%10+生成。主机长度()%10+生成。ID长度()%10+生成。制造商长度()%10+生成。型号.长度()%10+生成。产品长度()%10+构建。标签长度()%10+生成。TYPE.length()%10+生成。用户长度()%10;字符串序列=Build.getRadioVersion();字符串uuid=新的uuid(uniquePseudoID.hashCode(),serial.hashCode()).toString();String brand=构建。品牌;String modelno=构建。模型;字符串版本=内部版本。版本。发布;Log.e(标签,“fetchDeviceInfo:\n”+“\n uuid为:”+uuid+“\n brand是:”+brand+“\n model为:”+modelno+“\n版本为:”+版本);}

调用Above函数并检查上述代码的输出。请在android工作室中查看您的日志猫。如下所示:

在此处输入图像描述

  • 代码中的%10和35+“…”是什么?我的意思是,为什么要使用这种方法来构建唯一的id?为什么不简单地将这些字符串组合在一起并生成唯一的UUID?这种方法的输出对于世界上所有的设备都是唯一的吗? 评论 2020年6月14日22:13
  • 2
    Build.getRadioVersion()返回null
    – 尼尚特
    评论 2021年9月22日8:23
  • @Nishant pelase在这里分享您的代码。所以我可以帮你 评论 2021年9月29日4:37
8

谷歌现在有一个广告ID.
这也可以使用,但请注意:

广告ID是用户特定的、唯一的、可重置的ID

允许用户重置他们的标识符或选择退出Google Play应用程序中基于兴趣的广告。

所以虽然这个身份可能会改变,但似乎很快就会改变我们可能别无选择,取决于此id的用途。

更多信息@develper.android

此处为副本代码

高温高压

0

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.