有什么不明白的地方,扫描右方二维码加我微信交流。
       

在做移动开发的过程中,经常要跟踪用户行为数据,这就需要每个用户都有一个唯一Id。对于没有帐号系统的应用来说及其重要。

如今,唯一Id的获取越来越难。

Android早期还能获取IMEI,WIFI蓝牙Mac地址等永久不变的唯一硬件Id,现在厂商越来越注重隐私,逐渐都不允许获取了。

iOS早期可以获取UDID,WIFI的Mac地址等永久不变的唯一硬件Id,现在也是越来越注重隐私,不允许获取了。

那有什么好的解决方案呢?

 

Android

Android获取唯一Id最佳解决方案官方地址:Best practices for unique identifiers

简单总结下:

  • 官方要求避免使用硬件Id,像IMEI,Mac地址等。
  • 广告ID(即Gaid)也仅用于用户配置文件或广告用例,广告ID 用户可以禁止使用,而且可以随意重置,显然不能作为唯一Id。

剩下的就只有SSAID(Android_ID)了,似乎是个不错的选择。

 

SSAID(Android_ID)

在Android 8.0(API级别26)和更高版本的平台上,一个64位数字(表示为十六进制字符串)对于应用程序签名密钥,用户和设备的每种组合都是唯一的。的值ANDROID_ID受签名密钥和用户的限制。如果在设备上执行了出厂重置或APK签名密钥更改,则该值可能会更改。

获取方式:

import android.provider.Settings;
String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);

但也有一定的缺陷(缺陷内容来息其他博客,由于测试设备有限,我并没有遇到以下情况,如果有人遇到,可在评论区留言),如下

  • 厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID:9774d56d682e549c。
  • 厂商定制系统的Bug:有些设备返回的值为null。
  • 设备差异:对于CDMA设备,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
  • ANDROID_ID是设备第一次启动时产生和存储的64bit的一个数,当设备被wipe后该数重置

以上几个问题,如果是在国内发版本,我觉得出问题的概率比较大,因为国内的安卓手机厂商太多,技术良莠不齐,而且国内的用户量大,导致出问题的概率也变大。

海外用户大部分为三星,Pixel(google亲儿子)用户,出问题概率则较小。

 

iOS

iOS目前有3个可获取Id,如下:

IDFA:如同安卓的Gaid,同样可以禁止获取,随时重置。

IDFV:iOS 6.0系统新增用于替换uniqueIdentifier的接口。是给Vendor标识用户用的,每个设备在所属同一个Vendor的应用里,都有相同的值。通过CFBundleIdentifier(DNS反转格式)的前两部分生成。假如我下载了同一个公司开发的两款游戏,且这两款游戏的包名前两部相同,则在这两个app内获取到的IDFV是相同的。卸载其中一个,再安装,IDFV依然不变。若两个都卸载,再安装时其中一个或者两个,IDFV重置。通常情况用户只会安装我们开发的一款应用,一旦卸载,IDFV就会重置。

UUID:Universally Unique Identifier,通用唯一标识符,是一个32位的十六进制序列,UUID在某一时空下是唯一的。获得的UUID值系统不会存储, 而且每次调用得到UUID,系统都会返回一个新的唯一标示符。

 

以上3个Id,也无法确保唯一,我们还需要其他的解决方案。

 

KeyChain

iOS整个系统有一个KeyChain,每个程序都可以往KeyChain中记录数据,而且只能读取到自己程序记录在KeyChain中的数据。即使我们程序删除掉,系统经过升级以后再安装回来,依旧可以获取到与之前一致的数据(系统还原、刷机除外)。因此我们可以将Id的字符串存储到KeyChain中,然后下次直接从KeyChain获取UUID字符串。向KeyChain中存储数据不需要任何权限。

扩展:如果想要在应用中获取其他应用KeyChain的数据,则需要钥匙串权限。例如在AppA中获取AppB存储在KeyChain中的数据。

新的解决方案:KeyChain存储某个Id。

IDFA和IDFV是系统生成的,跟App相关性比较大的Id,当用户操作广告ID重置时IDFA改变,不允许获取时拿不到,当App卸载重装时IDFV会改变,我们可能会用到改变之后的IDFA和IDFV,如果拿IDFA或IDFV+KeyChain作为解决方案,则容易混淆用户Id。而UUID是我们自己调用iOS相关API生成的,和App的相关性不是特别大,并且我们希望它永远不变。所以我个人认为UUID更适合作为唯一Id。

 

UUID+KeyChain

iOS相关API可以生成UUID,KeyChain使用第三方的封装:BCCKeychain

代码如下:

- (NSString*) GetUUID{
    //userName
    NSString* userName = @"UUID";
    //serviceName
    NSString* serviceName = [[NSBundle mainBundle]bundleIdentifier];
    
    //KeyChain中是否能拿到UUID
    NSString* uuid = [BCCKeychain getPasswordStringForUsername:userName andServiceName:serviceName error:nil];
    if (uuid == nil){
        //拿不到,生成一个UUID
        uuid = [[NSUUID UUID] UUIDString];
        
        int retryCount = 0;
        int maxRetryCount = 3;

        //UUID存储
        while (![BCCKeychain storeUsername:userName andPasswordString:uuid forServiceName:serviceName updateExisting:false error:nil] && retryCount < maxRetryCount) {
            ++retryCount;
        }
    }
    
    return uuid;
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注