Android藍牙開發(fā)——實現(xiàn)藍牙聊天

一.藍牙API

與藍牙開發(fā)主要的相關(guān)類是以下四個

BluetoothAdapter

字面上則理解為藍牙適配器鱼冀,打開藍牙蒋腮,關(guān)閉藍牙割择,搜索設(shè)備,獲取藍牙Socket連接都是通過這個類來實現(xiàn)的冒萄。對應(yīng)的方法就有

enable():用來打開藍牙臊岸,一般在5.0后的話則會出現(xiàn)彈框提示是否開啟藍牙,其他則沒有提示宦言,也可以調(diào)用來進行彈窗打開

Intent intent =newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);? ? startActivity(intent );

disable():關(guān)閉藍牙

getDefaultAdapter():獲取藍牙適配器BluetoothAdapter扇单,只有這個方法來獲取

getAddress():獲取本地藍牙的MAC地址商模,這個則是每一個藍牙設(shè)備的唯一

getName():獲取本地藍牙的名稱

getRemoteDevice(String address):獲取藍牙地址獲取到遠程的設(shè)備

startDiscovery():開啟藍牙設(shè)備搜索

cancelDiscovery():關(guān)閉掃描

listenUsingRfcommWithServiceRecord(serverSocketName,UUID):獲取BluetoothServerSocket

BluetoothDevice

代表一個藍牙設(shè)備

createRfcommSocketToServiceRecord(UUIDuuid):根據(jù)UUID創(chuàng)建并返回一個BluetoothSocket

getName():獲取藍牙設(shè)備名稱

getAddress():獲取藍牙的MAC地址

BluetoothServerSocket:類似于ServerSocket奠旺,方法都差不多,可以說藍牙之間的通訊跟Socket相似施流。這個相當(dāng)于服務(wù)器Socket

accept():這個方法會阻塞响疚,直到連接建立,用來監(jiān)聽連接

close():關(guān)閉socket連接

BluetoothSocket:相當(dāng)于客戶端Socket

connect():用來向服務(wù)器BluetoothServerSocket發(fā)起連接

getInputStream():獲取輸入流

getOutputStream():獲取輸出流

close():關(guān)閉連接

getRemoteDevice():獲取這個Socket連接的遠程設(shè)備

二.搜索藍牙設(shè)備

知道對應(yīng)API后就可以進行對應(yīng)的藍牙開發(fā)瞪醋,這里以獲取藍牙設(shè)備為例子

1.獲取本地藍牙適配器

? ? ? BluetoothAdapter

mAdapter= BluetoothAdapter.getDefaultAdapter();

2.打開藍牙

if(!mAdapter.isEnabled()){//彈出對話框提示用戶是后打開Intent enabler =newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enabler, REQUEST_ENABLE);//不做提示忿晕,強行打開// mAdapter.enable();

}

3.搜索設(shè)備

mAdapter.startDiscovery();//開啟搜索

搜索設(shè)備的回調(diào)則需要通過注冊廣播的形式來獲取

//發(fā)現(xiàn)設(shè)備的ActionIntentFilter filter =newIntentFilter(BluetoothDevice.ACTION_FOUND);? registerReceiver(mReceiver, filter);//設(shè)備搜索完畢的actionfilter =newIntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);? registerReceiver(mReceiver, filter);

定義廣播

BroadcastReceiver mReceiver =newBroadcastReceiver() {publicvoidonReceive(Context context, Intent intent){? ? ? ? ? String action = intent.getAction();//當(dāng)掃描到設(shè)備的時候if(BluetoothDevice.ACTION_FOUND.equals(action)) {// 獲取設(shè)備對象BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);//提取強度信息intrssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);? ? ? ? ? ? Log.e(TAG, device.getName() +"\n"+ device.getAddress() +"\n強度:"+ rssi);? ? ? ? ? ? ? ? ? }//搜索完成elseif(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {? ? ? ? ? ? ? Log.e(TAG,"onReceive: 搜索完成");? ? ? ? }? ? }? };

之后就可以進行個人的一些操作

三.藍牙聊天的實現(xiàn)

要實現(xiàn)藍牙聊天則涉及到藍牙之間的傳輸通信,前面也說到了银受,這里肯定就是用到BluetoothServerSocket以及BluetoothSocket践盼。

藍牙傳輸通信相當(dāng)于服務(wù)器端與客戶端之間的通信,只不過不同是這里每一個藍牙設(shè)備本身自己既充當(dāng)服務(wù)器端也充當(dāng)客戶端宾巍,大致的關(guān)系就是

藍牙連接

注意咕幻,這些連接都是阻塞式的,都要放在線程里去執(zhí)行顶霞。

1.對于BluetoothServerSocket肄程,建立一個AcceptThread,來監(jiān)聽是否有設(shè)備發(fā)起連接

//連接等待線程classAcceptThreadextendsThread{privatefinalBluetoothServerSocket serverSocket;publicAcceptThread(){? ? ? ? ? ? BluetoothServerSocket tmp =null;try{//獲取實例tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);? ? ? ? ? ? }catch(IOException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }? ? ? ? ? ? serverSocket = tmp;? ? ? ? }@Overridepublicvoidrun(){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) {caseSTATE_LISTEN:caseSTATE_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;caseSTATE_NONE:caseSTATE_TRANSFER:// 沒有準(zhǔn)備好或者終止連接try{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? socket.close();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }catch(IOException e) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Log.e(TAG,"Could not close unwanted socket"+ e);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }break;? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }publicvoidcancel(){? ? ? ? ? ? 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è)備連接的時候古徒,就會調(diào)用dataTransfer開啟一個數(shù)據(jù)傳輸拓提。

2.同樣如何發(fā)起連接BluetoothSocket呢

需要一個ConnectThread來發(fā)起

classConnectThreadextendsThread{privatefinalBluetoothSocket socket;privatefinalBluetoothDevice device;publicConnectThread(BluetoothDevice device){this.device = device;? ? ? ? ? ? BluetoothSocket mSocket =null;try{//獲取SocketmSocket = device.createRfcommSocketToServiceRecord(? ? ? ? ? ? ? ? ? ? ? ? MY_UUID_SECURE);? ? ? ? ? ? }catch(IOException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? Log.e(TAG,"ConnectThread: fail");? ? ? ? ? ? ? ? sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST ,"連接失敗,請重新連接");? ? ? ? ? ? }? ? ? ? ? ? socket = mSocket;? ? ? ? }@Overridepublicvoidrun(){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 ,"連接失敗隧膘,請重新連接");? ? ? ? ? ? ? ? BluetoothChatService.this.start();? ? ? ? ? ? }// 重置synchronized(BluetoothChatService.this) {? ? ? ? ? ? ? ? mConnectThread =null;? ? ? ? ? ? }//連接建立代态,開始傳輸dataTransfer(socket, device);? ? ? ? }publicvoidcancel(){try{if(socket !=null)? ? ? ? ? ? ? ? ? ? ? ? socket.close();? ? ? ? ? ? ? ? }catch(IOException e) {? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? }? ? ? ? }? ? }

之后建立連接之后就會調(diào)用dataTransfer來進行數(shù)據(jù)傳輸,同樣也需要一個線程來維護數(shù)據(jù)傳輸

classTransferThreadextendsThread{privatefinalBluetoothSocket socket;privatefinalOutputStream out;privatefinalInputStream in;publicTransferThread(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;? ? ? ? }@Overridepublicvoidrun(){super.run();//讀取數(shù)據(jù)byte[] buffer =newbyte[1024];intbytes;while(true){try{? ? ? ? ? ? ? ? ? ? bytes = in.read(buffer);//TODO 分發(fā)到主線程顯示uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_READ, bytes, -1, buffer)? ? ? ? ? ? ? ? ? ? ? ? ? ? .sendToTarget();? ? ? ? ? ? ? ? ? ? Log.e(TAG,"run: read "+newString(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ù)傳輸? ? ? ? *@parambuffer? ? ? ? */publicvoidwrite(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);? ? ? ? ? ? }? ? ? ? }publicvoidcancel(){try{if(socket !=null)? ? ? ? ? ? ? ? ? ? socket.close();? ? ? ? ? ? }catch(IOException e) {? ? ? ? ? ? ? ? Log.e(TAG,"close() of connect socket failed"+ e);? ? ? ? ? ? }? ? ? ? }? ? }

藍牙聊天則是基于上面三個線程來進行實現(xiàn)舀寓,同樣胆数,對于藍牙文件間的傳輸也是同個道理,通過輸入輸出流來進行處理。之后的操作就比較容易處理了

四.簡單實現(xiàn)

https://github.com/ZengZeHong/BlueToothChat

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末市殷,一起剝皮案震驚了整個濱河市炊汹,隨后出現(xiàn)的幾起案子褐着,更是在濱河造成了極大的恐慌判莉,老刑警劉巖豆挽,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異券盅,居然都是意外死亡帮哈,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門锰镀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娘侍,“玉大人,你說我怎么就攤上這事泳炉『斗ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵花鹅,是天一觀的道長氧腰。 經(jīng)常有香客問我,道長刨肃,這世上最難降的妖魔是什么古拴? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮真友,結(jié)果婚禮上黄痪,老公的妹妹穿的比我還像新娘。我一直安慰自己锻狗,他們只是感情好满力,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著轻纪,像睡著了一般油额。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上刻帚,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天潦嘶,我揣著相機與錄音,去河邊找鬼崇众。 笑死掂僵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的顷歌。 我是一名探鬼主播锰蓬,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼眯漩!你這毒婦竟也來了芹扭?” 一聲冷哼從身側(cè)響起麻顶,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舱卡,沒想到半個月后辅肾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡轮锥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年矫钓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舍杜。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡新娜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蝴簇,到底是詐尸還是另有隱情杯活,我是刑警寧澤匆帚,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布熬词,位于F島的核電站,受9級特大地震影響吸重,放射性物質(zhì)發(fā)生泄漏互拾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一嚎幸、第九天 我趴在偏房一處隱蔽的房頂上張望颜矿。 院中可真熱鬧,春花似錦嫉晶、人聲如沸骑疆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽箍铭。三九已至,卻和暖如春椎镣,著一層夾襖步出監(jiān)牢的瞬間诈火,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工状答, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冷守,地道東北人。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓惊科,卻偏偏與公主長得像拍摇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子馆截,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容