RN 蓝牙
注意: 演示入口:React Native / Expo 参考 → react-native-demo
如果你的应用是纯 React Native 形态,可以用这里的 BLE 接入方式。它直接接入 @ukeyfe/hardware-ble-sdk 与 @ukeyfe/hardware-transport-react-native,无需 WebView,也不用额外编写底层传输插件;设备连上后,链 API 仍然按统一 SDK 方式调用。
部署
先把下面这些包加入应用工程,后续自动链接会把 BLE 原生模块带进 Dev Client 或正式安装包:
npm i @ukeyfe/hardware-ble-sdk @ukeyfe/hardware-transport-react-native \
@ukeyfe/react-native-ble-utils react-native-ble-plx \
buffer process react-native-get-random-values react-native-url-polyfill
在应用入口文件(例如 index.js 或 App.tsx)先加载 polyfill,再注册 RN transport 副作用:
// 先补齐运行时依赖,按你的 RN 项目实际情况取舍
import "react-native-get-random-values";
import "react-native-url-polyfill/auto";
// @ts-ignore
global.Buffer = global.Buffer || require("buffer").Buffer;
// @ts-ignore
global.process = global.process || require("process");
// 这行要放在 SDK init 之前,用来挂载 RN transport
import "@ukeyfe/hardware-transport-react-native";
import ukeySdk from "@ukeyfe/hardware-ble-sdk";
安卓配置
Android 12 及以上需要在 AndroidManifest.xml 放入这些权限声明:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
除了 Manifest 声明,运行时也要向用户请求 BLUETOOTH_SCAN 和 BLUETOOTH_CONNECT。有些系统还会把位置权限和系统定位开关一起纳入扫描条件,缺少其中任一项都可能导致列表为空。
iOS 目标配置
iOS 侧请把这些用途说明写入 Info.plist:
NSBluetoothAlwaysUsageDescription:蓝牙访问说明,必填。NSBluetoothPeripheralUsageDescription:旧版 iOS 仍可能读取,可按兼容需求保留。NSCameraUsageDescription:只有应用会用到相机,例如扫码时才需要。
Expo 项目可以把对应声明放在 app.json 的 expo.ios.infoPlist 与 expo.android.permissions 中。
Metro设置
React Native 默认不带完整的 Node 核心模块映射,因此需要在 Metro 中补上这些别名,并打开 package exports 以兼容依赖里的子路径导入:
// metro.config.js 参考
config.resolver.extraNodeModules = {
buffer: require.resolve("buffer/"),
crypto: require.resolve("crypto-browserify"),
stream: require.resolve("stream-browserify"),
process: require.resolve("process/browser"),
events: require.resolve("events/"),
http: require.resolve("http-browserify"),
https: require.resolve("https-browserify"),
zlib: require.resolve("browserify-zlib"),
util: require.resolve("util/"),
url: require.resolve("url/"),
path: require.resolve("path-browserify"),
};
config.resolver.unstable_enablePackageExports = true;
构建调试端
Expo Go 不会打入你项目里的自定义 BLE 原生模块,调试时请改用 Dev Client 或直接生成真机包:
# iOS 目标
expo run:ios --device
# Android 目标
expo run:android --device
# 启动 Dev Client 用的 bundler
expo start --dev-client -c
启动并监听
export async function initializeRnBleSession() {
await ukeySdk.init({
env: "react-native", // 指定走 React Native BLE 通道
debug: __DEV__,
fetchConfig: true,
});
// 最好尽快订阅 UI 事件(PIN / Passphrase / 确认)
// 可参考:ukeySdk.on(UI_EVENT, (uiEvent) => { ... });
}
找设备ID
const availableDevices = await ukeySdk.searchDevices();
if (!availableDevices.success) throw new Error(availableDevices.payload.error);
// BLE 扫描阶段一般先拿到 id 和 name,随后再用 getFeatures 读取 device_id。
const pickedDevice = availableDevices.payload[0];
const connectId = pickedDevice.connectId;
const featureInfo = await ukeySdk.getFeatures(connectId);
if (!featureInfo.success) throw new Error(featureInfo.payload.error);
const deviceId = featureInfo.payload.device_id;
拿到后建议把 connectId 与 deviceId 缓存在本次会话里,因为多数链方法都会同时用到它们。
首个例
const opResult = await ukeySdk.btcGetAddress(connectId, deviceId, {
path: "m/84'/0'/2'/0/0",
coin: "btc",
showOnUKey: false,
});
if (opResult.success) {
console.log("解析出的 BTC 地址:", opResult.payload.address);
} else {
console.error("调用失败:", opResult.payload.error, opResult.payload.code);
}
说明
- 尽早订阅
UI_EVENT,避免 PIN、Passphrase 或确认事件没人响应导致请求卡住。参见 事件说明。 - BLE 行为强依赖系统版本、权限状态和真机环境,请优先在真实设备上验证。
后续
- 导读
- 异常处理、常见入参(CommonParams)
- 帧格式(64 字节,仅供参考):传输插件(Low-level)