Android 蓝牙连接 ESC/POS 热敏打印机打印实例(蓝牙连接篇)

公司的一个手机端的 CRM 项目最近要增加小票打印的功能,就是我们点外卖的时候经常会见到的那种小票。这里主要涉及到两大块的知识:

蓝牙连接及数据传输 ESC/POS 打印指令

蓝牙连接不用说了,太常见了,这篇主要介绍这部分的内容。但ESC/POS 打印指令是个什么鬼?简单说,我们常见的热敏小票打印机都支持这样一种指令,只要按照指令的格式向打印机发送指令,哪怕是不同型号品牌的打印机也会执行相同的动作。比如打印一行文本,换行,加粗等都有对应的指令,这部分内容放在下一篇介绍。

本篇主要基于官方文档,相比官方文档,省去了大段的说明,更加便于快速上手。

1. 蓝牙权限

想要使用蓝牙功能,首先要在 AndroidManifest 配置文件中声明蓝牙权限:

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

BLUETOOTH 权限只允许建立蓝牙连接以及传输数据,但是如果要进行蓝牙设备发现等操作的话,还需要申请 BLUETOOTH_ADMIN 权限。

2. 初始配置

这里主要用到一个类BluetoothAdapter。用法很简单,直接看代码:

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); }

如果蓝牙未打开,那么执行 startActivityForResult() 后,会弹出一个对话框询问是否要打开蓝牙,点击`是`之后就会自动打开蓝牙。成功打开蓝牙后就会回调到 onActivityResult()。

除了主动的打开蓝牙,还可以监听 BluetoothAdapter.ACTION_STATE_CHANGED
广播,包含EXTRA_STATE和EXTRA_PREVIOUS_STATE两个 extra 字段,可能的取值包括 STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, and STATE_OFF。含义很清楚了,不解释。

3. 发现设备

初始化完成之后,蓝牙打开了,接下来就是扫描附近的设备,只需要一句话:

mBluetoothAdapter.startDiscovery();

不过这样只是开始执行设备发现,这肯定是一个异步的过程,我们需要注册一个广播,监听发现设备的广播,直接上代码:

private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // 当有设备被发现的时候会收到 action == BluetoothDevice.ACTION_FOUND 的广播 if (BluetoothDevice.ACTION_FOUND.equals(action)) { //广播的 intent 里包含了一个 BluetoothDevice 对象 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); //假设我们用一个 ListView 展示发现的设备,那么每收到一个广播,就添加一个设备到 adapter 里 mArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } } }; // 注册广播监听 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

注释已经写的很清楚了,除了 BluetoothDevice.EXTRA_DEVICE 之外,还有一个 extra 字段 BluetoothDevice.EXTRA_CLASS, 可以得到一个 BluetoothClass 对象,主要用来保存设备的一些额外的描述信息,比如可以知道这是否是一个音频设备。

关于设备发现,有两点需要注意:

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

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

执行之后会弹出对话窗询问是否允许设备被设为可发现的状态,点击`是`之后设备即被设为可发现的状态。

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

4. 设备配对与连接

4.1 配对

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

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

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

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

代码很简单,不解释了,就是调用BluetoothAdapter.getBondedDevices()得到一个 Set<BluetoothDevice> 并遍历取得已配对的设备信息。

4.2 连接

蓝牙设备的连接和网络连接的模型十分相似,都是Client-Server 模式,都通过一个 socket 来进行数据传输。那么作为一个 Android 设备,就存在三种情况:

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

因为是为了下一篇介绍连接热敏打印机打印做铺垫,所以这里先讲 Android 设备作为 Client 建立连接的情况。因为打印机是不可能主动跟 Android 设备建立连接的,所以打印机必然是作为 Server 被连接。

4.2.1 作为 Client 连接

首先需要获取一个 BluetoothDevice 对象。获取的方法前面其实已经介绍过了,可以通过调用 startDiscovery()并监听广播获得,也可以通过查询已配对设备获得。 通过 BluetoothDevice.createRfcommSocketToServiceRecord(UUID) 得到BluetoothSocket 对象 通过BluetoothSocket.connect()建立连接 异常处理以及连接关闭

废话不多说,上代码:

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; } @Override public 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 端设置的一样!否则就会报错!

如果是连接热敏打印机这种情况,不知道 Server 端设置的 UUID 是什么怎么办?
不用担心,因为一些常见的蓝牙服务协议已经有约定的 UUID。比如我们连接热敏打印机是基于 SPP 串口通信协议,其对应的 UUID 是 "00001101-0000-1000-8000-00805F9B34FB",所以实际的调用是这样:

复制代码 代码如下:
device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"))

其他常见的蓝牙服务的UUID大家可以自行搜索。如果只是用于自己的应用之间的通信的话,那么理论上可以随便定义一个 UUID,只要 server 和 client 两边使用的 UUID 一致即可。

4.2.2 作为 Server 连接

通过BluetoothAdapter.listenUsingRfcommWithServiceRecord(String, UUID)获取一个 BluetoothServerSocket 对象。这里传入的第一个参数用来设置服务的名称,当其他设备扫描的时候就会显示这个名称。UUID 前面已经介绍过了。 调用BluetoothServerSocket.accept()开始监听连接请求。这是一个阻塞操作,所以当然也要放在主线程之外进行。当该操作成功执行,即有连接建立的时候,会返回一个BluetoothSocket 对象。 调用 BluetoothServerSocket.close() 会关闭监听连接的服务,但是当前已经建立的链接并不会受影响。

还是看代码吧:

private class AcceptThread extends Thread { private final BluetoothServerSocket mmServerSocket; public AcceptThread() { BluetoothServerSocket tmp = null; try { // client 必须使用一样的 UUID !!! tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (IOException e) { } mmServerSocket = tmp; } @Override public void run() { BluetoothSocket socket = null; //阻塞操作 while (true) { try { socket = mmServerSocket.accept(); } catch (IOException e) { break; } //直到有有连接建立,才跳出死循环 if (socket != null) { //要在新开的线程执行,因为连接建立后,当前线程可能会关闭 doSomething(socket); mmServerSocket.close(); break; } } } public void cancel() { try { mmServerSocket.close(); } catch (IOException e) { } } }

5. 数据传输

终于经过了前面的4步,万事俱备只欠东风。而最后这一部分其实是最简单的,因为就只是简单的利用 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 和 OutputStream try { 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 stream int 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) { } } }

下一篇介绍通过手机操作热敏打印机打印的时候,还会用到这部分内容,所以这里就先不多讲了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

时间: 2024-10-30 04:19:56

Android 蓝牙连接 ESC/POS 热敏打印机打印实例(蓝牙连接篇)的相关文章

Android 蓝牙连接 ESC/POS 热敏打印机打印实例(ESC/POS指令篇)

上一篇 主要介绍了如何通过蓝牙连接到打印机.这一篇,我们就介绍如何向打印机发送打印指令,来打印字符和图片. 1. 构造输出流 首先要明确一点,就是蓝牙连接打印机这种场景下,手机是 Client 端,打印机是 Server 端. 在上一篇的最后,我们从 BluetoothSocket 得到了一个OutputStream.这里我们做一层包装,得到一个OutputStreamWriter 对象: OutputStreamWriter writer = new OutputStreamWriter(ou

Android进阶——安卓调用ESC/POS打印机打印实例

前言 前一段时间由于工作需要,要研究一下安卓程序调用打印机打印小票,并且要求不能使用蓝牙调用,研究了一下,可以利用socket连接,来实现打印功能.写了个Demo,分享一下. 工具:一台打印机(芯烨XP-80XX),一台安卓测试机 开发环境:Android Studio 1.5 需求:点击按钮,实现打印小票功能,小票上除必要文字外,还要有二维码. 封装了一个Pos打印工具类: package com.example.haoguibao.myapplication; import java.io.

Android手机通过蓝牙连接佳博打印机的实例代码_Android

所使用的打印机为佳博打印机,支持蓝牙.wifi.usb我所使用的是通过蓝牙来连接. 在网上找到一个佳博官方针对安卓开发的App源码,但是各种的跳转,没有看太懂,所以又去问度娘,找到了一个不错的文章 Android对于蓝牙开发从2.0版本的sdk才开始支持,而且模拟器不支持,测试至少需要两部手机,所以制约了很多技术人员的开发. 1. 首先,要操作蓝牙,先要在AndroidManifest.xml里加入权限 // 管理蓝牙设备的权限 <uses-permissionandroid:name="

Android手机通过蓝牙连接佳博打印机的实例代码

所使用的打印机为佳博打印机,支持蓝牙.wifi.usb我所使用的是通过蓝牙来连接. 在网上找到一个佳博官方针对安卓开发的App源码,但是各种的跳转,没有看太懂,所以又去问度娘,找到了一个不错的文章 Android对于蓝牙开发从2.0版本的sdk才开始支持,而且模拟器不支持,测试至少需要两部手机,所以制约了很多技术人员的开发. 1. 首先,要操作蓝牙,先要在AndroidManifest.xml里加入权限 // 管理蓝牙设备的权限 <uses-permissionandroid:name="

Android 蓝牙开发实例--蓝牙聊天程序的设计和实现

Android 蓝牙开发实例--蓝牙聊天程序的设计和实现 转自:http://blog.csdn.net/dlutbrucezhang/article/details/8963696 作者在这里介绍的这个实例是Google SDK中提供的一个蓝牙聊天程序,简单但信息量巨大,非常适合初学者学习蓝牙方面的知识. 在学习这个实例前请读者仔细阅读并理解Socket的工作原理和实现机制,作者的这篇博客中有详细的介绍: http://blog.csdn.net/dlutbrucezhang/article/

Android app开发如何通过按键来打开蓝牙,求一实例

问题描述 Android app开发如何通过按键来打开蓝牙,求一实例 小弟最近在研究app蓝牙的开发,需要通过一个按键来打开手机的蓝牙设备,希望好心人能给个实例让我研究一下,都已经纠结好久了 解决方案 刚好有一个例子,http://download.csdn.net/download/f8376904110/2646221 解决方案二: 刚好有一个例子,http://download.csdn.net/download/f8376904110/2646221

Android客户端利用Socket连接服务端思路及实例

Android客户端利用Socket连接服务器的大概思路如下: 客户端首次连接服务器: socket = new Socket();       SocketAddress address = new InetSocketAddress(svrHost, svrPort);       socket.connect(address, TIME_OUT);       socket.setSoTimeout(TIME_OUT);       in = new BufferedReader(new 

Android连接指定Wifi的方法实例代码

本篇文章主要记录一下Android中打开Wifi.获取Wifi接入点信息及连接指接入点的方法. 自己写的demo主要用于测试接口的基本功能,因此界面及底层逻辑比较粗糙. demo的整体界面如下所示: 上图中的OPEN按键负责开启Wifi: GET按键负责获取扫描到的接入点信息. 当获取到接入点信息后,我选取了其中的名称及信号强度,以列表的形式显示在主界面下方,如下图: 当点击列表中的Item时,就会去连接对应的接入点. 自己的逻辑比较简单,测试时的代码,假定连接的是不许要密码或密码已知的接入点.

Android通过JNI调用驱动程序(完全解析实例)

  [置顶] Android通过JNI调用驱动程序(完全解析实例)            要达到的目的:android系统中,用JAVA写界面程序,调用jni中间库提供的接口,去操作某个驱动节点,实现read,writer ioctl等操作!这对底层驱动开发人员是很重要的一个调试通道,也是android 系统下提供一些特殊功能接口的方法! 本文前提:我们假设已经写了一个驱动程序,它是控制LED的亮灭的,并且创建了一个节点:/dev/vib,也就是通过open这个vib节点,可以read/writ