Android蓝⽛连接ESCPOS热敏打印机打印实例(蓝⽛连
接篇)
公司的⼀个⼿机端的 CRM 项⽬最近要增加⼩票打印的功能,就是我们点外卖的时候经常会见到的那种⼩票。这⾥主要涉及到两⼤块的知识:
1. 蓝⽛连接及数据传输
2. 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 mBluetoothAdapter = DefaultAdapter();
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()。
除了主动的打开蓝⽛,还可以监听
⼴播,包含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 = Action();
// 当有设备被发现的时候会收到 action == BluetoothDevice.ACTION_FOUND 的⼴播
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
//⼴播的 intent ⾥包含了⼀个 BluetoothDevice 对象
BluetoothDevice device = ParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//假设我们⽤⼀个 ListView 展⽰发现的设备,那么每收到⼀个⼴播,就添加⼀个设备到 adapter ⾥
mArrayAdapter.Name() + "\n" + Address());
}
}
};
// 注册⼴播监听
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
注释已经写的很清楚了,除了BluetoothDevice.EXTRA_DEVICE 之外,还有⼀个 extra 字段BluetoothDevice.EXTRA_CLASS, 可以得到⼀个对象,主要⽤来保存设备的⼀些额外的描述信息,⽐如可以知道这是否是⼀个⾳频设备。
关于设备发现,有两点需要注意:
startDiscovery() 只能扫描到那些状态被设为可发现的设备。安卓设备默认是不可发现的,要改变设备为可发现的状态,需要如下操作:
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//设置可被发现的时间,00s
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(intent);
执⾏之后会弹出对话窗询问是否允许设备被设为可发现的状态,点击`是`之后设备即被设为可发现的状态。
startDiscovery()是⼀个⼗分耗费资源的操作,所以需要及时的调⽤cancelDiscovery()来释放资源。⽐如在进⾏设备连接之前,⼀定要先调⽤cancelDiscovery()
4. 设备配对与连接dnf好听的名字
4.1 配对
当与⼀个设备第⼀次进⾏连接操作的时候,屏幕会弹出提⽰框询问是否允许配对,只有配对成功之后,才能建⽴连接。
描写星空的四字词语系统会保存所有的曾经成功配对过的设备信息。所以在执⾏startDiscovery()之前,可以先尝试查已配对设备,因为这是⼀个本地信息读取的过程,所以⽐startDiscovery()要快得多,也避免占⽤过多资源。如果设备在蓝⽛信号的覆盖范围内,就可以直接发起连接了。
查配对设备的代码如下:
Set<BluetoothDevice> pairedDevices = BondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
mArrayAdapter.Name() + "\n" + Address());
}
}
代码很简单,不解释了,就是调⽤BondedDevices()得到⼀个 Set<BluetoothDevice> 并遍历取得已配对的设备信息。
4.2 连接
蓝⽛设备的连接和⽹络连接的模型⼗分相似,都是Client-Server 模式,都通过⼀个 socket 来进⾏数据传输。那么作为⼀个Android 设备,就存在三种情况:
1. 只作为 Client 端发起连接
2. 只作为 Server 端等待别⼈发起建⽴连接的请求
3. 同时作为 Client 和 Server
因为是为了下⼀篇介绍连接热敏打印机打印做铺垫,所以这⾥先讲 Android 设备作为 Client 建⽴连接的情况。因为打印机是不可能主动跟 Android 设备建⽴连接的,所以打印机必然是作为 Server 被连接。
4.2.1 作为 Client 连接
1. ⾸先需要获取⼀个BluetoothDevice 对象。获取的⽅法前⾯其实已经介绍过了,可以通过调⽤ startDiscovery()并监听⼴
播获得,也可以通过查询已配对设备获得。
2. 通过 ateRfcommSocketToServiceRecord(UUID) 得到BluetoothSocket 对象
3. 通过t()建⽴连接
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 = ateRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
水浒传章节梗概@Override
public void run() {
// 建⽴连接前记得取消设备发现
mBluetoothAdapter.cancelDiscovery();
try {
// 耗时操作,所以必须在主线程之外进⾏
} catch (IOException connectException) {
//处理连接建⽴失败的异常
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
doSomething(mmSocket);
}
//关闭⼀个正在进⾏的连接
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
Client 发起连接时传⼊的 UUID 必须要和 Server 端设置的⼀样!否则就会报错!
如果是连接热敏打印机这种情况,不知道 Server 端设置的 UUID 是什么怎么办?
不⽤担⼼,因为⼀些常见的蓝⽛服务协议已经有约定的 UUID。⽐如我们连接热敏打印机是基于 SPP 串⼝通信协议,其对应的 UUID 是 "00001101-0000-1000-8000-00805F9B34FB",所以实际的调⽤是这样:
复制代码代码如下:
其他常见的蓝⽛服务的UUID⼤家可以⾃⾏搜索。如果只是⽤于⾃⼰的应⽤之间的通信的话,那么理论上可以随便定义⼀个UUID,只要 server 和 client 两边使⽤的 UUID ⼀致即可。
4.2.2 作为 Server 连接
1. 通过BluetoothAdapter.listenUsingRfcommWithServiceRecord(String, UUID)获取⼀个BluetoothServerSocket 对象。这⾥传⼊的第⼀
个参数⽤来设置服务的名称,当其他设备扫描的时候就会显⽰这个名称。UUID 前⾯已经介绍过了。
2. 调⽤BluetoothServerSocket.accept()开始监听连接请求。这是⼀个阻塞操作,所以当然也要放在主线程之外进⾏。当该操作
成功执⾏,即有连接建⽴的时候,会返回⼀个BluetoothSocket 对象。
3. 调⽤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;
}
breakfast//直到有有连接建⽴,才跳出死循环
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 = InputStream();
tmpOut = OutputStream();
} 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 = ad(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) { }
}
}
下⼀篇介绍通过⼿机操作热敏打印机打印的时候,还会⽤到这部分内容,所以这⾥就先不多讲了。以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论