亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android 藍(lán)牙連接 ESC/POS 熱敏打印機打印實例(藍(lán)牙連接篇)

 更新時間:2017年04月12日 10:25:29   作者:VitaminChen  
這篇文章主要介紹了Android 藍(lán)牙連接 ESC/POS 熱敏打印機打印實例(藍(lán)牙連接篇),具有一定的參考價值,感興趣的小伙伴們可以參考一下。

公司的一個手機端的 CRM 項目最近要增加小票打印的功能,就是我們點外賣的時候經(jīng)常會見到的那種小票。這里主要涉及到兩大塊的知識:

  1. 藍(lán)牙連接及數(shù)據(jù)傳輸
  2. ESC/POS 打印指令

藍(lán)牙連接不用說了,太常見了,這篇主要介紹這部分的內(nèi)容。但ESC/POS 打印指令是個什么鬼?簡單說,我們常見的熱敏小票打印機都支持這樣一種指令,只要按照指令的格式向打印機發(fā)送指令,哪怕是不同型號品牌的打印機也會執(zhí)行相同的動作。比如打印一行文本,換行,加粗等都有對應(yīng)的指令,這部分內(nèi)容放在下一篇介紹。

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

1. 藍(lán)牙權(quán)限

想要使用藍(lán)牙功能,首先要在 AndroidManifest 配置文件中聲明藍(lán)牙權(quán)限:

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

BLUETOOTH 權(quán)限只允許建立藍(lán)牙連接以及傳輸數(shù)據(jù),但是如果要進(jìn)行藍(lán)牙設(shè)備發(fā)現(xiàn)等操作的話,還需要申請 BLUETOOTH_ADMIN 權(quán)限。

2. 初始配置

這里主要用到一個類BluetoothAdapter。用法很簡單,直接看代碼:

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

單例模式,全局只有一個實例,只要為 null,就代表設(shè)備不支持藍(lán)牙,那么需要有相應(yīng)的處理。

如果設(shè)備支持藍(lán)牙,那么接著檢查藍(lán)牙是否打開:

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

如果藍(lán)牙未打開,那么執(zhí)行 startActivityForResult() 后,會彈出一個對話框詢問是否要打開藍(lán)牙,點擊`是`之后就會自動打開藍(lán)牙。成功打開藍(lán)牙后就會回調(diào)到 onActivityResult()。

除了主動的打開藍(lán)牙,還可以監(jiān)聽 BluetoothAdapter.ACTION_STATE_CHANGED
廣播,包含EXTRA_STATEEXTRA_PREVIOUS_STATE兩個 extra 字段,可能的取值包括 STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, and STATE_OFF。含義很清楚了,不解釋。

3. 發(fā)現(xiàn)設(shè)備

初始化完成之后,藍(lán)牙打開了,接下來就是掃描附近的設(shè)備,只需要一句話:

mBluetoothAdapter.startDiscovery();

不過這樣只是開始執(zhí)行設(shè)備發(fā)現(xiàn),這肯定是一個異步的過程,我們需要注冊一個廣播,監(jiān)聽發(fā)現(xiàn)設(shè)備的廣播,直接上代碼:

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
 public void onReceive(Context context, Intent intent) {
  String action = intent.getAction();

  // 當(dāng)有設(shè)備被發(fā)現(xiàn)的時候會收到 action == BluetoothDevice.ACTION_FOUND 的廣播
  if (BluetoothDevice.ACTION_FOUND.equals(action)) {

   //廣播的 intent 里包含了一個 BluetoothDevice 對象
   BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

   //假設(shè)我們用一個 ListView 展示發(fā)現(xiàn)的設(shè)備,那么每收到一個廣播,就添加一個設(shè)備到 adapter 里
   mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
  }
 }
};
// 注冊廣播監(jiān)聽
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

注釋已經(jīng)寫的很清楚了,除了 BluetoothDevice.EXTRA_DEVICE 之外,還有一個 extra 字段 BluetoothDevice.EXTRA_CLASS, 可以得到一個 BluetoothClass 對象,主要用來保存設(shè)備的一些額外的描述信息,比如可以知道這是否是一個音頻設(shè)備。

關(guān)于設(shè)備發(fā)現(xiàn),有兩點需要注意:

startDiscovery() 只能掃描到那些狀態(tài)被設(shè)為 可發(fā)現(xiàn) 的設(shè)備。安卓設(shè)備默認(rèn)是不可發(fā)現(xiàn)的,要改變設(shè)備為可發(fā)現(xiàn)的狀態(tài),需要如下操作:

Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//設(shè)置可被發(fā)現(xiàn)的時間,00s
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(intent);

執(zhí)行之后會彈出對話窗詢問是否允許設(shè)備被設(shè)為可發(fā)現(xiàn)的狀態(tài),點擊`是`之后設(shè)備即被設(shè)為可發(fā)現(xiàn)的狀態(tài)。

startDiscovery()是一個十分耗費資源的操作,所以需要及時的調(diào)用cancelDiscovery()來釋放資源。比如在進(jìn)行設(shè)備連接之前,一定要先調(diào)用cancelDiscovery()

4. 設(shè)備配對與連接

4.1 配對

當(dāng)與一個設(shè)備第一次進(jìn)行連接操作的時候,屏幕會彈出提示框詢問是否允許配對,只有配對成功之后,才能建立連接。

系統(tǒng)會保存所有的曾經(jīng)成功配對過的設(shè)備信息。所以在執(zhí)行startDiscovery()之前,可以先嘗試查找已配對設(shè)備,因為這是一個本地信息讀取的過程,所以比startDiscovery()要快得多,也避免占用過多資源。如果設(shè)備在藍(lán)牙信號的覆蓋范圍內(nèi),就可以直接發(fā)起連接了。

查找配對設(shè)備的代碼如下:

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

代碼很簡單,不解釋了,就是調(diào)用BluetoothAdapter.getBondedDevices()得到一個 Set<BluetoothDevice> 并遍歷取得已配對的設(shè)備信息。

4.2 連接

藍(lán)牙設(shè)備的連接和網(wǎng)絡(luò)連接的模型十分相似,都是Client-Server 模式,都通過一個 socket 來進(jìn)行數(shù)據(jù)傳輸。那么作為一個 Android 設(shè)備,就存在三種情況:

  1. 只作為 Client 端發(fā)起連接
  2. 只作為 Server 端等待別人發(fā)起建立連接的請求
  3. 同時作為 Client 和 Server

因為是為了下一篇介紹連接熱敏打印機打印做鋪墊,所以這里先講 Android 設(shè)備作為 Client 建立連接的情況。因為打印機是不可能主動跟 Android 設(shè)備建立連接的,所以打印機必然是作為 Server 被連接。

4.2.1 作為 Client 連接

  1. 首先需要獲取一個 BluetoothDevice 對象。獲取的方法前面其實已經(jīng)介紹過了,可以通過調(diào)用 startDiscovery()并監(jiān)聽廣播獲得,也可以通過查詢已配對設(shè)備獲得。
  2. 通過 BluetoothDevice.createRfcommSocketToServiceRecord(UUID) 得到BluetoothSocket 對象
  3. 通過BluetoothSocket.connect()建立連接
  4. 異常處理以及連接關(guān)閉

廢話不多說,上代碼:

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() {
  // 建立連接前記得取消設(shè)備發(fā)現(xiàn)
  mBluetoothAdapter.cancelDiscovery();
  try {
   // 耗時操作,所以必須在主線程之外進(jìn)行
   mmSocket.connect();
  } catch (IOException connectException) {
   //處理連接建立失敗的異常
   try {
    mmSocket.close();
   } catch (IOException closeException) { }
   return;
  }
  doSomething(mmSocket);
 }

 //關(guān)閉一個正在進(jìn)行的連接
 public void cancel() {
  try {
   mmSocket.close();
  } catch (IOException e) { }
 }
}

device.createRfcommSocketToServiceRecord(MY_UUID) 這里需要傳入一個 UUID,這個UUID 需要格外注意一下。簡單的理解,它是一串約定格式的字符串,用來唯一的標(biāo)識一種藍(lán)牙服務(wù)。

Client 發(fā)起連接時傳入的 UUID 必須要和 Server 端設(shè)置的一樣!否則就會報錯!

如果是連接熱敏打印機這種情況,不知道 Server 端設(shè)置的 UUID 是什么怎么辦?
不用擔(dān)心,因為一些常見的藍(lán)牙服務(wù)協(xié)議已經(jīng)有約定的 UUID。比如我們連接熱敏打印機是基于 SPP 串口通信協(xié)議,其對應(yīng)的 UUID 是 "00001101-0000-1000-8000-00805F9B34FB",所以實際的調(diào)用是這樣:

復(fù)制代碼 代碼如下:

device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"))

其他常見的藍(lán)牙服務(wù)的UUID大家可以自行搜索。如果只是用于自己的應(yīng)用之間的通信的話,那么理論上可以隨便定義一個 UUID,只要 server 和 client 兩邊使用的 UUID 一致即可。

4.2.2 作為 Server 連接

  1. 通過BluetoothAdapter.listenUsingRfcommWithServiceRecord(String, UUID)獲取一個 BluetoothServerSocket 對象。這里傳入的第一個參數(shù)用來設(shè)置服務(wù)的名稱,當(dāng)其他設(shè)備掃描的時候就會顯示這個名稱。UUID 前面已經(jīng)介紹過了。
  2. 調(diào)用BluetoothServerSocket.accept()開始監(jiān)聽連接請求。這是一個阻塞操作,所以當(dāng)然也要放在主線程之外進(jìn)行。當(dāng)該操作成功執(zhí)行,即有連接建立的時候,會返回一個BluetoothSocket 對象。
  3. 調(diào)用 BluetoothServerSocket.close() 會關(guān)閉監(jiān)聽連接的服務(wù),但是當(dāng)前已經(jīng)建立的鏈接并不會受影響。

還是看代碼吧:

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;
   }
   //直到有有連接建立,才跳出死循環(huán)
   if (socket != null) {
    //要在新開的線程執(zhí)行,因為連接建立后,當(dāng)前線程可能會關(guān)閉
    doSomething(socket);
    mmServerSocket.close();
    break;
   }
  }
 }

 public void cancel() {
  try {
   mmServerSocket.close();
  } catch (IOException e) { }
 }
}

5. 數(shù)據(jù)傳輸

終于經(jīng)過了前面的4步,萬事俱備只欠東風(fēng)。而最后這一部分其實是最簡單的,因為就只是簡單的利用 InputStream OutputStream進(jìn)行數(shù)據(jù)的收發(fā)。

示例代碼:

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 取數(shù)據(jù)
  while (true) {
   try {
    bytes = mmInStream.read(buffer);
    mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
      .sendToTarget();
   } catch (IOException e) {
    break;
   }
  }
 }

 //向 Server 寫入數(shù)據(jù)
 public void write(byte[] bytes) {
  try {
   mmOutStream.write(bytes);
  } catch (IOException e) { }
 }

 public void cancel() {
  try {
   mmSocket.close();
  } catch (IOException e) { }
 }
}

下一篇介紹通過手機操作熱敏打印機打印的時候,還會用到這部分內(nèi)容,所以這里就先不多講了。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論