最近課上剛好需要做一個(gè)課程設(shè)計(jì)關(guān)于藍(lán)牙的就挑選了個(gè)藍(lán)牙聊天室魔种,其實(shí)關(guān)鍵還是在于對(duì)藍(lán)牙API的了解
一.藍(lán)牙API
與藍(lán)牙開發(fā)主要的相關(guān)類是以下四個(gè)
- BluetoothAdapter
字面上則理解為藍(lán)牙適配器,打開藍(lán)牙,關(guān)閉藍(lán)牙扫倡,搜索設(shè)備缸榄,獲取藍(lán)牙Socket連接都是通過(guò)這個(gè)類來(lái)實(shí)現(xiàn)的五慈。對(duì)應(yīng)的方法就有- enable():用來(lái)打開藍(lán)牙,一般在5.0后的話則會(huì)出現(xiàn)彈框提示是否開啟藍(lán)牙衫哥,其他則沒(méi)有提示,也可以調(diào)用來(lái)進(jìn)行彈窗打開
Intent intent =new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent );
disable():關(guān)閉藍(lán)牙
getDefaultAdapter():獲取藍(lán)牙適配器BluetoothAdapter襟锐,只有這個(gè)方法來(lái)獲取
getAddress():獲取本地藍(lán)牙的MAC地址撤逢,這個(gè)則是每一個(gè)藍(lán)牙設(shè)備的唯一
getName():獲取本地藍(lán)牙的名稱
getRemoteDevice(String address):獲取藍(lán)牙地址獲取到遠(yuǎn)程的設(shè)備
startDiscovery():開啟藍(lán)牙設(shè)備搜索
cancelDiscovery():關(guān)閉掃描
listenUsingRfcommWithServiceRecord(serverSocketName,UUID):獲取BluetoothServerSocket
-
BluetoothDevice
代表一個(gè)藍(lán)牙設(shè)備createRfcommSocketToServiceRecord(UUIDuuid):根據(jù)UUID創(chuàng)建并返回一個(gè)BluetoothSocket
getName():獲取藍(lán)牙設(shè)備名稱
getAddress():獲取藍(lán)牙的MAC地址
BluetoothServerSocket:類似于ServerSocket,方法都差不多粮坞,可以說(shuō)藍(lán)牙之間的通訊跟Socket相似蚊荣。這個(gè)相當(dāng)于服務(wù)器Socket
accept():這個(gè)方法會(huì)阻塞,直到連接建立莫杈,用來(lái)監(jiān)聽連接
close():關(guān)閉socket連接
-
BluetoothSocket:相當(dāng)于客戶端Socket
connect():用來(lái)向服務(wù)器BluetoothServerSocket發(fā)起連接
getInputStream():獲取輸入流
getOutputStream():獲取輸出流
close():關(guān)閉連接
getRemoteDevice():獲取這個(gè)Socket連接的遠(yuǎn)程設(shè)備
二.搜索藍(lán)牙設(shè)備
知道對(duì)應(yīng)API后就可以進(jìn)行對(duì)應(yīng)的藍(lán)牙開發(fā)互例,這里以獲取藍(lán)牙設(shè)備為例子
1.獲取本地藍(lán)牙適配器
BluetoothAdapter
mAdapter= BluetoothAdapter.getDefaultAdapter();
2.打開藍(lán)牙
if(!mAdapter.isEnabled()){
//彈出對(duì)話框提示用戶是后打開
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
//不做提示,強(qiáng)行打開
// mAdapter.enable();
}
3.搜索設(shè)備
mAdapter.startDiscovery(); //開啟搜索
搜索設(shè)備的回調(diào)則需要通過(guò)注冊(cè)廣播的形式來(lái)獲取
//發(fā)現(xiàn)設(shè)備的Action
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
//設(shè)備搜索完畢的action
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
定義廣播
BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//當(dāng)掃描到設(shè)備的時(shí)候
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// 獲取設(shè)備對(duì)象
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//提取強(qiáng)度信息
int rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);
Log.e(TAG, device.getName() + "\n" + device.getAddress() + "\n強(qiáng)度:" + rssi);
} //搜索完成
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
Log.e(TAG, "onReceive: 搜索完成" );
}
}
};
之后就可以進(jìn)行個(gè)人的一些操作
三.藍(lán)牙聊天的實(shí)現(xiàn)
要實(shí)現(xiàn)藍(lán)牙聊天則涉及到藍(lán)牙之間的傳輸通信筝闹,前面也說(shuō)到了敲霍,這里肯定就是用到BluetoothServerSocket以及BluetoothSocket俊马。
藍(lán)牙傳輸通信相當(dāng)于服務(wù)器端與客戶端之間的通信,只不過(guò)不同是這里每一個(gè)藍(lán)牙設(shè)備本身自己既充當(dāng)服務(wù)器端也充當(dāng)客戶端肩杈,大致的關(guān)系就是
注意柴我,這些連接都是阻塞式的,都要放在線程里去執(zhí)行扩然。
1.對(duì)于BluetoothServerSocket艘儒,建立一個(gè)AcceptThread,來(lái)監(jiān)聽是否有設(shè)備發(fā)起連接
//連接等待線程
class AcceptThread extends Thread{
private final BluetoothServerSocket serverSocket;
public AcceptThread(){
BluetoothServerSocket tmp = null;
try {
//獲取實(shí)例
tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
} catch (IOException e) {
e.printStackTrace();
}
serverSocket = tmp;
}
@Override
public void run() {
super.run();
//監(jiān)聽是否有端口連接
BluetoothSocket socket = null;
while(mState != STATE_TRANSFER) {
try {
Log.e(TAG, "run: AcceptThread 阻塞調(diào)用夫偶,等待連接");
socket = serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "run: ActivityThread fail");
break;
}
//獲取到連接Socket后則開始通信
if(socket != null){
synchronized (BluetoothChatService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
//傳輸數(shù)據(jù)界睁,服務(wù)器端調(diào)用
Log.e(TAG, "run: 服務(wù)器AcceptThread傳輸" );
sendMessageToUi(MainActivity.BLUE_TOOTH_DIALOG , "正在與" + socket.getRemoteDevice().getName() + "連接");
//開始數(shù)據(jù)傳輸
dataTransfer(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_TRANSFER:
// 沒(méi)有準(zhǔn)備好或者終止連接
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket" + e);
}
break;
}
}
}
}
}
public void cancel(){
Log.e(TAG, "close: activity Thread" );
try {
if(serverSocket != null)
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "close: activity Thread fail");
}
}
}
可以看到,當(dāng)BluetoothServerSocket監(jiān)聽到有設(shè)備連接的時(shí)候兵拢,就會(huì)調(diào)用dataTransfer開啟一個(gè)數(shù)據(jù)傳輸翻斟。
2.同樣如何發(fā)起連接BluetoothSocket呢
需要一個(gè)ConnectThread來(lái)發(fā)起
class ConnectThread extends Thread{
private final BluetoothSocket socket;
private final BluetoothDevice device;
public ConnectThread(BluetoothDevice device) {
this.device = device;
BluetoothSocket mSocket = null;
try {
//獲取Socket
mSocket = device.createRfcommSocketToServiceRecord(
MY_UUID_SECURE);
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "ConnectThread: fail" );
sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "連接失敗,請(qǐng)重新連接");
}
socket = mSocket;
}
@Override
public void run() {
super.run();
//建立后取消掃描
bluetoothAdapter.cancelDiscovery();
try {
//開啟連接
socket.connect();
} catch (IOException e) {
e.printStackTrace();
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
Log.e(TAG, "run: unable to close" );
}
//TODO 連接失敗顯示
sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "連接失敗说铃,請(qǐng)重新連接");
BluetoothChatService.this.start();
}
// 重置
synchronized (BluetoothChatService.this) {
mConnectThread = null;
}
//連接建立访惜,開始傳輸
dataTransfer(socket, device);
}
public void cancel(){
try {
if(socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
之后建立連接之后就會(huì)調(diào)用dataTransfer來(lái)進(jìn)行數(shù)據(jù)傳輸融欧,同樣也需要一個(gè)線程來(lái)維護(hù)數(shù)據(jù)傳輸
class TransferThread extends Thread{
private final BluetoothSocket socket;
private final OutputStream out;
private final InputStream in;
public TransferThread(BluetoothSocket mBluetoothSocket){
socket = mBluetoothSocket;
OutputStream mOutputStream = null;
InputStream mInputStream = null;
try {
if(socket != null){
//獲取連接的輸入輸出流
mOutputStream = socket.getOutputStream();
mInputStream = socket.getInputStream();
}
} catch (IOException e) {
e.printStackTrace();
}
out = mOutputStream;
in = mInputStream;
isTransferError = false;
}
@Override
public void run() {
super.run();
//讀取數(shù)據(jù)
byte[] buffer = new byte[1024];
int bytes;
while (true){
try {
bytes = in.read(buffer);
//TODO 分發(fā)到主線程顯示
uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_READ, bytes, -1, buffer)
.sendToTarget();
Log.e(TAG, "run: read " + new String(buffer , 0 , bytes) );
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "run: Transform error" + e.toString());
BluetoothChatService.this.start();
//TODO 連接丟失顯示并重新開始連接
sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "設(shè)備連接失敗/傳輸關(guān)閉");
isTransferError = true;
break;
}
}
}
/**
* 寫入數(shù)據(jù)傳輸
* @param buffer
*/
public void write(byte[] buffer) {
try {
out.write(buffer);
//TODO 到到UI顯示
uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_WRAITE , -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write " + e);
}
}
public void cancel() {
try {
if(socket != null)
socket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed" + e);
}
}
}
藍(lán)牙聊天則是基于上面三個(gè)線程來(lái)進(jìn)行實(shí)現(xiàn)葛假,同樣,對(duì)于藍(lán)牙文件間的傳輸也是同個(gè)道理抡砂,通過(guò)輸入輸出流來(lái)進(jìn)行處理幼苛。之后的操作就比較容易處理了