HarmonyOS开发5.0【埋点-获取系统信息】

背景

客户端埋点往往需要获取设备的基本信息,比如手机型号、手机系统信息、设备号等等,每次埋点上报都带着这些基本信息,以方便定位用户、识别用户的设备、操作系统等,从而最终帮用户解决问题。鸿蒙提供了封装好的api方便获取手机相关的信息,但是类似安卓或者ios设备的idfa\idfv是不提供的。而相应的替代是 AAID 或者 OAID。

AAID

AAID(Anonymous Application Identifier):应用匿名标识符,标识运行在移动智能终端设备上的应用实例,只有该应用实例才能访问该标识符,它只存在于应用的安装期,总长度36位。与无法重置的设备级硬件ID相比,AAID具有更好的隐私权属性。

AAID具有以下特性:

匿名化、无隐私风险:AAID和已有的任何标识符都不关联,并且每个应用只能访问自己的AAID。 同一个设备上,同一个开发者的多个应用,AAID取值不同。 同一个设备上,不同开发者的应用,AAID取值不同。 不同设备上,同一个开发者的应用,AAID取值不同。 不同设备上,不同开发者的应用,AAID取值不同。

AAID会在包括但不限于下述场景中发生变化:

应用卸载重装。 应用调用删除AAID接口。 用户恢复出厂设置。 用户清除应用数据。

OAID

开放匿名设备标识符,是一种非永久性设备标识符,基于开放匿名设备标识符,可在保护用户个人数据隐私安全的前提下,向用户提供个性化广告,同时三方监测平台也可以向广告主提供转化根因分析。

OAID具有以下特性:

OAID是设备级标识符,同一台设备上不同的App获取到的OAID值一样。 OAID的获取受应用的跟踪开关影响:当应用的跟踪开关开启时,该应用可获取到非全0的有效OAID;当应用的跟踪开关关闭时,该应用仅能获取到全0(00000000-0000-0000-0000-000000000000)的OAID。 同一台设备上首个应用开启应用跟踪开关时,会首次生成OAID。

OAID会在下述场景中发生变化:

用户恢复手机出厂设置。 用户操作重置OAID。

取舍

这两种能力各有优缺点。

  1. AAID 在许多场景中均会变化,但是它的使用并不需要用户授权,因此我们可以使用鸿蒙的关键资产功能使它在系统层面上进行缓存,这样也能一定程度上保证AAID的唯一和持久性
  2. OAID 虽然天生具有唯一和持久性,但是需要用户授权,如果用户拒绝授权,根本没有机会拿到它。
思路

我们当然想在应用初始化的时候拿到设备的唯一标识,因此我们可以设计这样的流程:

  1. 首先申请 OAID,如果用户授权则以此作为设备唯一标识,将其缓存到关键资产中
  2. 如果用户拒绝授权 OAID,那么我们使用 AAID 作为设备唯一标识,并将其缓存到关键资产中
  3. 其余时间我们均访问关键资产中的缓存,如果存在则直接返回,不存在则重复 1 和 2,并进行重新缓存
实现
  1. 构建SystemInfo静态类
export class SystemInfo {
  static deviceInfo: string = deviceInfo.displayVersion; // 产品版本
  static osInfo: string = deviceInfo.osFullName; // 系统版本
  static deviceVersion: string = deviceInfo.versionId; // 版本ID
  static deviceId: Promise<string> = SystemInfo.GetSystemDeviceId()

  constructor() {
    Logger.init()
  }
  
  // 获取oaid
  static async GetSystemOAID() {
   
  }

  // 缓存设备id为关键资产
  static SetSystemDeviceId(deviceId: string): Boolean   {
  
  }
  
  // 获取aaid
  static async GetSystemAAID(): Promise<string> {
  
  }

  // 读取关键资产缓存设备id
  static async GetSystemDeviceId(): Promise<string> {
   
  }
 
  // 根据定义逻辑获取设备id
  static async GetDeviceId() {
    
  }
}
  1. 实现oaid获取,如果取到则进行关键资产缓存
  static async GetSystemOAID() {
    try {
      // 首先需要获取用户授权
      const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
      let authRes = await atManager.requestPermissionsFromUser(getContext(), ["ohos.permission.APP_TRACKING_CONSENT"])
      if (authRes.authResults[0] == 0) {
        let oaid = await identifier.getOAID();
        SystemInfo.SetSystemDeviceId(oaid)
        return oaid
      } else {
        Logger.error('user rejected');
        return "";
      }
    } catch (err) {
      Logger.error(`Failed to get oaid by promise, catch error: ${err.code}, ${err.message}`);
      return "";
    }
  }

3.实现aaid获取,如果取到则进行关键资产缓存

  static async GetSystemAAID(): Promise<string> {
    try {
      let aaid = await AAID.getAAID()
      SystemInfo.SetSystemDeviceId(aaid);
      return aaid
    } catch (e) {
      let error = e as BusinessError;
      Logger.error(`Failed to get aaid ~ code: ${error.code} -·- message: ${error.message}`);
      return "";
    }
  }

4.实现deviceID获取。首先获取oaid,取不到再取aaid

 static async GetDeviceId() {
    let oaid = await SystemInfo.GetSystemOAID()
    if (oaid !== '' && oaid !== '00000000-0000-0000-0000-000000000000') {
      return oaid
    }
    let aaid = await SystemInfo.GetSystemAAID()
    return aaid
  }
  1. 实现关键资产缓存

  static stringToArray(str: string): Uint8Array {
    let textEncoder = new util.TextEncoder();
    return textEncoder.encodeInto(str);
  }



static SetSystemDeviceId(deviceId: string): Boolean {
    try {
      if (!canIUse("SystemCapability.Security.Asset")) {
        Logger.error(`Asset is not support`);
        return false;
      }
      let attr: asset.AssetMap = new Map();
      // 关键资产名称 DEVICE_ID
      attr.set(asset.Tag.ALIAS, SystemInfo.stringToArray('DEVICE_ID'));
      attr.set(asset.Tag.SECRET, SystemInfo.stringToArray(deviceId));
      attr.set(asset.Tag.SYNC_TYPE, asset.SyncType.THIS_DEVICE);
      attr.set(asset.Tag.CONFLICT_RESOLUTION, asset.ConflictResolution.OVERWRITE); //新增关键资产时的冲突,覆盖原有的关键资产
      attr.set(asset.Tag.IS_PERSISTENT, true); //在应用卸载时是否需要保留关键资产 这里需要授权声明 但不需要用户授权
      asset.addSync(attr);
      return true;
    } catch (err) {
      let error = err as BusinessError;
      Logger.error(`Asset-addSync device_id error ~ code: ${error.code} -·- message: ${error.message}`);
      return false;
    }
  }
  
  1. 关键资产读取
  static arrayToString(arr: Uint8Array): string {
    let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true })
    let result = textDecoder.decodeWithStream(arr, { stream: true });
    return result;
  }

  static async GetSystemDeviceId(): Promise<string> {
    try {
      if (!canIUse("SystemCapability.Security.Asset")) {
        Logger.error(`Asset is not support`);
        return "";
      }
      let query: asset.AssetMap = new Map();
      query.set(asset.Tag.ALIAS, SystemInfo.stringToArray('DEVICE_ID'));
      query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL);
      // **注意 这里如果关键资产不存在 并不会返回空 而是抛出24000002异常**
      const assetArr = asset.querySync(query);
      if (!assetArr || assetArr.length < 1) {
        return "";
      }
      let map: asset.AssetMap = assetArr[0]
      let deviceId = map.get(asset.Tag.SECRET) as Uint8Array;
      if (deviceId) {
        // 如果取到 直接返回
        return SystemInfo.arrayToString(deviceId);
      } else {
        // 未取到 重新获取并返回
        return await SystemInfo.GetDeviceId()
      }
    } catch (err) {
      let error = err as BusinessError;
      Logger.error(`Asset-getSync-异常 ~ code: ${error.code} -·- message: ${error.message}`);
      if (24000002 == error.code) {
        // 未取到 重新获取并返回
        return await SystemInfo.GetDeviceId()
      }
      return "";
    }
  }
授权

以上涉及的权限有关键资产 ohos.permission.STORE_PERSISTENT_DATA 和 OAID ohos.permission.APP_TRACKING_CONSENT,需要我们在module.json5中进行声明:

 "requestPermissions": [
      {
        "name": "ohos.permission.STORE_PERSISTENT_DATA"
      },
      {
        "name": "ohos.permission.APP_TRACKING_CONSENT",
        "reason": "$string:app_tracking_consent_reason",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "always",
        }
      }
    ]
调用

调用方式很简单,不过由于官方只提供了异步获取方法,所以我们的deviceId也需要异步获取

import { SystemInfo } from '../api/SystemInfo'

  let displayVersionInfo: string = SystemInfo.deviceInfo; // 产品版本
  let osFullNameInfo: string = SystemInfo.osInfo; // 系统版本
  let versionIdInfo: string = SystemInfo.deviceVersion; // 版本ID
  SystemInfo.deviceId.then(deviceId => {
    console.log(displayVersionInfo, ' | ', osFullNameInfo, ' | ', versionIdInfo, ' | ', deviceId)
  })
总结

我们可以通过多种方式来实现设备唯一标识的获取,具体实现可以根据我们的需要进行取舍,官方还提供了一个ODID标识(开发者匿名设备标识符),有需要的可以自行研究一下。
以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
1

除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下

内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!

鸿蒙【北向应用开发+南向系统层开发】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

2

为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!
3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值