Android - 傳統藍牙通訊聊天

Android -傳統藍牙通訊聊天

技術:java+Android4.4+jdk1.8

概述

Android 傳統藍牙的使用,包括開關藍牙、搜索設備、藍牙鏈接、通訊等。

詳細

原文地址html

Android 藍牙開發(一)藍牙通訊 CSDNjava

Android 藍牙開發(一)藍牙通訊 簡書android

1、準備工做

 

開發環境:安全

    jdk1.8 服務器

    Eclipse Luna Service Release 1 (4.4.1)異步

運行環境:socket

    華爲榮耀6(Android4.4)、華爲p9(Android7.0)ide

實現功能:函數

  • Android 藍牙開發 (開關藍牙、搜索設備、藍牙配對、鏈接、通訊、斷開鏈接等)。ui

2、代碼結構

代碼包裏面,有兩個部分,一個是源碼,一個是V7支持包。

屏幕快照 2017-07-20 下午5.54.54.png

3、程序實現-藍牙通訊

1 藍牙基本操做

 

隨着可穿戴設備的流行,研究藍牙是必不可少的一門技術了。

總結了下藍牙開發使用的一些東西分享一下。

 

藍牙權限

首先須要AndroidManifest.xml文件中添加操做藍牙的權限。

1
2
3
4
< uses-permissionandroid:name = "Android.permission.BLUETOOTH"  />
//容許程序鏈接到已配對的藍牙設備。
< uses-permissionandroid:name = "android.permission.BLUETOOTH_ADMIN"  />
//容許程序發現和配對藍牙設備。

 

BluetoothAdapter

操做藍牙主要用到的類 BluetoothAdapter類,使用時導包
import android.bluetooth.BluetoothAdapter;
源碼具體位置frameworks/base/core/Java/android/bluetooth/BluetoothAdapter.java

BluetoothAdapter 表明本地設備的藍牙適配器。該BluetoothAdapter能夠執行基本的藍牙任務,例如啓
動設備發現,查詢配對的設備列表,使用已知的MAC地址實例化一個BluetoothDevice類,並建立一個
BluetoothServerSocket監聽來自其餘設備的鏈接請求。

獲取藍牙適配器

1
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

開啓藍牙

1
2
3
4
5
6
7
if (!mBluetoothAdapter.isEnabled()){  
//彈出對話框提示用戶是後打開  
Intent enabler =  new  Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
startActivityForResult(enabler, REQUEST_ENABLE);  
       //不作提示,直接打開,不建議用下面的方法,有的手機會有問題。  
       // mBluetoothAdapter.enable();  
}

 

獲取本地藍牙信息

1
2
3
4
5
6
7
8
9
10
11
//獲取本機藍牙名稱  
String name = mBluetoothAdapter.getName();  
//獲取本機藍牙地址  
String address = mBluetoothAdapter.getAddress();  
Log.d(TAG, "bluetooth name =" +name+ " address =" +address);  
//獲取已配對藍牙設備  
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();  
Log.d(TAG,  "bonded device size =" +devices.size());  
for (BluetoothDevice bonddevice:devices){  
     Log.d(TAG,  "bonded device name =" +bonddevice.getName()+ " address" +bonddevice.getAddress());  
}

搜索設備

1
mBluetoothAdapter.startDiscovery();

 

中止搜索

1
mBluetoothAdapter.cancelDiscovery();

搜索藍牙設備,該過程是異步的,經過下面註冊廣播接受者,能夠監聽是否搜到設備。

1
2
3
4
5
6
7
8
IntentFilter filter =  new  IntentFilter();  
//發現設備  
filter.addAction(BluetoothDevice.ACTION_FOUND);  
//設備鏈接狀態改變  
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);  
//藍牙設備狀態改變  
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);  
registerReceiver(mBluetoothReceiver, filter);

 

監聽掃描結果

經過廣播接收者查看掃描到的藍牙設備,每掃描到一個設備,系統都會發送此廣播(BluetoothDevice.ACTION_FOUNDE)。其中參數intent能夠獲取藍牙設備BluetoothDevice

該demo中是鏈接指定名稱的藍牙設備,BLUETOOTH_NAME爲"Galaxy Nexus",若是掃描不到,記得改這個藍牙名稱。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private  BroadcastReceiver mBluetoothReceiver =  new  BroadcastReceiver(){  
         @Override  
         public  void  onReceive(Context context, Intent intent) {  
             String action = intent.getAction();  
             Log.d(TAG, "mBluetoothReceiver action =" +action);  
             if (BluetoothDevice.ACTION_FOUND.equals(action)){ //每掃描到一個設備,系統都會發送此廣播。  
                 //獲取藍牙設備  
                 BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
                 if (scanDevice ==  null  || scanDevice.getName() ==  null return ;  
                 Log.d(TAG,  "name=" +scanDevice.getName()+ "address=" +scanDevice.getAddress());  
                 //藍牙設備名稱  
                 String name = scanDevice.getName();  
                 if (name !=  null  && name.equals(BLUETOOTH_NAME)){  
                     mBluetoothAdapter.cancelDiscovery();  
                     //取消掃描  
                     mProgressDialog.setTitle(getResources().getString(R.string.progress_connecting));                    //鏈接到設備。  
                     mBlthChatUtil.connect(scanDevice);  
                 }  
             } else  if  (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){  
   
             }
         }          
};

 

 

設置藍牙可見性

有時候掃描不到某設備,這是由於該設備對外不可見或者距離遠,須要設備該藍牙可見,這樣該才能被搜索到。

可見時間默認值爲120s,最多可設置300。

 

1
2
3
4
5
6
7
8
9
10
if  (mBluetoothAdapter.isEnabled()) {  
     if  (mBluetoothAdapter.getScanMode() !=   
             BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {  
         Intent discoverableIntent =  new  Intent(  
                 BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);  
         discoverableIntent.putExtra(  
                 BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,  120 );  
         startActivity(discoverableIntent);  
     }  
}

 

2 服務端

 

 

android 藍牙之間能夠經過SDP協議創建鏈接進行通訊,通訊方式相似於日常使用socket。

首先建立BluetoothServerSocket ,BluetoothAdapter中提供了兩種建立BluetoothServerSocket 方式,以下圖所示爲建立安全的RFCOMM Bluetooth socket,該鏈接是安全的須要進行配對。而經過listenUsingInsecureRfcommWithServiceRecord建立的RFCOMM Bluetooth socket是不安全的,鏈接時不須要進行配對。

其中的uuid須要服務器端和客戶端進行統一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
private  class  AcceptThread  extends  Thread {  
         // 本地服務器套接字  
         private  final  BluetoothServerSocket mServerSocket;  
         public  AcceptThread() {           
             BluetoothServerSocket tmp =  null ;  
             // 建立一個新的偵聽服務器套接字  
             try  {  
                 tmp = mAdapter.listenUsingRfcommWithServiceRecord(  
                         SERVICE_NAME, SERVICE_UUID);  
                 //tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID);  
             catch  (IOException e) {  
                 Log.e(TAG,  "listen() failed" , e);  
             }  
             mServerSocket = tmp;  
         }  
   
         public  void  run() {  
             BluetoothSocket socket =  null ;  
             // 循環,直到鏈接成功  
             while  (mState != STATE_CONNECTED) {  
                 try  {  
                     // 這是一個阻塞調用 返回成功的鏈接  
                     // mServerSocket.close()在另外一個線程中調用,能夠停止該阻塞  
                     socket = mServerSocket.accept();  
                 catch  (IOException e) {  
                     Log.e(TAG,  "accept() failed" , e);  
                     break ;  
                 }  
                 // 若是鏈接被接受  
                 if  (socket !=  null ) {  
                     synchronized  (BluetoothChatUtil. this ) {  
                         switch  (mState) {  
                         case  STATE_LISTEN:  
                         case  STATE_CONNECTING:  
                             // 正常狀況。啓動ConnectedThread。  
                             connected(socket, socket.getRemoteDevice());  
                             break ;  
                         case  STATE_NONE:  
                         case  STATE_CONNECTED:  
                             // 沒有準備或已鏈接。新鏈接終止。  
                             try  {  
                                 socket.close();  
                             catch  (IOException e) {  
                                 Log.e(TAG,  "Could not close unwanted socket" , e);  
                             }  
                             break ;  
                         }  
                     }  
                 }  
             }  
             if  (D) Log.i(TAG,  "END mAcceptThread" );  
         }  
   
         public  void  cancel() {  
             if  (D) Log.d(TAG,  "cancel "  this );  
             try  {  
                 mServerSocket.close();  
             catch  (IOException e) {  
                 Log.e(TAG,  "close() of server failed" , e);  
             }  
         }  
}

mServerSocket經過accept()等待客戶端的鏈接(阻塞),直到鏈接成功或失敗。

 

3 客戶端

 

客戶端主要用來建立RFCOMM socket,並鏈接服務端。

先掃描周圍的藍牙設備,若是掃描到指定設備則進行鏈接。mBlthChatUtil.connect(scanDevice)鏈接到設備,

鏈接過程主要在ConnectThread線程中進行,先建立socket,方式有兩種,

以下代碼中是安全的(createRfcommSocketToServiceRecord)。另外一種不安全鏈接對應的函數是createInsecureRfcommSocketToServiceRecord

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private  class  ConnectThread  extends  Thread {  
         private  BluetoothSocket mmSocket;  
         private  final  BluetoothDevice mmDevice;  
         public  ConnectThread(BluetoothDevice device) {  
             mmDevice = device;  
             BluetoothSocket tmp =  null ;  
             // 獲得一個bluetoothsocket  
             try  {  
                 mmSocket = device.createRfcommSocketToServiceRecord  
                         (SERVICE_UUID);  
             catch  (IOException e) {  
                 Log.e(TAG,  "create() failed" , e);  
                 mmSocket =  null ;  
             }  
         }  
   
         public  void  run() {  
             Log.i(TAG,  "BEGIN mConnectThread" );  
             try  {   
                 // socket 鏈接,該調用會阻塞,直到鏈接成功或失敗  
                 mmSocket.connect();  
             catch  (IOException e) {  
                 connectionFailed();  
                 try  { //關閉這個socket  
                     mmSocket.close();  
                 catch  (IOException e2) {  
                     e2.printStackTrace();  
                 }  
                 return ;  
             }  
             // 啓動鏈接線程  
             connected(mmSocket, mmDevice);  
         }  
   
         public  void  cancel() {  
             try  {  
                 mmSocket.close();  
             catch  (IOException e) {  
                 Log.e(TAG,  "close() of connect socket failed" , e);  
             }  
         }  
}

接着客戶端socket主動鏈接服務端。鏈接過程當中會自動進行配對,須要雙方贊成才能夠鏈接成功。

 

4 數據傳輸

客戶端與服務端鏈接成功後都會調用connected(mmSocket, mmDevice),建立一個ConnectedThread線程()。

該線程主要用來接收和發送數據。客戶端和服務端處理方式同樣。該線程經過socket得到輸入輸出流。

 

private  InputStream mmInStream = socket.getInputStream();

private  OutputStream mmOutStream =socket.getOutputStream();

發送數據

1
2
3
4
5
6
7
8
9
10
public  void  write( byte [] buffer) {  
     try  {  
         mmOutStream.write(buffer);  
         // 分享發送的信息到Activity  
         mHandler.obtainMessage(MESSAGE_WRITE, - 1 , - 1 , buffer)  
                 .sendToTarget();  
     catch  (IOException e) {  
         Log.e(TAG,  "Exception during write" , e);  
     }  
}

 

接收數據

 

線程循環進行接收數據。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  void  run() {  
     // 監聽輸入流  
     while  ( true ) {  
         try  {  
             byte [] buffer =  new  byte [ 1024 ];  
             // 讀取輸入流  
             int  bytes = mmInStream.read(buffer);  
             // 發送得到的字節的ui activity  
             Message msg = mHandler.obtainMessage(MESSAGE_READ);  
             Bundle bundle =  new  Bundle();  
             bundle.putByteArray(READ_MSG, buffer);  
             msg.setData(bundle);  
             mHandler.sendMessage(msg);            
         catch  (IOException e) {  
             Log.e(TAG,  "disconnected" , e);  
                 connectionLost();  
                 break ;  
             }  
         }  
}

 

4、運行效果

一、運行,右鍵項目:Run as -》Android Application   (備註:Eclipse須要配置Android開發環境)

二、運行效果以下: 

 

客戶端 

1500550538898087682.jpeg

 

服務端

Screenshot_2017-07-20-19-26-57.png

 

出處:http://www.demodashi.com/demo/10676.html

相關文章
相關標籤/搜索