最近在研究android的蓝牙打印机程序,中间遇到不少坑,在这里分享一下我的经历。

先来看打印过程:

1、申请必要的权限

2、确定本机有蓝牙硬件,并且打开蓝牙适配器

3、开通广播接收功能,用于发现蓝牙设备回传的周围蓝牙设备

4、发送蓝牙发现信号广播,周边设备收到这个信号后,会回传自己的mac地址和设备名

5、通过广播收到周边蓝牙设备mac,通过mac地址,连接到指定的蓝牙打印机,获取socket。

6、通过socket发送GB2312编码的字符串,打印完成。

热敏打印机一般都自带字库,所以直接发送文字给打印机就可以打印出想要的东西,如果要打印其它字体、图片、二维码和条形码,只能通过ESC指令,把这些换算成坐标,通过写点的方式打印。

ESC打印控制指令,可以对文字大小、对齐进行各种控制,大家可以网上搜索具体指令。

部分源代码来源于网上整理。

请使用手机真机测试,并且打开需要的各种权限,特别是打开手机上的位置获取权限,因为这个我在手机上没有打开,一直没有收到设备广播数据,折腾了很久。

android 网页调用蓝牙标签打印机(蓝牙热敏打印机安卓源代码分析)(1)

android 网页调用蓝牙标签打印机(蓝牙热敏打印机安卓源代码分析)(2)

android 网页调用蓝牙标签打印机(蓝牙热敏打印机安卓源代码分析)(3)

下面看源代码

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.myapp2"> <uses-permission android:name="android.permission.Bluetooth_CONNECT" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_CALENDAR" /> <!-- 蓝牙--> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/> <!-- 仅在支持BLE(即蓝牙4.0)的设备上运行--> <uses-permission android:name="android.hardware.bluetooth_le" android:required="true"/> <!-- 如果Android 6.0蓝牙搜索不到设备,需要补充下面两个权限--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Myapp2"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

MainActivity.java

注意那个UUID,不可以随便修改,不然无法连接上打印机,我这里省略了显示蓝牙设备列表的程序,找到打印就直接使用了。

package com.example.myapp2; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothSocket; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import java.io.IOException; import java.io.OutputStream; import java.util.UUID; public class MainActivity extends AppCompatActivity { private Button btn_bt_open; private Button btn_bt_start; private TextView txtinfo; private BluetoothAdapter mBluetooth; private int mOpenCode = 0x01; private BluetoothDevice deviceprint; //通过广播,接收周边蓝牙设备信息 private BroadcastReceiver discoveryReceiver = new BroadcastReceiver() { @SuppressLint("MissingPermission") @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.i("myapp", "action:" action); //获得已经搜索到的蓝牙设备 if (action.equals(BluetoothDevice.ACTION_FOUND)) {//发现新的蓝牙设备 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); Log.i("myapp", "Find:" device.getAddress()); Log.i("myapp", "Find:" device.getName()); if (device.getAddress().equals("99:32:88:A6:19:FE")) { //找到打印机直接使用 deviceprint = mBluetooth.getRemoteDevice("99:32:88:A6:19:FE"); mBluetooth.cancelDiscovery(); } } } }; @Override protected void onStart() { super.onStart(); //需要过滤多个动作,则调用IntentFilter对象的addAction添加新动作 IntentFilter discoveryFilter = new IntentFilter(); Log.i("myapp", "Reg discoverReceiver"); discoveryFilter.addAction(BluetoothDevice.ACTION_FOUND); //注册蓝牙设备搜索的广播接收器 Intent intent = registerReceiver(discoveryReceiver, discoveryFilter); } @Override protected void onStop() { super.onStop(); //注销蓝牙设备搜索的广播接收器 unregisterReceiver(discoveryReceiver); } @SuppressLint("MissingPermission") private void beginDiscovery() { //如果当前不是正在搜索,则开始新的搜索任务 if (mBluetooth.isDiscovering() != true) { Log.i("myapp", "Start find BlueTooth"); mBluetooth.startDiscovery();//开始扫描周围的蓝牙设备 } } @SuppressLint("MissingPermission") private void startBluetooth() { //弹出是否允许扫描蓝牙设备的选择对话框 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); startActivityForResult(intent, mOpenCode); } BluetoothSocket socket; @SuppressLint("MissingPermission") private void printtxt() { if (deviceprint == null) return; if (mBluetooth.isDiscovering()) mBluetooth.cancelDiscovery();//关闭扫描 UUID uuid= UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");//不可以修改,这个值对应打印机 try { socket = deviceprint.createRfcommSocketToServiceRecord(uuid); } catch (IOException e) { e.printStackTrace(); } new Thread() { //蓝牙连接必须新开一个线程 @Override public void run() { if (socket == null) return; try { if (socket.isConnected() == false)socket.connect(); String otxt = "OK\r\n打印汉字测试\r\nHeHe\r\n"; OutputStream outs = socket.getOutputStream(); //InputStream ins=socket.getInputStream(); outs.write(0x1B); outs.write(0x40); outs.flush(); //ESC初始化打印机 outs.write(0x1B); outs.write(0x61); outs.write(0x0); //0居左 1居中 2 居右 outs.flush(); //ESC初始化打印机 outs.write(otxt.getBytes("gb2312")); //这里必须与打印机内部编码一样,不然打印汉字是乱码 outs.flush(); //Log.i("myapp","input:" String.copyValueOf(ins.read()) outs.close(); Log.i("myapp", "print finish"); //socket.close(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } /** * 初始化蓝牙适配器 */ private void initBluetooth() { //Android从4.3开始增加支持BLE技术(即蓝牙4.0以上版本) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { //从系统服务中获取蓝牙管理器 BluetoothManager bm = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetooth = bm.getAdapter(); Log.i("myapp", "从系统服务中获取蓝牙管理器"); } else { //获取系统默认的蓝牙适配器 mBluetooth = BluetoothAdapter.getDefaultAdapter(); Log.i("myapp", "获取系统默认的蓝牙适配器"); } if (mBluetooth == null) { txtinfo.setText("本机没有蓝牙"); Log.i("findresult", "没有找到蓝牙"); } else { txtinfo.setText("本机有蓝牙设备"); Log.i("findresult", "本机有蓝牙"); startBluetooth(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_bt_open = (Button) findViewById(R.id.btn_bt_open); btn_bt_start = (Button) findViewById(R.id.btn_bt_print); txtinfo = (TextView) findViewById(R.id.textinfo); btn_bt_open.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { initBluetooth(); } }); btn_bt_start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { printtxt(); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == mOpenCode) {//来自允许蓝牙扫描的对话框 if (resultCode == 120) { txtinfo.setText("允许本地蓝牙被附近的其他蓝牙设备发现"); //延迟50毫秒后启动蓝牙设备的刷新任务 new Handler().postDelayed(new Runnable() { @Override public void run() { Log.i("myApp", "开始查找蓝牙..."); beginDiscovery(); } }, 50); } else { txtinfo.setText("不允许蓝牙被附近的其他蓝牙设备发现"); } } } }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="vertical" tools:layout_editor_absoluteX="1dp" tools:layout_editor_absoluteY="1dp"> <Button android:id="@ id/btn_bt_open" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="获取蓝牙" /> <Button android:id="@ id/btn_bt_print" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="蓝牙打印" /> <TextView android:id="@ id/textinfo" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="TextView" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>

,