Android 藍牙串口調試程序開發
前言:本次項目需要爲智能設備開發一個 App 於是就開始學習 Android 的藍牙串口通信方面的知識,現在 App 已經寫完了,當初學的時候走了不少彎路和嘗試,現作爲一名初學者和大家分享一下經驗,以及總結我該部分的學習。
〇、藍牙串口開發的流程
一、添加需要用到的權限
在 Android 項目中,打開 AndroidManif.xml 文件 添加對藍牙操作的權限。
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
二、檢測和打開藍牙設備
1.檢測藍牙設備
如果要通過藍牙進行串口通信,必須得保證藍牙設備正常工作,首先要檢測藍牙設備是否正常。通常情況下這一步也可以省略,或在打開藍牙設備的時候檢測。
//獲取默認藍牙設備
mADAPTER_BLUETOOTH = BluetoothAdapter.getDefaultAdapter();
//判斷是否有藍牙設備
if (mADAPTER_BLUETOOTH == null){
Toast.makeText(MainActivity.this, "未找到藍牙功能", LENGTH_SHORT).show();
}else {
Toast.makeText(MainActivity.this, "藍牙功能正常", LENGTH_SHORT).show();
通過 BluetoothAdapter 來獲取默認藍牙設備,值爲 null 說明藍牙功能缺失,反之則反。
2.打開藍牙設備
打開藍牙設備的方式一共有兩種,一種是直接通過使能藍牙打開,另一種是彈出對話框讓用戶確認打開。一般不使用第一種,而使用有交互的第二種。因爲如果我們打開藍牙,不打開藍牙可見的話,別人就發現不了我們的設備,對我們的項目沒有任何意義,所以在打開藍牙的同時會打開可見。
- 直接使能藍牙的方式
mADAPTER_BLUETOOTH = BluetoothAdapter.getDefaultAdapter();
mADAPTER_BLUETOOTH.enable();
-
具有人機交互的方式
構建一個打開藍牙的方法,可以把對藍牙操作的方法,寫在一個類裏面,方便調用。
//打開藍牙的方法
public void TurnOnBlueTooth(){
//獲取默認藍牙設備
mADAPTER_BLUETOOTH = BluetoothAdapter.getDefaultAdapter();
//判斷是否有藍牙設備
if (mADAPTER_BLUETOOTH == null){
Toast.makeText(MainActivity.this, "未找到藍牙功能", LENGTH_SHORT).show();
}else {
Toast.makeText(MainActivity.this, "藍牙功能正常", LENGTH_SHORT).show();
if (!mADAPTER_BLUETOOTH.isEnabled()){
//如果藍牙設備正常 藍牙沒打開 請求打開並可見
Intent TurnOn = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(TurnOn,1);
}else {
Toast.makeText(MainActivity.this, "藍牙已打開 請勿重複打開", LENGTH_SHORT).show();
}
}
}
-
藍牙狀態的廣播接收者
通過註冊一個藍牙狀態的廣播接收者,來接收藍牙狀態改變,給用戶提供一個跟友好的人機交互。
註冊廣播接收者
//藍牙狀態改變的廣播 //註冊和綁定廣播接收者和廣播
IntentFilter mBROADCAST_BLUETOOTH_STATE = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(RECEIVER_BLUETOOTH_STATE,mBROADCAST_BLUETOOTH_STATE);
寫藍牙狀態改變的廣播接收者
//藍牙打開狀態的廣播接收
private BroadcastReceiver RECEIVER_BLUETOOTH_STATE = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,-1);
switch (state){
case BluetoothAdapter.STATE_OFF:
Toast.makeText(MainActivity.this, "藍牙已關閉", LENGTH_SHORT).show();
break;
case BluetoothAdapter.STATE_ON:
Toast.makeText(MainActivity.this, "藍牙已打開", LENGTH_SHORT).show();
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Toast.makeText(MainActivity.this, "藍牙正在關閉", LENGTH_SHORT).show();
break;
case BluetoothAdapter.STATE_TURNING_ON:
Toast.makeText(MainActivity.this, "藍牙正在打開", LENGTH_SHORT).show();
break;
}
}
};
通過接受藍牙狀態改變的廣播,來給用戶相應的提示,形成良好的人機交互。
三、搜索並連接藍牙設備
1.搜索藍牙設備
搜索藍牙設備操作直接調用 BluetoothAdapter 的 startDiscovery 方法即可。同樣的,我們可以寫一個搜索藍牙的方法,把這個方法寫在 對藍牙操作的 類裏面,方便我們去調用。
public void DiscoveryBlueTooth() {
//獲取默認的藍牙設備
mADAPTER_BLUETOOTH = BluetoothAdapter.getDefaultAdapter();
//判斷是否在搜索
if (mADAPTER_BLUETOOTH.isDiscovering()) {
mADAPTER_BLUETOOTH.cancelDiscovery();
}
//開始搜索
mADAPTER_BLUETOOTH.startDiscovery();
Toast.makeText(MainActivity.this, "藍牙設備正在搜索中", LENGTH_SHORT).show();
}
這只是開啓搜索,我們搜索的結果該怎麼反饋給我們呢?我們需要一個容器來存放我們剛剛搜索到的設備。
- ListView 和 ListAdapter
我們在佈局文件中,加入一個Listview控件,來顯示我們搜索到的藍牙設備。寫好佈局後,給 Listview 的 item 寫一個佈局樣式。
<TextView
android:id="@+id/TV_BLUETOOTH_DEVICE"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="13sp"
android:textColor="@color/colorWhite"
android:background="@drawable/button_selector"/>
我這裏只用了一個 Textview
寫好了 Listview 我們需要一個東西,把搜索到的設備信息和 Listview 的內容綁定起來。這裏我用的是 ArrayList ,通過 ArrayAdapter 綁定設備信息和 Listview的內容。
//ArrayAdapter聲明
private ArrayAdapter mADAPTER_ARRAY;
//ArrayList定義:存放藍牙名稱和地址
private List<String> BLUETOOTH_DEViCE = new ArrayList<>();
/*
*通過 ArrayAdapter 綁定 ListView控件和ArrayList裏的數據
*/
//通過ArrayAdapter綁定數據
mADAPTER_ARRAY = new ArrayAdapter<String>(this,R.layout.list_view_item,R.id.TV_BLUETOOTH_DEVICE,BLUETOOTH_DEViCE);
//給ListView使用這個 adapter
mLV_DEVICE.setAdapter(mADAPTER_ARRAY);
到這裏,我們的準備工作就完成了,接下來就是把搜索到的設備信息添加進去。
- 接收搜索結果的廣播
在接受搜索設備的廣播之前,先註冊搜索設備的廣播,將接收者和廣播綁定。
//藍牙狀態改變的廣播 //註冊和綁定廣播接收者和廣播
IntentFilter mBROADCAST_BLUETOOTH_STATE = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(RECEIVER_BLUETOOTH_STATE,mBROADCAST_BLUETOOTH_STATE);
然後寫搜索藍牙設備的廣播接收者
private BroadcastReceiver RECEIVER_BLUETOOTH_FOUND = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
BLUETOOTH_DEViCE.add(" NAME:"+device.getName()+"(待連接)"+"\n"+" *MAC:"+device.getAddress());
mADAPTER_ARRAY.notifyDataSetChanged();
}
}
};
將搜索到的設備添加到 ArrayList 裏,然後就會在 Listview 上顯示。
2.連接藍牙設備
在完成了搜索設備之後,我們就需要與藍牙設備進行連接。與單片機上藍牙串口連接,我們需要藍牙串口服務的 UUID 00001101-0000-1000-8000-00805F9B34FB
需要一個 Sokect 來連接藍牙串口。
//設定本機的UUID->通用唯一識別碼 //串口UUID
private final UUID mUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
//定義BluetoothSocket
public static BluetoothSocket mBLUETOOTH_SOCKET;
我們給 Listview 的 item 設立一個點擊事件,好讓我們點擊一個設備的時候,可以連接該設備。在點擊事件中,我們通過 ArrayAdapter 的 getItem 方法,來獲取保存在 Array List裏面的設備信息。然後通過 “*” 定位我們需要的地址的起始位置,來獲取地址。通過 BluetoothDevice 的 GetRemoteDevice 方法,獲取該地址的的設備。用我們寫的 ConnectThread 的方法進行連接設備。
//LV_DEVICE 的 item 被點擊:
mLV_DEVICE.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//BluetoothDevice mBTD =
//獲取item的內容 //解析出內容裏的地址
String s = (String) mADAPTER_ARRAY.getItem(position);
if (mADAPTER_BLUETOOTH.isDiscovering())
{
mADAPTER_BLUETOOTH.cancelDiscovery();
}
if (s != null){
//在這裏我們通過*來定位地址的起始字符然後保存在 address裏
String address = s.substring(s.indexOf("*")+5).trim();
Toast.makeText(MainActivity.this,"MAC:"+address,Toast.LENGTH_LONG).show();
BluetoothDevice device = mADAPTER_BLUETOOTH.getRemoteDevice(address);
ConnectThread(device);
}
}
});
ConnectThread 方法
進行連接後,我們就開始數據接收服務
public void ConnectThread(BluetoothDevice device) {
try {
mBLUETOOTH_SOCKET = device.createRfcommSocketToServiceRecord(mUUID);
mADAPTER_BLUETOOTH.cancelDiscovery();
mBLUETOOTH_SOCKET.connect();
Toast.makeText(MainActivity.this, "連接成功", Toast.LENGTH_LONG).show();
// 接收數據進程
ReceiveData receiveThread = new ReceiveData();
// 連接成功後開啓接收數據服務
receiveThread.start();
} catch (IOException e) {
Toast.makeText(MainActivity.this, "連接失敗", Toast.LENGTH_SHORT)
.show();
try {
mBLUETOOTH_SOCKET.close();
} catch (IOException e2) {
e.printStackTrace();
}
}
}
既然要連接我們就要有一個取消連接的方法。關閉 Socket 來釋放資源。
// 取消鏈接的方法
public void CancelConnect() {
try {
mBLUETOOTH_SOCKET.close();
} catch (IOException e) {
e.printStackTrace();
}
}
在以上過程中,我們可以自己設立一個 Boolean 型的變量,來表示與設備的連接狀態,通過判斷該變量的值來做出相應的人機交互。
四、發送並接受串口數據
到這一步,我們的項目基本成型了,只剩下最後也是最關鍵的與 串口藍牙進行通信。我這裏採用的是發送 字符數據 和 接受 字符數據 進行交互。在發送的時候,會將字符數據轉化成字節流的形式發送。
1.發送數據部分
發送數據數據和接受數據,我們需要 輸入流 和 輸出流。
//定義輸出流//定義輸入流
public static OutputStream OS;
public static InputStream IS;
然後寫一個 write 的方法來調用 OutputStream 的 write 方法來寫一個字節。
//發送數據的方法
public static void write(String str) {
if (CONNECT_STATUS) {
try {
OS = mBLUETOOTH_SOCKET.getOutputStream();
if (OS != null){
OS.write(str.getBytes("UTF-8"));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
我們可以將 String 來源於 Edit Text 控件的內容,通過一個按鈕來執行發送的操作。
2.接收數據部分
接受數據的服務我們已經在連接那一步打開了,裏面用到的我們寫的一個接收數據的方法 ReceiveData 。並且在調用前,我們開啓了一個新的線程來做這件事情。
// 讀數據線程
private class ReceiveData extends Thread {
InputStream mmInStream;
private ReceiveData() {
try {
mmInStream = mBLUETOOTH_SOCKET.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
int bytes = 0;
byte[] buffer = new byte[256];
while (true) {
// 接收數據
try {
bytes = mmInStream.read(buffer);
final String readString = new String(buffer, 0, bytes);
runOnUiThread(new Runnable() {
@Override
public void run() {
mTV_ACCEPT.append(readString + " ");
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我們可以用一個 Textview 用來顯示我們接收到的數據。
五、總結學習
本次項目的學習主要難點在搜索添加設備和,讀寫數據上,很多內容還需要多家理解,對該部分的知識還不夠深刻,需要系統性的學習一下。
以下是 App 的實際效果。