跳到主要内容

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.jsApp.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_SCANBLUETOOTH_CONNECT。有些系统还会把位置权限和系统定位开关一起纳入扫描条件,缺少其中任一项都可能导致列表为空。

iOS 目标配置

iOS 侧请把这些用途说明写入 Info.plist

  • NSBluetoothAlwaysUsageDescription:蓝牙访问说明,必填。
  • NSBluetoothPeripheralUsageDescription:旧版 iOS 仍可能读取,可按兼容需求保留。
  • NSCameraUsageDescription:只有应用会用到相机,例如扫码时才需要。

Expo 项目可以把对应声明放在 app.jsonexpo.ios.infoPlistexpo.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;

拿到后建议把 connectIddeviceId 缓存在本次会话里,因为多数链方法都会同时用到它们。

首个例

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 行为强依赖系统版本、权限状态和真机环境,请优先在真实设备上验证。

后续