The Bluetooth API family of uni-app is marked as not supported on HarmonyOS Next: no error is reported, no callback is given, the page is stuck in initialization, and the button looks “no response when clicked”. Our complementary solution is three-layered: the UTS plug-in encapsulates @kit.ConnectivityKit, the front-end gasket is disguised as a Bluetooth API with the same name and signature as uni, and then it is used as an injection host to be handed over to the pure JS manufacturer SDK – not a single line of protocol logic is transplanted. What really takes time is three things: There are 4 copies of the same plug-in logic in the project and are out of sync; the native event listener registration and annotation are not paired, causing random ghost failures of “the connection is successful but the connection fails”; the persistent deviceId is the broadcast address derived during the scanning period, which will become invalid when the device is restarted. Attached is the ArkTS compilation error comparison table, cross-bridge ArrayBuffer processing, write queue, hilog collection, and 9-item self-check list before using it on a real machine.
Targeted at: Developers who want to use uni-app to adjust BLE on HarmonyOS Next.
This article only talks aboutPlatform infrastructure(Plug-ins, gaskets, compilation chains, life cycle defects), does not involve any specific device protocols; device side docking (handshake, data analysis, reading storage), see the second part of the series.
All package names, AppIDs, plug-in names, and paths in this article are placeholders or proxies.
TL;DR
- The Bluetooth API family of uni-app is listed in the official compatibility table of HarmonyOS Next.
x(not supported), callNever call backthe performance is “no response when clicking the button”, and it is not a permission issue. - We use UTS plug-in package layer
@kit.ConnectivityKitand then use one withuniBluetoothAPI Same name and signatureThe shim is injected into the manufacturer’s SDK, so that not a single line of protocol code is ported. - The real pitfall is not that “API does not exist”, but in three places:There are 4 copies of the product that are out of sync.、Native listener registration/annotation is not paired、Persistent deviceId will rot。
- The whole process has been run and returned on a real machine. All ArkTS compilation errors encountered have definite solutions. This article provides a comparison table.
1. Phenomenon: three illusions “like permission issues”
When I click “Start Search” on the device list page, there is no response on the Hongmeng terminal; on the Android terminal, it is normal. The three hypotheses in my mind at that time were all falsified:
| hypothesis | Evidence collection method | in conclusion |
|---|---|---|
| Changes are not synchronized into the package | Compare on device www/app-service.js Build fingerprint + build intermediates |
Already included in the package |
| Conditional compilation strips the code | Check the corresponding product #ifdef branch string count |
branch correct |
| Bluetooth permission not declared | Check inside the bag module.json Does it contain ACCESS_BLUETOOTH |
Declared |
And the click did reach JS – the page popped up with the “Bluetooth initialization timeout” tip we added ourselves.
Troubleshooting experience:when await When an API never returns and your code link has been proven to be in the package, be suspicious firstLack of platform capabilitiesrather than a bridging bug.
2. Root cause: lack of platform capabilities, not a bug
In the uni-app compatibility table of HarmonyOS Next, the following APIs are all marked as not supported:
openBluetoothAdapter / getBluetoothAdapterState
startBluetoothDevicesDiscovery / stopBluetoothDevicesDiscovery / onBluetoothDeviceFound
createBLEConnection / closeBLEConnection
getBLEDeviceServices / getBLEDeviceCharacteristics
writeBLECharacteristicValue / notifyBLECharacteristicValueChange / onBLECharacteristicValueChange
The failure pattern is quiet:No error reporting, no callback,success/fail/complete None of the three families came. then await Hangs, the page is stuck on initialization, and the buttons look “unresponsive”. Complementing abilities must be done natively.
3. Selection: Three options, choose the most economical one
| plan | practice | cost |
|---|---|---|
| A. Migrate the protocol to native | Rewriting device protocols and state machines in ArkTS | The workload is the largest, and the logic of the two protocols at both ends has been bifurcated for a long time. |
| B. Waiting for official support | none | Uncontrollable |
| C. Add a layer of API with the same name | UTS plugin provides native primitives + frontend shim disguised as uni BluetoothAPI |
A one-time investment, the business code is almost unchanged |
The key prerequisite for choosing C is to first read the implementation of the manufacturer’s SDK:
// node_modules/-bluetooth-sdk/package.json
"dependencies": {} // 无原生依赖
// 内部调用形态
uni.createBLEConnection(...) // 蓝牙能力全部来自「注入进来的宿主对象」
SDK isPure JSand the Bluetooth API host isParameter injectionof:
adapter.createBLEConnection(deviceId, callback, host, timeout, authParams)
// ^^^^ 原来传 uni,现在可以传任何东西
Just provide a and uni For objects with the same name and signature of the Bluetooth subset, the protocol logic (handshake, instructions, unpacking) is reused as it is.This is the foundation of the entire plan。
4. Structure: three layers, each responsible for one thing
业务层 useXxxScan.ts / useBluetoothDevice.ts
│ bleApi(条件编译赋值)
│ #ifdef APP-HARMONY → bleApi = harmonyBle
│ #ifndef → bleApi = uni
▼
垫片层 utils/bluetooth/harmonyBle.ts
与 uni 蓝牙 API 同名、同签名({ success, fail, complete } + errCode)
负责:桥接语义归一(广播数据、二进制)+ 插件缺失时显式 fail
│ uni.hbXxx(...) 扁平属性访问
▼
原生层 uni_modules/harmony-ble/utssdk/app-harmony/index.uts
@kit.ConnectivityKit:access.getState / ble.startBLEScan /
ble.createGattClientDevice / device.writeCharacteristicValue ...
4.1 Platform indirection layer: two const Will compile and report an error
conditionally compiledoriginalwill be seen together by tsc/IDE, and the two branches will const bleApi “Cannot redeclare block scope variable” will appear. The correct way to write it is to declare it once and assign the value in the branch:
let bleApi: any;
// #ifdef APP-HARMONY
import { harmonyBle } from '@/utils/bluetooth/harmonyBle';
bleApi = harmonyBle;
// #endif
// #ifndef APP-HARMONY
bleApi = uni;
// #endif
Two other small pits:
APP-HARMONYandAPP-PLUSyesjuxtaposeRelationship, not inclusion. Originally written in#ifdef APP-PLUSThe authorization logic in Hongmeng will be completely stripped off.- from
@/uni_modules/Name the path to import the UTS plug-in in our offline build projectNot confirmed to be available(See §6 for details), and finally changed to flat attribute access.
4.2 Shims: Better to fail explicitly than to timeout silently
The most important thing about gaskets is not forwarding;Total failure: When the native plug-in is not included in the package, attribute access will be obtained undefinedif called directly, it will be “stuck without callback”. So detect first and then call back:
const ERR_PLUGIN_MISSING = 10002;
function call(flatName: string, opts: any) {
const f = (uni as any)[flatName];
if (typeof f !== 'function') {
const errMsg = `harmonyBle.${flatName}:fail native plugin not installed`;
console.log('[BLE]', errMsg);
opts?.fail?.({ errMsg, errCode: ERR_PLUGIN_MISSING });
opts?.complete?.({ errMsg, errCode: ERR_PLUGIN_MISSING });
return;
}
f(opts);
}
What this code gets is:The fault changed from “metaphysical timeout” to “a readable reason in the log”. The entire troubleshooting cycle was thus shortened by half.
5. ArkTS will teach you how to be a good person: Compilation error comparison table
.uts It needs to be compiled into ArkTS on the Hongmeng side, and ArkTS is TypeScript.restricted subset. The causes and solutions of the first 12 compilation errors:
| Report an error | root cause | solution |
|---|---|---|
Indexed signatures are not supported(arkts-no-indexed-signatures) |
ArkTS disables index signatures,[key: string]: any The ____ does not work |
In the interfaceExplicit declaration one by oneTransparent transmission fields:deviceId?/serviceId?/characteristicId?/value?: ArrayBuffer |
not assignable to string index type 'Object' |
Same as above | Same as above |
Property 'advData' does not exist on type 'ScanResult' |
The field name was guessed wrong and the original broadcast was called data |
advertisData: r.data |
Property 'createBLEDevice' does not exist on type 'typeof ble' |
Wrong memory of factory function name | ble.createGattClientDevice(deviceId) |
Use explicit types instead of "any"(arkts-no-any-unknown) |
The previous API does not exist, causing the return value to be pushed to any ofjointly wrong |
It will automatically disappear after modifying the real API name; it will be empty if entered. undefined guard rather than as string |
Namespace 'ble' has no exported member 'ConnectionState' |
Event callback type names are different | (state: ble.BLEConnectionChangeState) => ... |
Property 'STATE_CONNECTED' does not exist on type 'typeof BluetoothState' |
BundleAdapter switch enumerationTake it asConnection status enum | state.state === constant.ProfileConnectionState.STATE_CONNECTED |
Three methodologies:
- All signatures must be checked on this machine
.d.tsdon’t write based on impressions. Location:bucket file/sdk/default/openharmony/ets/api/@ohos.bluetooth.*.d.ts ets/kits/@kit.ConnectivityKit.d.ts。 anyError reports are often a chain reaction of incorrectly written upstream API names.first fix the “member does not exist” at the top level..utsFiles usuallyNo native lint supportgrammatical legitimacy can only rely onassembleHarExposed – so the first step is to run the compilation chain.
By the way, there is an ArkTS closure restriction:device.off('BLEConnectionStateChange', listener) quoted here listener itself will run into self-reference problems, and off(type)(Without callback) will remove all listeners for the event – as long as the only registered party for the event in the process is the plug-in itself, it is cleaner to use the callback-free form.
6. How to turn front-end functions into native functions: four registrations + two forms
The UTS plug-in is not finished yet .uts can be uni. Tune in. Offline (without HBuilderX cloud packaging) requiredCollaborate everywhere:
1) 把插件编成 har → <原生工程>/libs/uni_modules__harmony_ble.har
2) 声明依赖 → entry/oh-package.json5
"@uni_modules/harmony-ble": "../libs/uni_modules__harmony_ble.har"
3) 注册挂载 → entry/src/main/ets/uni_modules/index.generated.ets
import { openAdapter, ... } from '@uni_modules/harmony-ble'
Reflect.set(uni, 'hbOpenAdapter', openAdapter) // ×14
4) 构建期登记 → standalone-ext-apis.json
{ "type": "extapi", "plugin": "harmony-ble", "apis": [14 个 hbXxx], "version": "..." }
Two points that must be remembered:
index.generated.etsIn many projects it isManually maintained superset(Registered verify/push/payment/share…).publishThe generated version only contains the plug-ins that participated in the compilation this time.Full coverage will silently drop other pluginsbe sure to add them one by one.- Step 3 has two forms, depending on how the front-end consumes:
- flat mount:
Reflect.set(uni, 'hbOpenAdapter', fn),front enduni.hbOpenAdapter(...)Pure attribute access. ArkTS friendly (avoids anonymous object literal restrictions), and the front end can detect when the plug-in is missingundefined。 - Plug-in registration:
uni.registerUTSPlugin('uni_modules/harmony-ble', ns)cooperate with the front-endimport from '@/uni_modules/harmony-ble'. The dev project follows this path, but it is different from the running model of our offline main project——dev running through does not mean that your call can pass。
- flat mount:
our api 数量一致性 Relying on a constraint:standalone-ext-apis.json The number of entries must equal Reflect.set number of rows (currently 14 each). When adding the API, all the changes were made at the same time, and the one thing missing was “Compiled and run 10002”.
7. ArrayBuffer cross-bridge: byteLength will be lost
ArkTS ArrayBuffer Throw it directly to JS, and it often becomes {}(byteLength lost). The plan isJust walk across the bridge Array:
// 原生侧:ArrayBuffer → Array
function ab2arrForJs(ab: ArrayBuffer): Array {
const u8 = new Uint8Array(ab);
const out: Array = [];
for (let k = 0; k < u8.length; k++) out.push(u8[k]);
return out;
}
// JS 侧垫片:Array → ArrayBuffer,并做语义对齐
function normalizeIncomingValue(v: any): ArrayBuffer | null {
if (v instanceof ArrayBuffer) return v;
if (Array.isArray(v)) { /* 逐字节 & 0xff 重建 */ }
// TypedArray/DataView 兜底:v 只是底层 buffer 上的一个窗口视图,
// 直接返回 v.buffer 会把整段内存写出去(长度、内容全错),必须按视图范围切片
if (typeof v.byteLength === 'number' && v.buffer != null) {
return v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength);
}
return null;
}
The last branch is the easiest to miss:View is not equal to its underlying buffer. The symptom of this bug is “the write command occasionally contains a few extra tail bytes”, which is extremely difficult to attribute.
8. Product consistency red line: 4 copies of the same piece of logic
There is no technical difficulty in this section, butIt cost us two rounds of real machine verification in vain. The same piece of plug-in logic exists in 4 copies on the machine at the same time:
① src/uni_modules/harmony-ble/utssdk/app-harmony/index.uts ← 你在这里改代码
② dist/dev/app-harmony/.../app-harmony/index.ets ← har 的真实编译源
③ <原生工程>/libs/uni_modules__harmony_ble.har ← 打进包的东西
④ 设备上的 www / hap ← 实际运行的东西
② It is UTS→ArkTS translation + manual correction version(object literal to UTSJSONObjectMap value conversion UTS.mapGet wait). If you only change ①, the har will always have the old logic; conversely, the IDE’s “Run to Hongmeng” will use ① to re-translate and overwrite ②, and manual corrections may be lost after overwriting.Changes must be semantically synchronized on both sides ①②.
besides www double source trap: GUI package export and CLI export appResource fell into two different directories respectively, and brought /MIR The sync script uses one of these as a source – running it in the wrong direction willPut the new bag back into the old bag. Our approach is to hard-code a dedicated script for each product chain and do not allow manual combination of commands.
Verify that compressed products cannot be searched by text:.har is a compressed archive, on a har file grep/findstr The key identifier must have zero hits – zero hits only means “no decompression”, not “no such logic”.
tar -xzf uni_modules__harmony_ble.har -C tmp_verify
# 在解出的 modules.abc(字节码)里做字符串计数
The same is true for front-end JS products: compression will rename variables, so pressSemantic fingerprintJudgment (string constant, calling form), not variable name.
General Lesson: Yes
har/jar/aar/zipThis type of compressed product is used to verify “whether the content contains my changes”. It must be unpacked first and then the bytecode is checked.
9. Three real flaws: from “ability to run” to “dare to use”
After the link was established, three random failures occurred. What they have in common is:The semantic level of packaging native events is wrong。
9.1 Ghost failure: The connection is successful, but “Connection failed” pops up
The initial implementationcalled this time success and fail Together, the closure enters the connection status monitoring:
// ❌ 初版
device.on('BLEConnectionStateChange', (state) => {
if (state.state === CONNECTED) options.success(...);
else if (state.state === DISCONNECTED) options.fail(...); // 监听从不解除
});
After the connection is established, the deviceAutomatically shut down after testingor the upper level takes the initiative disconnect()will be derived again DISCONNECTED ——So it triggeredThe original call that returned successfully fail. The upper layer resets the connection status and pops up “Connection Failed”. The symptoms are random and difficult to reproduce, and in the log, “success comes first and failure comes last.”
Repair mode issingle final state semantics:
// ✅ 只认首个终态,到终态立即解注
let settled = false;
const listener = (state: ble.BLEConnectionChangeState) => {
if (settled) return;
const connected = state.state === constant.ProfileConnectionState.STATE_CONNECTED;
const disconnected = state.state === constant.ProfileConnectionState.STATE_DISCONNECTED;
if (!connected && !disconnected) return; // CONNECTING 等中间态只记日志
settled = true;
clearConnTimer(deviceId);
device.off('BLEConnectionStateChange'); // 无 callback 形式,移除该事件全部监听
if (connected) {
options.success?.({ errMsg: 'createBLEConnection:ok' });
} else {
gattDevices.delete(deviceId); // 建连以断连收尾 → 回收实例,重连走全新流程
charListenerDevices.delete(deviceId);
clearWriteState(deviceId);
try { device.close(); } catch (e) { /* ignore */ }
options.fail?.({ errMsg: 'createBLEConnection:fail disconnected', errCode: 10003 });
}
};
at the same time:closeConnection insideAnnotate first and then disconnect();same deviceId Repeat the connection establishment and annotate and merge close() Old GattClient (otherwise the GATT connection leaks).
Transferable rules: Do not put request-level success/fail The callback is hung on a long-lived device event that will be triggered multiple times – subsequent legitimate events will backfire on the completed call. All gaskets/plug-ins (Bluetooth, serial port, socket) that encapsulate “connection class native events → Promise style callback” are applicable. In turn, the state callback API, which itself has continuous subscription semantics, should retain monitoring, but must be annotated in pairs.
By the way, there is a log interpretation pit:constant.ProfileConnectionState yes DISCONNECTED=0 / CONNECTING=1 / CONNECTED=2. When only printing the original enumeration value,state=1 It is very easy to read as “connected”, so we once looked the other way.
9.2 Listener registration is not paired: each broadcast/notification is dispatched K times
onDeviceFound / notifyCharacteristic Used for every JS callnew closureGo to register native onand the JS side callback is a single slot coverage. In ArkTS on Pass in different function referencesNot idempotentso each broadcast and each notification is dispatched multiple times – in addition to log flooding, there are alsoReport measurement values repeatedlybusiness risks.
Repair uses two types of guards:
// 扫描监听:模块级布尔,原生 on 只注册一次;off 时复位
let scanListenerRegistered = false;
export function onDeviceFound(callback: (res: any) => void) {
deviceFoundCb = callback; // 单槽派发
if (scanListenerRegistered) return;
scanListenerRegistered = true;
ble.on('BLEDeviceFind', (results) => { deviceFoundCb?.({ devices: /* 归一 */ }); });
}
// 特征监听:deviceId → boolean,同设备多特征(notify + indicate)共用一个原生监听
const charListenerDevices = new Map();
// off 系列必须做真正的原生 off,不能只把回调置 null
export function offDeviceFound() {
deviceFoundCb = null;
scanListenerRegistered = false;
try { ble.off('BLEDeviceFind'); } catch (e) { /* ignore */ }
}
offCharacteristicChange To iterate through registered devices one by one device.off back clear()——Only clearing JS side references is the most common false release.
9.3 Concurrent write rejected:The operation is busy
The manufacturer’s SDK will launch twice concurrently within a few milliseconds after the subscription is successful. writeBLECharacteristicValue(Handshake command + setting time), with 1200ms self-resend. Hongmeng GATT only allows one pending write at the same time, and concurrency is directly rejected by the native The operation is busy。
The solution is to add it at the plug-in layerSerial write queue by deviceIdand put the “CCCD write for subscription” into the same queue (the same GATT write channel):
type BleWriteTask = (next: () => void) => void;
const writeQueues = new Map>();
const writeBusy = new Map();
const WRITE_TIMEOUT_MS = 5000;
function flushWrite(deviceId: string) {
if (writeBusy.get(deviceId) === true) return;
const q = writeQueues.get(deviceId);
if (q == null || q.length === 0) return;
const task = q[0]; q.shift();
writeBusy.set(deviceId, true);
task(() => { writeBusy.set(deviceId, false); flushWrite(deviceId); }); // 结算后放行下一个
}
function enqueueWrite(deviceId: string, task: BleWriteTask) {
let q = writeQueues.get(deviceId);
if (q == null) { q = []; writeQueues.set(deviceId, q); }
q.push(task);
flushWrite(deviceId);
}
Two details:
- A single write must have a timeout. If the native Promise is not resolved, it will permanently occupy the queue of the device.
- In the timeout branch
next()to be placedfinally. upper layerfailThe queue must also be released even if the callback throws an exception, otherwise subsequent write tasks to the device will be permanently queued.
give at the same time createConnection Plus input parameters timeout respect (SDK transfer 20000no time limit by default):
const timeoutMs: number = options.timeout == null ? 0 : options.timeout as number;
if (timeoutMs > 0) {
const timer = setTimeout(() => {
if (settled) return;
settled = true;
connTimers.delete(deviceId);
try { device.off('BLEConnectionStateChange'); } catch (e) { /* ignore */ }
gattDevices.delete(deviceId); charListenerDevices.delete(deviceId); clearWriteState(deviceId);
try { device.disconnect(); device.close(); } catch (e) { /* ignore */ }
options.fail?.({ errMsg: `createBLEConnection:fail timeout ${timeoutMs}ms`, errCode: 10003 });
}, timeoutMs);
connTimers.set(deviceId, timer); // 终态 / closeConnection / 重复建连 三处都要 clearConnTimer
}
connTimers The three cleanups are not defensive redundancies –The timer goes back and triggers fail, which is the second entrance to the ghost failure in 9.1。
The real machine returns after repair:The operation is busy 0 times,connect fail 0 times, 0 late callbacks, and no repeated notifications.
10. deviceId is perishable data
This is the most hidden pit, becauseThe symptoms are exactly the same as “device not turning on”。
Hongmeng side ble.createGattClientDevice(deviceId) Used deviceId yesBroadcast address derived during scan periodafter the device is restarted or the address is rotated, the old value stored will become invalid immediately. The reconnection code often looks like this:
// ❌ 只 openAdapter + 取本地存储的 deviceId 直连,从不扫描
const ok = await connectDevice(storedDevice);
After the failure, GATT has been stuck at CONNECTINGwill not reply until the system times out in ~30s createBLEConnection:fail disconnected(The 4 actual measurements were all between 30.05~30.14s). If the failure prompt is commented out again, all the user will see is “no response after clicking”. Android the same path is available because the system holds the bond/address cache –This is a platform difference, not a code difference。
We observed 4 address patterns on one device within a day:
| form | type |
|---|---|
FE:79:xx:xx:xx:29 |
Random static address |
0C:35:xx:xx:xx:FC |
public address |
5E:F1:xx:xx:xx:20 |
Resolvable Private Address (RPA) |
02:1E:xx:xx:xx:5B |
Another rotation value |
Conclusion: Never assume that a persistent deviceId will be available long-term.
Repair three-piece set (front end side, immobile har):
const HARMONY_CONNECT_WATCHDOG_MS = 12000; // 不等系统 30s,主动收敛
const HARMONY_REFRESH_SCAN_MS = 10000;
// 1) 看门狗:超时即 closeBLEConnection 并 resolve(false),配 settled 忽略迟到回调
const watchdog = setTimeout(() => {
if (settled) return;
settled = true;
bleApi.closeBLEConnection({ deviceId: device.deviceId });
resolve(false);
}, HARMONY_CONNECT_WATCHDOG_MS);
// 2) 直连失败后扫一轮拿新鲜地址,再重连一次;成功后由 saveBoundDevice 写回存储
const fresh = await scanForFreshDevice((name) => prefixInName(deviceType.bleNamePrefix, name));
if (fresh && fresh.deviceId !== boundDevice.deviceId) {
return connectDevice({ ...device, deviceId: fresh.deviceId }, deviceType);
}
// 3) 页面 onShow 的自动重连走 silent 分支:不扫描、不打扰用户
Two other lessons:
- Failure prompt must be visible,butOnly pop up once at the page level–according to
productCodeWhen connecting in a loop, the toasts in the composable will be played several times in succession. - Determining “whether the failure reason is due to an expired address” depends on the scan results: if the scanned
deviceIdIt is consistent with the binding record, indicating that there is another reason for the failure.Don’t blindly retry。
Actual measured timing: watchdog triggers in 11.8s → scans for 384ms and hits new address → establishes connection state=2 Only 0.4~1s → SDK connect success, the new address is written back to the storage, and the subsequent 7 connection establishments use the new address directly and succeed. When the device is really shut down, the scan will time out after 10 seconds and the message “Device not scanned” will be returned and only prompted once.
11. Permissions: Statement + Active application, two things
Native module.json5 Three permanent posts:
| Permissions | type | Remark |
|---|---|---|
ohos.permission.USE_BLUETOOTH |
system_grant | Just declare |
ohos.permission.DISCOVER_BLUETOOTH |
system_grant | Just declare |
ohos.permission.ACCESS_BLUETOOTH |
user_grant | need reason + usedScene,andMust be applied at runtime |
uni.openBluetoothAdapter Hongmeng will not open the authorization box for you, so the plug-in openAdapter Apply proactively before:
export function requestBluetoothPermission(success: (granted: boolean) => void) {
const atManager = abilityAccessCtrl.createAtManager();
const perms: Array = ['ohos.permission.ACCESS_BLUETOOTH'];
atManager.requestPermissionsFromUser(getContext(), perms)
.then((r) => success(r.authResults.length > 0 && r.authResults[0] === 0))
.catch(() => success(false));
}
getContext() Get the host context of the current window:
function getContext(): common.UIAbilityContext {
return UTSHarmony.getCurrentWindow()!.getUIContext().getHostContext() as common.UIAbilityContext;
}
By the way, there is a pitfall in building baselines: the API version in which har is compiled in the project will have that minimum version requirement. If the main project compatibleSdkVersion below it,assembleHap Will be reported in the MergeProfile stage 00306004 Specification Limit Violation. Either upgrade the main project baseline in a unified manner (need to check at the same time compatibleSdkVersionStage (with signing certificate), or drop the plug-in to the target API and reprogram——Pick one, don’t mix them。
12. Real machine logs: tags, encoding and lost logs
hdc tconn <设备IP:端口> # 无线连接,掉线需重连并授权
hdc shell hilog -T JSAPP # 前端 console.log 落在 JSAPP tag
hdc shell hilog -Q pidoff # 需要时关闭流控
Three details that will waste you half a day:
- tag is not
chromiumNeitherEntryAbility,yesJSAPP. Before applying your own log prefix grep, pressJSAPPFiltering – “Not a single front-end log could be found” before, because there was no filtering + buffer rotation. - PowerShell
*>Redirects will be encoded twice(UTF-8 is read by GBK and then written into UTF-8). To search Chinese, you must firstUTF8 读 → GetEncoding(936).GetBytes → UTF8.GetStringRestore; used when the file is occupied by the collection processFileStream(..., 'ReadWrite')Shared reading.Counts are based on ASCII keywordsfull-width punctuation restoration may lose bytes. - hilog has rate limithigh-frequency application logs will beDiscard the entire section. Don’t think “it’s not in the log” as “it didn’t happen”;
Start-Process -RedirectStandardOutputThe collection child process started may also be recycled along with the parent process, leaving a 0-byte file.
13. Impact area and rollback
To Fei Hongmeng Endzero impact:bleApi = uni Branching makes the behavior of Android / iOS / H5 / applet exactly the same as before the change.
To temporarily disable Hongmeng Connect, just change #ifdef APP-HARMONY inside bleApi = harmonyBle Change back bleApi = uni(Return to the old behavior of “API not supported, card initialization”).
| Rollback scope | operate | Notice |
|---|---|---|
| front end | bleApi one line |
Hot update (only change www) will take effect |
| plug-in | Roll back old har → delete oh_modules with two oh-package-lock.json5 → ohpm install --all → assembleHap |
has 与 standalone-ext-apis.json、index.generated.ets Must roll back to the same version |
| whole pack | Reinstall hap | Overwrite the installation and retain login status and device binding records;hdc uninstall It will be lost, please pay attention when scheduling |
Another thing: WGT/hot update can only carry www.Unable to host new UTS plug-ins. Changes to the plug-in layer must be made in the entire package.
14. Self-check list before using the real machine
15. Summary
The really difficult part of this matter is not “finding out that Hongmeng does not support the uni Bluetooth API” – the conclusion can be reached in five minutes by looking up the table. The hard part is the next three steps:
- Let the matter of supplementing capabilities not affect the existing business: Same name and same signature shim + injection point replacement, protocol logic zero migration.
- Let the changes really be included: Consistency of four copies, and meta-rules such as “compression products cannot be text verified”.
- Make the link stable enough to be handed over to users: Single final state semantics, paired annotations, write queues, and address refreshes – the four repairs are not complicated, but their absence manifests itself as random failures, and random failures are the most costly in real machine troubleshooting.
The second article will write about the device itself: why the authentication of the manufacturer’s SDK only responds with “Authentication failed”, why Hongmeng’s broadcast data requires manual parsing of the AD structure, and the process data from package to package to the three numbers of blood pressure and pulse in the form.
Attached: Desensitization instructions for this article (delete this section before publishing)
| true value | In-text treatment |
|---|---|
The real plug-in name and its uni. Mount prefix, har file name |
Uniformly change to neutral pronouns harmony-ble / hbXxx / uni_modules__harmony_ble.har(There is no real identification in the text) |
Front-end project name, native project name, package name com.*、DCloud AppID、bundleName |
delete or change <原生工程> / / <设备IP:端口> placeholder |
Manufacturer SDK package name, device model,appKey / appIdentifier and its background error code |
This article only writes “Manufacturer SDK (Pure JS)”; the manufacturer-related information is left to the second article, and no real values appear. |
| Real machine complete MAC / deviceId four forms | The middle byte ends with xx Mask, only the first and last bits are retained to distinguish address types. |
Certificate absolute path,keyPassword / storePassword |
Not citedand please do not src/manifest.json Copy to any external document |
Inside the warehouse scripts/ The specific build script name under |
Without naming names, it was changed to “write dedicated scripts for each product chain.” |
specific @dcloudio Version number, UniAppRuntime version, measured readings |
Did not appear (the reading belongs to the second article and must be marked as a self-test sample) |
Before publishing, it is recommended to search the full text again: AppID, package name,hdc tconn After the IP,C:\Users\<你> Path, company domain name.