Android實現(xiàn)藍牙聊天功能
藍牙,時下最流行的智能設(shè)備傳輸數(shù)據(jù)的方式之一,通過手機app和智能設(shè)備進行連接,獲取設(shè)備上的測量數(shù)據(jù),我們生活中隨處可見的比如藍牙智能手環(huán),藍牙電子秤,藍牙心電測量設(shè)備等等。
本篇我將緊接著上篇結(jié)尾所寫,一起來看下手機之間如何通過藍牙實現(xiàn)文字聊天。
先貼出上篇的一些demo;

當點擊圖上的兩個列表中的任何一個列表,執(zhí)行如下代碼:
mBtAdapter.cancelDiscovery(); String info = ((TextView) v).getText().toString(); String address = info.substring(info.length() - 17); Intent intent = new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS, address); setResult(Activity.RESULT_OK, intent); finish();
此藍牙聊天工具最后實現(xiàn)的效果是這樣的:

將回到聊天主界面:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
LogUtils.getInstance().e(getClass(), "onActivityResult " + resultCode);
switch (requestCode) {
case REQUEST_CONNECT_DEVICE:
// 當DeviceListActivity返回與設(shè)備連接的消息
if (resultCode == Activity.RESULT_OK) {
// 連接設(shè)備的MAC地址
String address = data.getExtras().getString(
DeviceListActivity.EXTRA_DEVICE_ADDRESS);
// 得到藍牙對象
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
// 開始連接設(shè)備
mChatService.connect(device);
}
break;
case REQUEST_ENABLE_BT:
// 判斷藍牙是否啟用
if (resultCode == Activity.RESULT_OK) {
// 建立連接
setupChat();
} else {
LogUtils.getInstance().e(getClass(), "藍牙未啟用");
Toast.makeText(this, R.string.bt_not_enabled_leaving,
Toast.LENGTH_SHORT).show();
finish();
}
}
}
在此,我將重點介紹下BluetoothChatService類中的連接流程;
因為藍牙聊天是兩個手機之間進行通訊,所以他們互為主機和從機,主要思路以及步驟如下:
1.開一個線程獲取socket去連接藍牙;
2.開一個線程獲監(jiān)聽藍牙傳入的連接,如果連接被接受的話,再開啟第三個線程去處理所有傳入和傳出的數(shù)據(jù);
public synchronized void connect(BluetoothDevice device) {
if (mState == STATE_CONNECTING) {
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
mConnectThread = new ConnectThread(device);
mConnectThread.start();
setState(STATE_CONNECTING);
}
開線程去連接
/**
* @description:藍牙連接線程
* @author:zzq
* @time: 2016-8-6 下午1:18:41
*/
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
LogUtils.getInstance().e(getClass(), "socket獲取失?。? + e);
}
mmSocket = tmp;
}
public void run() {
LogUtils.getInstance().e(getClass(), "開始mConnectThread");
setName("ConnectThread");
// mAdapter.cancelDiscovery();
try {
mmSocket.connect();
} catch (IOException e) {
// 連接失敗,更新ui
connectionFailed();
try {
mmSocket.close();
} catch (IOException e2) {
LogUtils.getInstance().e(getClass(), "關(guān)閉連接失敗" + e2);
}
// 開啟聊天接收線程
startChat();
return;
}
synchronized (BluetoothChatService.this) {
mConnectThread = null;
}
connected(mmSocket, mmDevice);
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
LogUtils.getInstance().e(getClass(), "關(guān)閉連接失敗" + e);
}
}
}
/**
* 監(jiān)聽傳入的連接
*/
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
BluetoothServerSocket tmp = null;
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (IOException e) {
LogUtils.getInstance().e(getClass(), "--獲取socket失敗:" + e);
}
mmServerSocket = tmp;
}
public void run() {
setName("AcceptThread");
BluetoothSocket socket = null;
while (mState != STATE_CONNECTED) {
LogUtils.getInstance().e(getClass(), "----accept-循環(huán)執(zhí)行中-");
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
LogUtils.getInstance().e(getClass(), "accept() 失敗" + e);
break;
}
// 如果連接被接受
if (socket != null) {
synchronized (BluetoothChatService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// 開始連接線程
connected(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_CONNECTED:
// 沒有準備好或已經(jīng)連接
try {
socket.close();
} catch (IOException e) {
LogUtils.getInstance().e(getClass(),"不能關(guān)閉這些連接" + e);
}
break;
}
}
}
}
LogUtils.getInstance().e(getClass(), "結(jié)束mAcceptThread");
}
public void cancel() {
LogUtils.getInstance().e(getClass(), "取消 " + this);
try {
mmServerSocket.close();
} catch (IOException e) {
LogUtils.getInstance().e(getClass(), "關(guān)閉失敗" + e);
}
}
}
/**
* 連接成功后的線程 處理所有傳入和傳出的傳輸
*/
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;
// 得到BluetoothSocket輸入和輸出流
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
LogUtils.getInstance().e(getClass(),"temp sockets not created" + e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
int bytes;
String str1 = "";
// 循環(huán)監(jiān)聽消息
while (true) {
try {
byte[] buffer = new byte[256];
bytes = mmInStream.read(buffer);
String readStr = new String(buffer, 0, bytes);// 字節(jié)數(shù)組直接轉(zhuǎn)換成字符串
String str = bytes2HexString(buffer).replaceAll("00", "").trim();
if (bytes > 0) {// 將讀取到的消息發(fā)到主線程
mHandler.obtainMessage(BluetoothChatActivity.MESSAGE_READ, bytes, -1,buffer).sendToTarget();
} else {
LogUtils.getInstance().e(getClass(),"disconnected");
connectionLost();
if (mState != STATE_NONE) {
LogUtils.getInstance().e(getClass(), "disconnected");
startChat();
}
break;
}
} catch (IOException e) {
LogUtils.getInstance().e(getClass(), "disconnected" + e);
connectionLost();
if (mState != STATE_NONE) {
// 在重新啟動監(jiān)聽模式啟動該服務(wù)
startChat();
}
break;
}
}
}
/**
* 寫入OutStream連接
*
* @param buffer
* 要寫的字節(jié)
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// 把消息傳給UI
mHandler.obtainMessage(BluetoothChatActivity.MESSAGE_WRITE, -1,-1, buffer).sendToTarget();
} catch (IOException e) {
LogUtils.getInstance().e(getClass(),
"Exception during write:" + e);
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
LogUtils.getInstance().e(getClass(),"close() of connect socket failed:" + e);
}
}
}
大概的流程就是上面三個線程里面所展現(xiàn)的,當然具體情況,根據(jù)項目來,比如藍牙協(xié)議協(xié)議解析這塊的根據(jù)協(xié)議定義的方式來進行解析;
代碼中牽扯的到的藍牙連接狀態(tài)的改變,用到的handle,直接把狀態(tài)發(fā)送至activity,通知activity更新;
/**
* 無法連接,通知Activity
*/
private void connectionFailed() {
setState(STATE_LISTEN);
Message msg = mHandler.obtainMessage(BluetoothChatActivity.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(BluetoothChatActivity.TOAST, "無法連接設(shè)備");
msg.setData(bundle);
mHandler.sendMessage(msg);
}
/**
* 設(shè)備斷開連接,通知Activity
*/
private void connectionLost() {
Message msg = mHandler.obtainMessage(BluetoothChatActivity.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(BluetoothChatActivity.TOAST, "設(shè)備斷開連接");
msg.setData(bundle);
mHandler.sendMessage(msg);
}
當點擊發(fā)送按鈕時,將文本輸入框中的文字發(fā)送數(shù)據(jù)的方法:
private void sendMessage(String message) {
if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
Toast.makeText(this, R.string.not_connected,Toast.LENGTH_SHORT).show();
return;
}
if (message.length() > 0) {
byte[] send = message.getBytes();
mChatService.write(send);
}
}
//調(diào)用BluetoothChatService類中的write進行數(shù)據(jù)發(fā)送
public void write(byte[] out) {
ConnectedThread r;
synchronized (this) {
if (mState != STATE_CONNECTED)
return;
r = mConnectedThread;
}
r.write(out);
}
如此,藍牙聊天的流程就是這樣,如果退出聊天的時候,停止所有線程;
public synchronized void stop() {
LogUtils.getInstance().e(getClass(), "---stop()");
setState(STATE_NONE);
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mAcceptThread != null) {
mAcceptThread.cancel();
mAcceptThread = null;
}
}
相信看完本篇文章,在安卓藍牙連接這塊應(yīng)該問題不大了(spp協(xié)議)。
源碼地址:點我查看源碼
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android開發(fā)實現(xiàn)長按返回鍵彈出關(guān)機框功能
這篇文章主要介紹了Android開發(fā)實現(xiàn)長按返回鍵彈出關(guān)機框功能,涉及Android針對長按事件的響應(yīng)與處理相關(guān)操作技巧,需要的朋友可以參考下2017-09-09
Android實現(xiàn)Unity3D下RTMP推送的示例
像Unity3D下的RTMP或RTSP播放器一樣,好多開發(fā)者苦于在Unity環(huán)境下,如何高效率低延遲的把數(shù)據(jù)采集并編碼實時推送到流媒體服務(wù)器,實現(xiàn)Unity場景下的低延遲推拉流方案。本文介紹幾種RTMP推送的方案2021-06-06
關(guān)于Android Studio封裝SDK的那些事兒
這篇文章主要給大家介紹了關(guān)于Android Studio封裝SDK的那些事兒,文中通過圖文以及示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧2018-09-09
詳解Kotlin Android開發(fā)中的環(huán)境配置
這篇文章主要介紹了詳解Kotlin Android開發(fā)中的環(huán)境配置的相關(guān)資料,需要的朋友可以參考下2017-06-06

