# ElectronODIDAsset **Repository Path**: scenario-samples/electron-odidasset ## Basic Information - **Project Name**: ElectronODIDAsset - **Description**: 获取设备唯一标识符是PC应用高频使用场景之一,以便开发者提供个性化服务。 本示例主要基于使用Electron框架开发PC应用情况下,通过HarmonyOS Electron调用ets方法,使用关键资产存储服务保存ODID(开发者匿名设备标识符)实现处恢复出厂设置外,设备唯一标识符。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-01-22 - **Last Updated**: 2026-01-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Electron实现获取设备唯一标识符 ## 场景介绍 获取设备唯一标识符是PC应用高频使用场景之一,以便开发者提供个性化服务。 本示例主要基于使用Electron框架开发PC应用情况下,通过[HarmonyOS Electron调用ets方法](https://devcloud.cn-north-4.huaweicloud.com/codehub/project/b19f5ea8ffd4492ea8c06ca2ebf3f858/codehub/2834876/home?ref=hw%2Fchromium%2Fohos%2F132.0.6834.161-electron-release&filePath=src%252Fdocs%252Fohos),使用[关键资产存储服务](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/asset-store-kit-overview)保存[ODID](https://developer.huawei.com/consumer/cn/doc/HMSCore-Guides/odid-0000001051063255)(开发者匿名设备标识符)实现处恢复出厂设置外,设备唯一标识符。 ## 效果预览 ![odid.gif](screenshots/odid.gif) ## 工程目录 ``` ├──odid-asset-addon // addon实现 │ ├──odid_adapter │ | ├──CMakeLists.txt │ │ ├──demo.cc │ │ ├──odid_adapter.cc │ │ └──odid_adapter.h │ └──odid_addon │ ├──addon.cc │ ├──CMakeLists.txt │ ├──package.json │ ├──setAttrWorker.cpp │ └──setAttrWorker.h ├──library // ArkTS接入 │ ├──src/main │ │ ├──cpp/types/libodid_adapter │ │ │ ├──Index.d.ts │ │ │ └──oh-package.json5 │ │ ├──ets │ │ │ ├─interface │ │ │ │ └──interface.ts │ │ │ └─utils │ │ │ └──JSBindingEts.ets │ │ └──module.json5 │ ├──Index.ets │ └──oh-package.json5 └──app // 主页面 ``` ## 实现思路 1. ArkTS侧: - 获取ODID: 查询关键资产是否存在设备唯一标识符,如果不存在,则通过[deviceInfo.ODID](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-device-info)获取。考虑到开发者一般需要在获取设备标识符后执行某些操作,该行为为阻塞性的,这里使用同步函数实现。 ```ts static getODID() { let query: asset.AssetMap = new Map(); query.set(asset.Tag.ALIAS, JSBindingEts.stringToArray('my_device_id')); // 指定关键资产别名,最多查询到一条满足条件的关键资产,使用时最好拼接自己的项目标识 query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL); // 此处表示需要返回关键资产的所有信息,即属性+明文 try { const res: Array = asset.querySync(query); // 解析密钥 let secret: Uint8Array = res[0].get(asset.Tag.SECRET) as Uint8Array; // 将uint8array解析为字符串 let secretStr: string = JSBindingEts.arrayToString(secret); return secretStr; } catch (error) { console.error(`Failed to query Asset. Error code: ${error.code}, message: ${error.message}. `); return deviceInfo.ODID; } } ``` - 设置关键资产: ODID可能在设备上该开发者的所有应用都卸载时重置,为确保即使在应用卸载后仍能保证获取的设备标识符不发生变化,将ODID设为关键资产。应用卸载时,[IS_PERSISTENT](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-asset#tag)属性设置为true的数据将保留。 ```ts static setODIDAttr(id: string, callback: (ret: boolean) => void) { let attr: asset.AssetMap = new Map(); attr.set(asset.Tag.SECRET, JSBindingEts.stringToArray(id)); attr.set(asset.Tag.ALIAS, JSBindingEts.stringToArray('my_device_id')); attr.set(asset.Tag.IS_PERSISTENT, true); asset.add(attr).then(() => { console.info(`Asset added successfully.`); callback(true); }).catch((error: BusinessError) => { console.error(`Failed to add Asset. Error code: ${error.code}, message: ${error.message}.`); callback(false); }); } ``` > 完成一次编译后,在不改变ArkTS侧函数名称、返回值、返回时机、参数数量、参数类型的情况下,可任意更改函数实现,无需重新编译adapter和addon。否则有任何其他更改,都需要重新编译。 2. adapter侧: - 获取ODID: 在Native侧通过aki调用ArkTS侧注册的`getODID`方法直接获取odid。 ```cpp std::string GetODID() { if (auto jsFunc = aki::JSBind::GetJSFunction("getODID")) { auto odid = jsFunc->Invoke(); return odid; } return ""; } ``` - 设置关键资产: 设置关键资产方法`setODIDAttr`为异步函数,cpp侧将异步回调接口同步化,阻塞等待异步操作结果。定义回调函数callback,捕获promise的引用,`setODIDAttr`异步执行完成时回调触发,通过`set_value`存储操作结果。 ```cpp bool SetODIDAttr(std::string& odid) { std::promise promise; auto future = promise.get_future(); std::function callback = [&promise](bool ret) { promise.set_value(ret); }; if (auto jsFunc = aki::JSBind::GetJSFunction("setODIDAttr")) { jsFunc->Invoke(odid, callback); } else { promise.set_value(false); } return future.get(); } ``` 3. addon侧: - 获取ODID: 调用上述adapter侧方法,直接获取ODID字符串转换为N-API字符串对象返回给JS层。 ```cpp static Napi::String GetODID(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); std::string odid = ODIDAdapter::GetODID(); return Napi::String::New(env, odid); } ``` - 设置关键资产: 获取odid参数,将N-API字符串(JS字符串)转换为C++ std::string,创建自定义异步Worker实例,加入N-API异步任务队列,底层会自动启动后台线程执行异步操作,不阻塞。返回与Worker绑定的Promise对象,JS层可直接接收该Promise并处理异步结果。 ```cpp static Napi::Value SetODIDAttr(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsString()) { Napi::TypeError::New(env, "需要传入一个字符串参数 odid").ThrowAsJavaScriptException(); return env.Undefined(); } std::string odid = info[0].As().Utf8Value(); SetAttrWorker* worker = new SetAttrWorker(env, odid); worker->Queue(); return worker->Promise(); } ``` 4. Electron侧(js): - 获取ODID: 引入上述编译的node后,直接通过`getODID`获取ODID字符串。 ```js function getODID() { return odid_addon.getODID(); } ``` - 设置关键资产: 引入上述编译的node后,直接通过`setODIDAttr`获取Promise对象,这里可以通过`.then`执行异步回调,也可以在渲染进程中通过ipc通信透传Promise执行`.then`。 ```js function setODIDAttr(odid) { return odid_addon.setODIDAttr(odid); } ``` ## 使用说明 1. 将示例代码中`odid-asset-addon`文件夹传到编译器上(推荐Ubuntu22.04),参考[HarmonyOS Electron调用ets方法](https://devcloud.cn-north-4.huaweicloud.com/codehub/project/b19f5ea8ffd4492ea8c06ca2ebf3f858/codehub/2834876/home?ref=hw%2Fchromium%2Fohos%2F132.0.6834.161-electron-release&filePath=src%252Fdocs%252Fohos)拉取Electron源码并配置环境变量后,在该`odid-asset-addon`目录中**下载aki并编译**(上述文档步骤一)。 进入`odid_adapter`目录,修改`CMakeLists.txt`中的`ELE_PATH`后,执行**编译adapter**操作(上述文档步骤二、4)。 进入`odid_addon`目录,修改`CMakeList.text`中的`ELE_PATH`及`libshim.a`路径后,直接`npm install`**编译addon**(上述文档步骤三、2)。 > 除该步骤在Ubuntu上进行外,都在Windows/MacOS等开发机上进行。 2. 获取[HarmonyOS Electron工程](https://devcloud.cn-north-4.huaweicloud.com/codehub/project/b19f5ea8ffd4492ea8c06ca2ebf3f858/codehub/2821214/home?ref=electron34-release)解压至`ohos_hap`,使用DevEco Studio打开。 将示例代码中`library`文件夹放入`ohos_hap`根目录中,并修改`web_engine`模块的`oh-package.json5`文件的`dependencies`,通过`"library": "file:../library"`引入`library`模块。 并在`web_engine/src/main/ets/application/WebAbilityStage.ets`中初始化`JSBindingEts`并绑定(上述文档步骤四、7)。 3. 将步骤1编译获得的`libaki_jsbind.so``libodid_adapter.so``odid_addon.node`放在`electron/libs/arm64-v8a`下。 4. 将示例代码中`app`文件夹替换`web_engine/src/main/resources/resfile/resources/app`后,运行工程。 ## 约束与限制 * 本示例支持API Version 20 Release及以上版本。 * 本示例支持HarmonyOS 6.0.0 Release SDK及以上版本。 * 本示例需要使用DevEco Studio 6.0.0 Release及以上版本进行编译运行。 ## 权限说明 允许应用存储持久化的数据:[ohos.permission.STORE_PERSISTENT_DATA](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/permissions-for-all#ohospermissionstore_persistent_data) ## 参考文档 [HarmonyOS Electron调用ets方法](https://devcloud.cn-north-4.huaweicloud.com/codehub/project/b19f5ea8ffd4492ea8c06ca2ebf3f858/codehub/2834876/home?ref=hw%2Fchromium%2Fohos%2F132.0.6834.161-electron-release&filePath=src%252Fdocs%252Fohos) [通过API获取常见设备标识符并实现标识符不发生变化](https://developer.huawei.com/consumer/cn/doc/architecture-guides/architecture-v1-3_2-ts_8-0000002353684201) [ODID](https://developer.huawei.com/consumer/cn/doc/HMSCore-Guides/odid-0000001051063255) [关键资产存储服务](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/asset-store-kit-overview) [@ohos.deviceInfo](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-device-info)