一、概述

最近公司刚好遇到个蓝牙打印的功能,以前实习时看到过类似功能,刚好这次自己实现,顺便记录一下。

二、基本环境

权限:

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>

初始化蓝牙适配器:

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {// Device does not support Bluetooth
}

如果为null代表设备不支持蓝牙功能,需要作出相应处理,比如弹出个对话框;如果设备支持蓝牙,就检查蓝牙是否打开:

if (!mBluetoothAdapter.isEnabled()) {Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(intent, REQUEST_ENABLE_BT);
}

该intent为强制自动打开蓝牙,看机型会有不同的情况,通常机型会弹出对话框询问是否打开蓝牙;也可以处理成跳转到系统蓝牙界面:

Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
startActivityForResult(intent, REQUEST_SETTING);

打开蓝牙后,最好实现onActivityResult方法,收到回应后设置一些初始化工作:

    @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case REQUEST_ENABLE_BT:initBluetooth();break;case REQUEST_SETTING:initBluetooth();break;default:break;}}

三、扫描配对连接

1.扫描:发现设备
打开蓝牙之后,接下来就是扫描附近的蓝牙设备:

mBluetoothAdapter.startDiscovery();

这是个异步过程,通常需要十多秒的时间,扫描的开始、发现、结束均有广播,所以,我们需要注册广播,并在onStop或者onDestory时解除注册:

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (BluetoothDevice.ACTION_FOUND.equals(action)) {// 发现设备// 得到BluetoothDevice对象的意图BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);BluetoothClass bluetoothClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {// 扫描结束} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){// 扫描开始      }}};

广播中的intent包含一个BluetoothDevice对象,通过

intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)

可以获取到,同时还有一个extra字段

BluetoothDevice.EXTRA_CLASS

, 可以得到一个 BluetoothClass 对象,主要用来保存设备的一些额外的描述信息,比如可以知道这是否是一个音频设备。

注意:

startDiscovery() 只能扫描到那些状态被设为 可发现 的设备。安卓设备默认是不可发现的,要改变设备为可发现的状态,需要如下操作:

Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//设置可被发现的时间,300s
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(intent);

startDiscovery()是一个十分耗费资源的操作,所以需要及时的调用cancelDiscovery()来释放资源。比如在进行设备连接之前,一定要先调用cancelDiscovery()

2.配对连接
2.1 配对:
当与一个设备第一次进行连接操作的时需先配对,屏幕会弹出提示框询问是否允许配对,只有配对成功之后,才能建立连接。

系统会保存所有的曾经成功配对过的设备信息。所以在执行startDiscovery()之前,可以先尝试查找已配对设备,因为这是一个本地信息读取的过程,所以比startDiscovery()要快得多,也避免占用过多资源。如果设备在蓝牙信号的覆盖范围内,就可以直接发起连接了。

查找已配对的设备代码如下:

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {for (BluetoothDevice device : pairedDevices) {mArrayAdapter.add(device.getName() + "\n" + device.getAddress());}
}

2.2 连接:
蓝牙连接也是Client-Server模式,通过一个socket来进行数据传输,作为一个android设备,会存在三种情况:

  1. 只作为 Client 端发起连接
  2. 只作为 Server 端等待别人发起建立连接的请求
  3. 同时作为 Client 和 Server

    但这篇重点是蓝牙打印,需要连接打印机,所以只能作为client端,毕竟打印机不可能主动跟其他设备发起连接;另外两种情况应该是ble通信会经常遇到的,后续有机会也会学习记录。

连接步骤:
1.获取一个BluetoothDevice对象,可通过扫描并监听广播获取,也可以通过查询已配对设备获得,还可以通过mac地址获取:

BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(bd.getAddress());

2.获得BluetoothSocket对象:

BluetoothSocket mmSocket = device.createRfcommSocketToServiceRecord(UUID);

3.通过BluetoothSocket.connect()建立连接(这是个同步过程,连接失败需要处理异常)
4.异常处理以及连接关闭

代码:

private class ConnectThread extends Thread {private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;public ConnectThread(BluetoothDevice device) {BluetoothSocket tmp = null;mmDevice = device;try {// 通过 BluetoothDevice 获得 BluetoothSocket 对象tmp = device.createRfcommSocketToServiceRecord(MY_UUID);} catch (IOException e) { }mmSocket = tmp;}@Overridepublic void run() {// 建立连接前记得取消设备发现mBluetoothAdapter.cancelDiscovery();try {// 耗时操作,所以必须在主线程之外进行mmSocket.connect();} catch (IOException connectException) {//处理连接建立失败的异常try {mmSocket.close();} catch (IOException closeException) { }return;}doSomething(mmSocket);}//关闭一个正在进行的连接public void cancel() {try {mmSocket.close();} catch (IOException e) { }}
}

注:

device.createRfcommSocketToServiceRecord(MY_UUID) 这里需要传入一个 UUID,这个UUID 需要格外注意一下。简单的理解,它是一串约定格式的字符串,用来唯一的标识一种蓝牙服务。Client 发起连接时传入的 UUID 必须要和 Server 端设置的一样!否则就会报错!一些常见的蓝牙服务协议已经有约定的 UUID。比如我们连接热敏打印机是基于 SPP 串口通信协议,其对应的 UUID 是 “00001101-0000-1000-8000-00805F9B34FB”。其他常见的蓝牙服务的UUID大家可以自行搜索。如果只是用于自己的应用之间的通信的话,那么理论上可以随便定义一个 UUID,只要 server 和 client 两边使用的 UUID 一致即可。

四、蓝牙数据传输

连接成功之后,我们可以利用InputStream 和OutputStream进行数据的收发。

代码如下:

private class ConnectedThread extends Thread {private final BluetoothSocket mmSocket;private final InputStream mmInStream;private final OutputStream mmOutStream;public ConnectedThread(BluetoothSocket socket) {mmSocket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;//通过 socket 得到 InputStream 和 OutputStreamtry {tmpIn = socket.getInputStream();tmpOut = socket.getOutputStream();} catch (IOException e) { }mmInStream = tmpIn;mmOutStream = tmpOut;}public void run() {byte[] buffer = new byte[1024];  // buffer store for the streamint bytes; // bytes returned from read()//不断的从 InputStream 取数据while (true) {try {bytes = mmInStream.read(buffer);mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();} catch (IOException e) {break;}}}//向 Server 写入数据public void write(byte[] bytes) {try {mmOutStream.write(bytes);} catch (IOException e) { }}public void cancel() {try {mmSocket.close();} catch (IOException e) { }}
}

五、蓝牙打印

其实蓝牙打印就是从BluetoothSocket 得到了一个OutputStream,然后不停的往里面write数据。

手机通过蓝牙向打印机发送的都是纯字节流,打印机如何知道该打印什么呢?这里就要 ESC/POS 打印控制命令,详情可以百度,通常用到的命令如下:

public static final byte[][] byteCommands = { { 0x1b, 0x40 },// 复位打印机 0{ 0x1b, 0x4d, 0x00 },// 标准ASCII字体1{ 0x1b, 0x4d, 0x01 },// 压缩ASCII字体2{ 0x1d, 0x21, 0x00 },// 字体不放大3{ 0x1d, 0x21, 0x02 },// 宽高加倍4{ 0x1d, 0x21, 0x11 },// 宽高加倍5{ 0x1b, 0x45, 0x00 },// 取消加粗模式6{ 0x1b, 0x45, 0x01 },// 选择加粗模式7{ 0x1b, 0x7b, 0x00 },// 取消倒置打印8{ 0x1b, 0x7b, 0x01 },// 选择倒置打印9{ 0x1d, 0x42, 0x00 },// 取消黑白反显10{ 0x1d, 0x42, 0x01 },// 选择黑白反显11{ 0x1b, 0x56, 0x00 },// 取消顺时针旋转90°12{ 0x1b, 0x56, 0x01 },// 选择顺时针旋转90°13{ 0x1b, 0x61, 0x30 },// 左对齐14{ 0x1b, 0x61, 0x01 },// 居中对齐15{ 0x1b, 0x61, 0x32 },// 右对齐16{ 0x1C, 0x21, 0x0C },// 设置倍宽倍高17{ 0x1B, 0x61, 0x00 },// 取消居中18{ 0x1C, 0x21, 0x00 },// 取消倍宽19{ 0x0a },// 换行20{ 0x1d, 0x56, 0x42, 0x01 },// 切纸21{ 0x1C, 0x21, 0x08 },// 稍微小一点的倍宽22{ 0x0D, 0x1B, 0x40 },// 23{ 0x1A },// 24// { 0x1b, 0x69 },// 切纸};

注:

每次打印开始之前,都要对打印机进行初始化,发送指令如下:

mService.write(byteCommands[0]);

注:

通常打印机最大宽度为256个像素点,可以根据这个值来设置格式,不推荐使用空格等来达到打印效果,另,需要打印字符串的话需要进行转码,如下:

mService.write(("测试蓝牙打印").getBytes("GB2312"));// 或者封装下OutputStream
OutputStreamWriter writer = new OutputStreamWriter(outputStream, "GB2312");

六、总结

关于蓝牙连接部分的线程管理代码是网上很多博客采用的,之前项目中领导应该也是这样借鉴的;通过线程来管理不同的连接状态情况,建议实现蓝牙打印功能时:蓝牙连接线程管理封装成一个工具类,同时保存当前连接状态以便随时打印;具体打印内容格式则封装成另一个类,方便扩展。

Android蓝牙相关—蓝牙打印相关推荐

  1. Android 蓝牙相关的广播

    2019独角兽企业重金招聘Python工程师标准>>> Android 蓝牙相关的广播 监听蓝牙相关的广播并获得相关的信息,蓝牙相关的广播主要集中在BluetoothAdapter和 ...

  2. android 蓝牙相关的类,Android中BluetoothAdapter类简介

    一. BluetoothAdapter类介绍 BluetoothAdapter类简单点来说就是代表了本设备(手机.电脑等)的蓝牙适配器对象,通过它我们可以蓝牙设备进行基本 开发了,主要有如下功能: 1 ...

  3. Android经典蓝牙相关知识

    1 蓝牙基础知识 1.1 蓝牙相关的权限 <!--想要用蓝牙进行通信则要申明bluetooth权限--> <uses-permission android:name="an ...

  4. android蓝牙广播自定义,Android 蓝牙相关的广播

    Android 蓝牙相关的广播 监听蓝牙相关的广播并获得相关的信息,蓝牙相关的广播主要集中在BluetoothAdapter和BluetoothDevice类中, 可以通过在AndroidManife ...

  5. android 蓝牙相关广播,Android 蓝牙相关的广播

    Android 蓝牙相关的广播 监听蓝牙相关的广播并获得相关的信息,蓝牙相关的广播主要集中在BluetoothAdapter和BluetoothDevice类中, 可以通过在AndroidManife ...

  6. Android中需要知道的蓝牙相关常识

    首先蓝牙设备有很多种,需要区分蓝牙设备的类型,市面上主要的蓝牙设置有 蓝牙鼠标,键盘,游戏手柄等 蓝牙耳机 蓝牙音箱 可穿戴的蓝牙手环手表 蓝牙健康设备,电子秤等 因此技术上大体分为经典蓝牙和低功耗( ...

  7. Android蓝牙打印服务,GitHub - ZhuangXiong/BluetoothPrint: android bluetooth print .蓝牙打印

    Android 蓝牙打印 入口和回调方便,目前只针对一个打印模板做了封装. 没有扫描设备功能,后期会加入.用户需要先去 设置---蓝牙---配对设备,配对完成后,可以获取手机上已配对的设备信息,从而进 ...

  8. android 蓝牙锁应用开发实例(三)蓝牙相关功能实现【第一部分】

    本人水平有限,文章中如果出现什么不正确或者模糊的地方,还请各位小伙伴留下评论,多多指教 : ) 正式开始前的话 蓝牙开发梳理 整体思路 核心API BlueToothAdapter 简介 getDef ...

  9. Android手机蓝牙连接热敏打印机 打印票据

    手机蓝牙连接热敏打印机 打印票据 话不多说上代码: 项目地址:可直接作为项目依赖 引用 allprojects {repositories {...maven { url 'https://jitpa ...

最新文章

  1. 1013 Battle Over Cities(图的DFS解法)
  2. 常州一院有全消化道的机器人的_【商务对接】昆山智能机器人及成套装备协会链接京东和智能制造...
  3. 史上最全的SpringBatch学习教程
  4. css知识笔记(二)——盒子模型
  5. think python 2ed_Think Python 2ed 笔记(二)
  6. Oracle11g新特性:在线操作功能增强-Oracle11g在线重建索引功能增强 (转载)
  7. python- 属性 静态方法,类方法
  8. 【转】30个你不可不知的CSS选择器
  9. jbig java_jbig2 Java Develop 238万源代码下载- www.pudn.com
  10. 智能陈桥五笔输入法 for linux,智能陈桥五笔官方版
  11. 华为安装gsm框架_华为谷歌框架安装app下载-华为谷歌服务框架安装器(GMS安装器)下载v1.2.0 最新版-西西软件下载...
  12. gatk过滤_快速入门GATK | Public Library of Bioinformatics
  13. 八、管道弯头中流体混合流动与传热
  14. 3D动画展示--3D图片旋转展示
  15. linux swap
  16. C语言的字符串的联接
  17. 收获一篇好文章,与大家共享
  18. 前端微服务化解决方案
  19. 燕文物流完成上市辅导:董事长周文兴持股30%,曾因丢失邮件被批
  20. X79主板win10启动卡住问题修复处理

热门文章

  1. hitchhiker_Hitchhiker的现代Android开发指南:陷阱和隐密错误
  2. 微信该服务器已饱满,微信官方:看看你们都许的什么愿望!把我服务器都干崩了...
  3. 蓝桥杯 ADV_302 秘密行动
  4. Redis缓存树形结构
  5. 3分钟让你明白JSON是什么
  6. 社区团购怎么解决 “配送最后一公里”
  7. java计算机毕业设计果蔬在线销售系统MyBatis+系统+LW文档+源码+调试部署
  8. kdevelop用法
  9. [推荐系统]推荐系统概述
  10. python语言程序设计编程题_Python语言程序设计(2020年版)高等教育出版社,课后编程题答案(仅供参考)...