You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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。


完整实现代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 18:35:54