React Native在Zebra TC15上无法发现ZQ511打印机求助
React Native 连接 Zebra ZQ511 打印机无法发现设备的解决方案
问题说明
Zebra ZQ511 支持在 React Native 中通过 Link-OS SDK 实现蓝牙打印,若按照官方步骤操作后仍无法发现设备,可通过以下方案排查修复:
核心修复步骤
Android 权限补充
Android 12 及以上版本新增蓝牙扫描和连接权限,需在AndroidManifest.xml中添加:<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />同时需在 React Native 代码中动态申请这些权限,确保运行时权限通过。
错误回调补全
原生模块的DiscoveryResult类中未实现错误回调逻辑,导致无法捕获发现过程中的异常,需补充错误回调并传递到 React Native 端。设备配对验证
确保 Zebra ZQ511 已与手机完成蓝牙配对,未配对设备无法被 SDK 发现。SDK 版本兼容
确认使用的 Link-OS SDK 版本与 ZQ511 固件版本匹配,建议使用最新稳定版 SDK。
完整实现代码
Print.js
import React, { Component } from 'react'; import { ActivityIndicator, Platform, PermissionsAndroid } from 'react-native'; import { StyleSheet, Button, TouchableOpacity, Text, View, Alert } from 'react-native'; import ZSDKModule from './ZSDKModule.js'; class App extends Component { state = { printers: [] }; isDiscovering = false; buttonTitle = '点击发现蓝牙打印机'; // 动态申请Android权限 requestPermissions = async () => { if (Platform.OS !== 'android') return true; try { const granted = await PermissionsAndroid.requestMultiple([ PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN, PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT, PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, ]); return granted[PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] === PermissionsAndroid.RESULTS.GRANTED && granted[PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] === PermissionsAndroid.RESULTS.GRANTED && granted[PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION] === PermissionsAndroid.RESULTS.GRANTED; } catch (err) { console.warn(err); return false; } }; printTestLabel = (printer) => { Alert.alert(`正在${printer.name}上打印测试标签`); if (Platform.OS === 'ios') { ZSDKModule.zsdkWriteBluetooth(printer.name, ''); } else { const [macAddress] = printer.name.split(', '); ZSDKModule.zsdkWriteBluetooth(macAddress, ''); } }; discoverPrinters = async () => { const hasPermissions = await this.requestPermissions(); if (!hasPermissions) { Alert.alert('权限不足', '请授予蓝牙和位置权限'); return; } this.setState({ printers: [] }); this.isDiscovering = true; this.buttonTitle = '正在扫描Zebra打印机...'; this.forceUpdate(); ZSDKModule.zsdkPrinterDiscoveryBluetooth((error, discoveredPrinters) => { this.isDiscovering = false; this.buttonTitle = '点击发现蓝牙打印机'; this.forceUpdate(); if (error) { console.error(`发现错误: ${error}`); Alert.alert('发现失败', error); return; } const printersJson = JSON.parse(discoveredPrinters); const printersArray = []; if (Platform.OS === 'ios') { printersJson.forEach((printer, i) => { printersArray.push({ id: i, name: printer.friendlyName }); }); } else { printersJson.forEach((printer, i) => { printersArray.push({ id: i, name: `${printer.address}, ${printer.friendlyName}` }); }); } this.setState({ printers: printersArray }); }); }; render() { return ( <View style={{ marginTop: 50 }}> <Text style={styles.headline}>ZSDK React Native 打印Demo</Text> <Button title={this.buttonTitle} color='#841584' disabled={this.isDiscovering} onPress={this.discoverPrinters} /> {this.state.printers.map((printer) => ( <TouchableOpacity key={printer.id} style={styles.container} onPress={() => this.printTestLabel(printer)}> <Text style={styles.text}> {printer.name} </Text> </TouchableOpacity> ))} <View style={{ marginTop: 150 }}> {this.isDiscovering && <ActivityIndicator size='large' color='#0000ff' />} </View> </View> ); } } export default App; const styles = StyleSheet.create({ container: { marginTop: 3, padding: 10, backgroundColor: '#d9f9b1', alignItems: 'center', justifyContent: 'center', }, text: { color: '#4f603c' }, headline: { textAlign: 'center', fontWeight: 'bold', fontSize: 17, marginTop: 0, }, });
ZSDKModule.js
import { NativeModules } from 'react-native'; const { ZSDKModule } = NativeModules; export default ZSDKModule;
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="com.symbol.datawedge.api.ACTION" /> <uses-permission android:name="com.symbol.datawedge.api.RESULT_ACTION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <!-- Android 12+ 蓝牙权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
MainApplication.java
package com.sistapmobileapp; import android.app.Application; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; import com.facebook.react.defaults.DefaultReactNativeHost; import com.facebook.soloader.SoLoader; import java.util.List; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new DefaultReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { List<ReactPackage> packages = new PackageList(this).getPackages(); packages.add(new ZSDKModulePackage()); return packages; } @Override protected String getJSMainModuleName() { return "index"; } @Override protected boolean isNewArchEnabled() { return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; } @Override protected Boolean isHermesEnabled() { return BuildConfig.IS_HERMES_ENABLED; } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this, false); if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { DefaultNewArchitectureEntryPoint.load(); } ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); } }
ZSDKModulePackage.java
package com.sistapmobileapp; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ZSDKModulePackage implements ReactPackage { @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new ZSDKModule(reactContext)); return modules; } }
ZSDKModule.java
package com.sistapmobileapp; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.zebra.sdk.comm.BluetoothConnection; import com.zebra.sdk.comm.Connection; import com.zebra.sdk.comm.ConnectionException; import com.zebra.sdk.printer.PrinterLanguage; import com.zebra.sdk.printer.ZebraPrinter; import com.zebra.sdk.printer.ZebraPrinterFactory; import com.zebra.sdk.printer.ZebraPrinterLanguageUnknownException; import com.facebook.react.bridge.Callback; import com.zebra.sdk.printer.discovery.BluetoothDiscoverer; import com.zebra.sdk.printer.discovery.DiscoveredPrinter; import com.zebra.sdk.printer.discovery.DiscoveredPrinterBluetooth; import com.zebra.sdk.printer.discovery.DiscoveryHandler; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.HashMap; import android.util.Log; import org.json.JSONArray; import org.json.JSONObject; public class ZSDKModule extends ReactContextBaseJavaModule { ZSDKModule(ReactApplicationContext context) { super(context); } @Override public String getName() { return "ZSDKModule"; } @ReactMethod public void zsdkWriteBluetooth(String macAddress, String zpl) { Log.d("ZSDKModule", "蓝牙打印地址: " + macAddress + ", ZPL内容: " + zpl); Connection printerConnection = null; ZebraPrinter printer = null; printerConnection = new BluetoothConnection(macAddress); try { printerConnection.open(); if (printerConnection.isConnected()) { printer = ZebraPrinterFactory.getInstance(printerConnection); PrinterLanguage printerLanguage = printer.getPrinterControlLanguage(); byte[] testLabel = getTestLabel(printerLanguage); printerConnection.write(testLabel); } } catch (ConnectionException e) { Log.e("ZSDKModule", "连接异常: " + e.getMessage()); } catch (ZebraPrinterLanguageUnknownException e) { Log.e("ZSDKModule", "打印机语言未知: " + e.getMessage()); } finally { try { if (printerConnection != null) { printerConnection.close(); } } catch (ConnectionException ex) { Log.e("ZSDKModule", "关闭连接异常: " + ex.getMessage()); } } } private byte[] getTestLabel(PrinterLanguage printerLanguage) { byte[] testLabel = null; if (printerLanguage == PrinterLanguage.ZPL) { testLabel = "^XA^FO17,16^GB379,371,8^FS^FT65,255^A0N,135,134^FDTEST^FS^XZ".getBytes(); } else if (printerLanguage == PrinterLanguage.CPCL || printerLanguage == PrinterLanguage.LINE_PRINT) { String cpclConfigLabel = "! 0 200 200 406 1\r\n" + "ON-FEED IGNORE\r\n" + "BOX 20 20 380 380 8\r\n" + "T 0 6 137 177 TEST\r\n" + "PRINT\r\n"; testLabel = cpclConfigLabel.getBytes(); } return testLabel; } @ReactMethod public void zsdkPrinterDiscoveryBluetooth(Callback callback) { try { BluetoothDiscoverer.findPrinters(getReactApplicationContext(), new DiscoveryResult(callback)); } catch (ConnectionException e) { Log.e("ZSDKModule", "发现打印机异常: " + e.getMessage()); callback.invoke(e.getMessage(), null); } } public class DiscoveryResult implements DiscoveryHandler { protected Callback callback = null; protected ArrayList<Map<String, String>> foundPrinterList; public DiscoveryResult(Callback callback) { super(); this.callback = callback; foundPrinterList = new ArrayList<>(); } @Override public void foundPrinter(final DiscoveredPrinter printer) { Map<String, String> foundPrinter = new HashMap<>(); foundPrinter.put("address", printer.address); foundPrinter.put("friendlyName", ((DiscoveredPrinterBluetooth) printer).friendlyName); foundPrinterList.add(foundPrinter); } @Override public void discoveryFinished() { List<JSONObject> jsonObj = new ArrayList<>(); for(Map<String, String> data : foundPrinterList) { jsonObj.add(new JSONObject(data)); } JSONArray foundPrinterJson = new JSONArray(jsonObj); Log.d("ZSDKModule", "发现打印机列表: " + foundPrinterJson.toString()); callback.invoke(null, foundPrinterJson.toString()); } @Override public void discoveryError(String message) { Log.e("ZSDKModule", "发现错误: " + message); callback.invoke(message, null); } } }
内容的提问来源于stack exchange,提问作者İbrahim Ethem Bildirici
相关产品推荐
相关产品推荐

