uniapp中使用plus对象实现经典蓝牙

如果要使用的话建议用uts改写,连接蓝牙是耗时操作,使用plus对象无法通过plus.android.importClass('java.lang.Thread')将耗时操作放入子线程,会导致页面卡顿。

javascript 复制代码
//经典蓝牙服务的UUID
const UUIDString = "00001101-0000-1000-8000-00805F9B34FB";

class Bluetooth {
  //蓝牙类的实例
  instance;
  /**当前程序页面上下文 */
  Activity;
  /**蓝牙适配器 */
  Adapter;
  /**蓝牙建立的连接 */
  BluetoothSocket;
  /**蓝牙适配器接收器 */
  BluetoothReciever;
  /**蓝牙所需的权限 */
  permissions = [];
  /**搜索到蓝牙的回调 */
  onBluetoothDeviceFoundCallBack = () => {};
  /**蓝牙状态变更 */
  onBluetoothConnectedChangeCallback = () => {};
  /**蓝牙适配器状态变更 */
  onBluetoothAdapterChangeCallback = () => {};

  //初始化
  constructor() {
    if (Bluetooth.instance) return Bluetooth.instance;
    Bluetooth.instance = this;
    return Bluetooth.instance;
  }
  //初始化蓝牙适配器
  init() {
    this.Activity = plus.android.runtimeMainActivity();
    const BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
    const BluetoothDevice = plus.android.importClass("android.bluetooth.BluetoothDevice");
    const Intent = plus.android.importClass("android.content.Intent");
    //初始化蓝牙适配器
    this.Adapter = BluetoothAdapter.getDefaultAdapter();
    //校验是否存在蓝牙权限
    if (!this.Adapter) return showToast({ position: "top", message: "当前设备不支持蓝牙!" });
    //保存this指向
    //如果适配器不可用跳往设置页
    if (!this.Adapter.isEnabled) {
      //创建意图
      const intent = new Intent(this.Adapter.ACTION_REQUEST_ENABLE);
      //跳转至蓝牙设置
      plus.android.invoke(this.Activity, "startActivity", intent, 1, null);
      return;
    }
    const Manifest = plus.android.importClass("android.Manifest");
    this.permissions = [
      Manifest.permission.BLUETOOTH_SCAN,
      Manifest.permission.BLUETOOTH_CONNECT,
      Manifest.permission.BLUETOOTH_ADVERTISE,
      Manifest.permission.ACCESS_FINE_LOCATION,
      Manifest.permission.BLUETOOTH,
      Manifest.permission.BLUETOOTH_ADMIN
    ];
    //蓝牙开关变化常量
    const STATE_CHANGED = BluetoothAdapter.ACTION_STATE_CHANGED;
    //蓝牙连接常量
    const ACTION_ACL_CONNECTED = BluetoothDevice.ACTION_ACL_CONNECTED;
    //蓝牙断开连接常量
    const ACTION_ACL_DISCONNECTED = BluetoothDevice.ACTION_ACL_DISCONNECTED;
    //搜索到蓝牙常量
    const ACTION_FOUND = BluetoothDevice.ACTION_FOUND;
    //保存this指向
    const that = this;
    //接收广播(注意:这里广播的类需要加io.dcloud前缀,不加会出现报错无法执行)
    this.BluetoothReciever = plus.android.implements("io.dcloud.android.content.BroadcastReceiver", {
      //接收蓝牙发出的广播
      onReceive: function (context, intent) {
        //获取广播意图
        const action = plus.android.invoke(intent, "getAction");
        //获取蓝牙设备信息
        const device = plus.android.invoke(intent, "getParcelableExtra", "android.bluetooth.device.extra.DEVICE");
        //获取蓝牙设备名称
        const deviceName = device.getName();
        if (!deviceName) return;
        //获取蓝牙设备id
        const deviceId = device.getAddress();
        switch (action) {
          //蓝牙开关变化
          case STATE_CHANGED:
            //获取蓝牙广播状态
            const EXTRA_STATE = plus.android.getAttribute(new BluetoothAdapter(), "EXTRA_STATE");
            //广播状态码
            const code = plus.android.invoke(intent, "getIntExtra", EXTRA_STATE, -1);
            switch (code) {
              case BluetoothAdapter.STATE_OFF:
                that.onBluetoothAdapterChangeCallback({ state: false });
                break;
              case BluetoothAdapter.STATE_ON:
                that.onBluetoothAdapterChangeCallback({ state: true });
                break;
            }
            break;
          //蓝牙连接成功
          case ACTION_ACL_CONNECTED:
            that.onBluetoothConnectedChangeCallback({ name: deviceName, deviceId, status: true });
            break;
          //蓝牙断开连接成功
          case ACTION_ACL_DISCONNECTED:
            //蓝牙断开则把建立的socket置空
            that.BluetoothSocket = null;
            that.onBluetoothConnectedChangeCallback({ name: deviceName, deviceId, status: false });
            break;
          //搜索到蓝牙
          case ACTION_FOUND:
            if (!deviceName) return;
            that.onBluetoothDeviceFoundCallBack({ name: deviceName, deviceId });
            break;
        }
      }
    });
    //创建意图过滤器,添加需要监听的蓝牙广播动作
    const IntentFilter = plus.android.newObject("android.content.IntentFilter");
    // 监听蓝牙开关状态变化(核心广播)
    plus.android.invoke(IntentFilter, "addAction", STATE_CHANGED);
    // 监听蓝牙设备连接成功
    plus.android.invoke(IntentFilter, "addAction", ACTION_ACL_CONNECTED);
    // 监听蓝牙设备断开连接
    plus.android.invoke(IntentFilter, "addAction", ACTION_ACL_DISCONNECTED);
    // 监听搜索到的蓝牙
    plus.android.invoke(IntentFilter, "addAction", ACTION_ACL_DISCONNECTED);
    //搜索到蓝牙
    plus.android.invoke(IntentFilter, "addAction", ACTION_FOUND);
    //注册广播接收器
    plus.android.invoke(this.Activity, "registerReceiver", this.BluetoothReciever, IntentFilter);
    //蓝牙提示语
    console.info("蓝牙广播接收器注册成功");
  }
  //获取已连接蓝牙列表
  getBondedDevices() {
    return new Promise((resolve, reject) => {
      //获取已配对列表
      const devices = this.Adapter.getBondedDevices();
      const iterator = plus.android.invoke(devices, "iterator");
      plus.android.importClass(iterator);
      const list = [];
      while (iterator.hasNext()) {
        const device = iterator.next();
        const name = plus.android.invoke(device, "getName");
        const deviceId = plus.android.invoke(device, "getAddress");
        list.push({ name, deviceId });
      }
      resolve(list);
    });
  }
  //搜索蓝牙
  discoverDevices(callback) {
    plus.android.requestPermissions(
      this.permissions,
      ({ granted }) => {
        console.log("请求权限", granted);
        this.onBluetoothDeviceFoundCallBack = callback;
        //当前是否正在搜索
        const isDiscovering = plus.android.invoke(this.Adapter, "isDiscovering");
        //如果正在搜索,先停止再重新搜索
        if (isDiscovering) {
          this.stopDiscover();
        }
        //开始搜索蓝牙
        plus.android.invoke(this.Adapter, "startDiscovery");
        //三秒后停止搜索
        setTimeout(() => {
          this.stopDiscover();
        }, 3000);
      },
      () => {}
    );
  }
  //蓝牙状态变更
  onBluetoothConnectedChange(callback) {
    this.onBluetoothConnectedChangeCallback = callback;
  }
  //蓝牙状态变更
  onBluetoothAdapterChange(callback) {
    this.onBluetoothAdapterChangeCallback = callback;
  }
  //停止搜索
  stopDiscover() {
    if (!this.Adapter || !this.BluetoothReciever) return console.log({ message: "蓝牙适配器未开启!" });
    plus.android.invoke(this.Adapter, "cancelDiscovery");
  }
  //连接蓝牙
  connectDevice(id) {
    return new Promise((resolve, reject) => {
      plus.android.requestPermissions(
        this.permissions,
        ({ granted }) => {
          console.log("请求权限", granted);
          try {
            //停止扫描
            const isDiscovering = plus.android.invoke(this.Adapter, "isDiscovering");
            //判断是否正在扫描附近的蓝牙
            if (isDiscovering) {
              plus.android.invoke(this.Adapter, "cancelDiscovery");
            }
            //如果已连接先关闭
            if (this.BluetoothSocket) {
              plus.android.invoke(this.BluetoothSocket, "close");
              this.BluetoothSocket = null;
            }
            //蓝牙设备
            if (!id) return reject({ message: "蓝牙参数错误!" });
            //获取远程蓝牙信号
            const device = plus.android.invoke(this.Adapter, "getRemoteDevice", id);
            //筛选可用的设备UUID
            const UUID = plus.android.importClass("java.util.UUID");
            const uuid = UUID.fromString(UUIDString);
            this.BluetoothSocket = device.createRfcommSocketToServiceRecord(uuid);
            plus.android.invoke(this.BluetoothSocket, "connect");
            resolve();
          } catch (error) {
            console.error("连接蓝牙出现了错误", error);
            this.BluetoothSocket = null;
            reject({ code: -1, message: "连接超时或设备未开启!" });
          }
        },
        (err) => reject(err)
      );
    });
  }
  //断开蓝牙
  disconnectDevice(id) {
    return new Promise((resolve, reject) => {
      try {
        //获取当前连接的蓝牙
        const device = plus.android.invoke(this.Adapter, "getRemoteDevice", id);
        const UUID = plus.android.importClass("java.util.UUID");
        const uuid = UUID.fromString(UUIDString);
        //获取蓝牙建立的连接
        const socket = plus.android.invoke(device, "createInsecureRfcommSocketToServiceRecord", uuid);
        //当前蓝牙是否已连接
        const isConnected = plus.android.invoke(device, "isConnected");
        //如果未连接直接阻断
        if (!socket || !isConnected) return showToast({ position: "top", message: "当前蓝牙未连接!" });
        //关闭输入流
        const inputStream = plus.android.invoke(socket, "getInputStream");
        plus.android.invoke(inputStream, "close");
        //关闭输出流
        const outputStream = plus.android.invoke(socket, "getOutputStream");
        plus.android.invoke(outputStream, "close");
        //断开建立的连接
        plus.android.invoke(socket, "close");
        resolve();
      } catch (error) {
        reject(error);
      }
    });
  }
  /**
   * 发送数据
   * @param {*} byte[] 二进制字节数组
   * @returns
   */
  sendDataToBluetooth(bytes) {
    return new Promise(async (resolve, reject) => {
      try {
        if (!this.BluetoothSocket) return showToast({ position: "top", message: "连接已断开,请重新连接!" });
        //生成输出流;
        const outputStream = plus.android.invoke(this.BluetoothSocket, "getOutputStream");
        //字节长度
        const byteLength = bytes.byteLength;
        const blob = new Int8Array(bytes);
        //当前字节索引
        let currentSize = 0;
        //切片大小
        const chunkSize = 2048;
        //子线程实现方法
        while (currentSize < byteLength) {
          const chunk = blob.slice(currentSize, currentSize + chunkSize);
          //发送数据
          plus.android.invoke(outputStream, "write", [...chunk]);
          //当前索引自增
          currentSize += chunkSize;
        }
        resolve();
      } catch (error) {
        console.error("发送失败", error);
        reject(error);
      }
    });
  }
}

/**
 * 创建蓝牙实例
 * @returns
 */
export const createBluetooth = () => {
  return new Bluetooth();
};

页面中使用

javascript 复制代码
<template>
  <div class="page">
    <van-button @click="discover">搜索蓝牙</van-button>
    <van-button @click="sendData">发送数据</van-button>
    <ul class="container">
      <li v-for="item in list" :key="item.deviceId" @click="connect(item.deviceId)">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref, onMounted } from "vue";
import { getBinary, getTextBinary, getQrCode } from "@/api/common";
import { createBluetooth } from "@/system/bluetooth";

const list = ref([]);

const bluetooth = createBluetooth();

onMounted(() => {
  bluetooth.init();
});
//搜索蓝牙
const discover = () => {
  bluetooth.discoverDevices((device) => {
    const same = list.value.find((el) => el.deviceId === device.deviceId);
    if (same) return;
    list.value.push(device);
  });
};
//连接蓝牙
const connect = (id) => {
  bluetooth.connectDevice(id);
};

//发送数据
const sendData = async () => {
  const res = await getBinary(); //请求打印数据
  bluetooth.sendDataToBluetooth(res);
};

bluetooth.onBluetoothConnectedChange((res) => console.log("蓝牙连接状态变更了", res));
bluetooth.onBluetoothAdapterChange(({ state }) => console.info("蓝牙开关变更了", state));
</script>

<style scoped lang="scss">
.page {
  padding-top: 50px;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100vh;
  overflow-y: scroll;

  .van-button {
    flex-shrink: 0;
  }
  .container {
    width: 100%;
    flex: 1;
    overflow-y: scroll;
    li {
      width: 100%;
      height: 40px;
      text-align: center;
      overflow: hidden;
    }
  }
}
</style>
相关推荐
2501_915918412 小时前
基于Mach-O文件的动态库与静态库归属方案及API扫描实践
android·ios·小程序·https·uni-app·iphone·webview
2501_915106322 小时前
iOS 证书无法跨电脑使用?签名迁移方法一文讲透
android·ios·小程序·https·uni-app·iphone·webview
小王码农记3 小时前
uniapp中使用vuex
uni-app
HWL56793 小时前
uni-app中路由的使用
前端·uni-app
万物得其道者成3 小时前
uni-app App 端不支持 SSE?用 renderjs + XHR 流式解析实现稳定输出
前端·javascript·uni-app
WeirdoPrincess3 小时前
iOS 打包签名资料准备指南(HBuilderX / uni-app)
ios·uni-app
笨笨狗吞噬者1 天前
维护 uniapp 小程序端近一年,我想拉一个开发者交流群
前端·程序员·uni-app
你的眼睛會笑2 天前
uni-app 实战:使用 lime-painter 实现页面内容一键生成海报并下载
uni-app
一只程序熊2 天前
uniapp 高德地图 打开选择地址报错,也没有展示出附近的位置
android·uni-app