android藍牙socket
『壹』 Android藍牙開發(二)經典藍牙消息傳輸實現
上篇文章中,我們主要介紹了藍牙模塊,傳統/經典藍牙模塊BT和低功耗藍牙BLE及其相關的API,不熟悉的可以查看 Android藍牙開發(一)藍牙模塊及核心API 進行了解。
本篇主要記錄用到的經典藍牙開發流程及連接通訊。
藍牙連接前,給與相關系統許可權:
安卓6.0以上系統要動態請求及獲取開啟GPS內容:
藍牙核心對象獲取,若獲取對象為null則說明設備不支持藍牙:
判斷藍牙是否開啟,沒有則開啟:
藍牙掃描:
取消掃描:
藍牙監聽廣播,監聽藍牙開關,發現設備,掃描結束等狀態,定義狀態回調介面,進行對應操作,例如:監聽到藍牙開啟後,進行設備掃描;發現設備後進行連接等。
客戶端,與服務端建立長連接,進行通訊:
服務端監聽客戶端發起的連接,進行接收及通訊:
客戶端連接及服務端監聽基類,用於客戶端和服務端之前Socket消息通訊,進行消息或文件的發送、接收,進行通訊關閉操作等:
我這里只是簡單記錄了項目中用到的藍牙通訊,兩個設備之間不通過配對進行連接、通訊。
相關詳細內容及使用請查看Github項目: https://github.com/MickJson/BluetoothCS
藍牙配對操作及其它內容,可以詳細查看我下面的參考資料,寫的十分詳細,比如設備通過MAC地址,可以通過BluetoothAdapter獲取設備,再通過客戶端connect方法去進行連接等。
連接中遇到問題:read failed, socket might closed or timeout, read ret: -1。
通過改UUID,反射等方法都還是會出現錯誤。連接時,要確保服務端及客戶端都處於完全斷開狀態,否則連接就會出現以上問題,但偶爾還是會有問題,期待有什麼好的方法可留言告訴我。
參考資料:
Android-經典藍牙(BT)-建立長連接傳輸短消息和文件
Android藍牙開發—經典藍牙詳細開發流程
歡迎點贊/評論,你們的贊同和鼓勵是我寫作的最大動力!
『貳』 android開發SPP經典藍牙
Android 開發SPP經典藍牙。
1、傳統藍牙採用的是SPP(Serial Port Profile)協議進行數據傳輸。
2、SPP的UUID:00001101-0000-1000-8000-00805F9B34FB
3、手機一般以客戶端的角色主動連接SPP協議設備
概念:
BluetoothAdapter:
本地藍牙適配器,是所有藍牙交互的入口,表示藍牙設備自身的一個藍牙適配器,整個系統只有一個藍牙適配器,通過他可以發現其他藍牙設備,查詢綁定(配對)設備列表,使用MAC地址實例化BluetoothDevice以及創建BluetoothServerSocket用來偵聽來自其他設備的通信。
myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//獲取默認的藍牙Adapter
BluetoothDevice:
遠程的藍牙設備。
private static BluetoothDevice myDevice;
myDevice = myBluetoothAdapter.getRemoteDevice(BDAddr);//獲取遠程設備,通過藍牙的MAC地址來獲取一個遠程對象
兩種連接方式
BluetoothSocket
客戶端:調用BluetoothDevice的()可以獲取該對象;調用connect()方法可以建立連接。
private static BluetoothSocket mySocket = null;
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
Method m = myDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});//由BluetoothDevice衍生出BluetoothSocket, createRfcommSocket來選擇連接的服務和協議
mySocket = (BluetoothSocket) m.invoke(myDevice, 1);
BluetoothServerSocket:
服務端:通過BluetoothServerSocket對象可以創建BluetoothSocket對象,調用BluetoothServerSocket的accept()的方法可以得到改對象。
開發流程:
1:聲明許可權:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
2:啟動和關閉藍牙
獲取藍牙適配器,使用close()介面可以關閉藍牙適配器
myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//獲取默認的藍牙Adapter
啟動藍牙
if (!blueadapter.isEnabled())
//判斷本機藍牙是否打開
{//如果沒打開,則打開藍牙
blueadapter.enable();
}
3.使用BlueAdatper搜索
使用bluetoothAdapter搜索設備,bluetoothAdapter.startDiscovery()在搜索過程中,系統會發出三個廣播信息:
ACTION_DISCOVERY_START:開始搜索
ACTION_DISCOVERY_FINISHED:搜索結束
ACTION_FOUND:找到設備
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();//如果藍牙設備未連接則取消搜索
}
bluetoothAdapter.startDiscovery();
}
4:(1)通過注冊廣播獲取搜索到的設備。
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//找到設備廣播
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索完成廣播
registerReceiver(receiver, intentFilter);//注冊廣播接收器
// receiver
private final BroadcastReceiver receiver = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// find a device
BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
//未配對設備
newDeviceArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}else {
//已經配對過的設備
TextView tvPaired = (TextView)findViewById(R.id.tv_paired);
tvPaired.setVisibility(View.VISIBLE);
lvPairedDevices.setVisibility(View.VISIBLE);
pairedDeviceArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
Log.i(TAG,"name:" + device.getName() + " address"+ device.getAddress());
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action){
// search finish
Log.i(TAG, "search finish!");
}
}
};
(2),直接得到當前的藍牙設備後,就可用通過遍歷pairedDevices ,得到當前手機已經配對過的藍牙設備。
Set<BluetoothDevice> pairedDevices = myBluetoothAdapter.getBondedDevices();//獲取當前藍牙設備
if (pairedDevices.size() <= 0) return false;
for (BluetoothDevice device : pairedDevices) {
Map<String, String> map = new HashMap<String, String>();
map.put("DeviceName", device.getName());
map.put("BDAddress", device.getAddress());
list.add(map);
5.建立連接
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
Method m = myDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});//由BluetoothDevice衍生出BluetoothSocket, createRfcommSocket來選擇連接的服務和協議
mySocket = (BluetoothSocket) m.invoke(myDevice, 1);
mySocket.connect();//使用BluetoothSocket來連接設備
6.把得到的藍牙設備給通過點擊ListView選擇設備。
listView.setOnItemClickListener(new ListView.OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
SelectedBDAddress = list.get(arg2).get("BDAddress");
if (((ListView) arg0).getTag() != null) {
((View) ((ListView) arg0).getTag()).setBackgroundDrawable(null);
}
((ListView) arg0).setTag(arg1);
arg1.setBackgroundColor(Color.BLUE);
myDevice = myBluetoothAdapter.getRemoteDevice(SelectedBDAddress);
}
});
7.客戶端發送數據
當兩個設備成功連接之後,雙方都會有一個BluetoothSocket對象,這時,就可以在設備之間傳送數據了。
1.使用getOutputStream()方法來獲取輸出流來處理傳輸。
2.調用write()。
os = socket.getOutputStream();//獲取輸出流
if (os != null) {//判斷輸出流是否為空
os.write(message.getBytes("UTF-8"));
}
os.flush();//將輸出流的數據強制提交
os.close();//關閉輸出流
}
將輸出流中的數據提交後,要記得關閉輸出流,否則,可能會造成只能發送一次數據。
8.服務端接收數據
1.使用getInputStream()方法來獲取輸入流來處理傳輸。
2.調用read()。
InputStream im=null;
im=bluetoothSocket.getInputStream();
byte buf[] = new byte[1024];
if (is != null) {
is.read(buf, 0, buf.length);//讀取發來的數據
String message = new String(buf);//把發來的數據轉化為String類型
BuletoothMainActivity.UpdateRevMsg(message);//更新信息在顯示文本框
is.close();//關閉輸入流
使用服務端接收數據時,要先從客戶端向服務端發起連接,只有接收到連接請求之後,才會返回一個BluetoothSocket對象。有BluetoothSocket對象才能獲取到輸入流。
『叄』 Android - Socket簡單使用
ServerSocket類提供如下構造器:
當ServerSocket使用完畢,應使用 close() 方法來關閉此ServerSocket。通常情況下,伺服器不應該只接收一個客戶端請求,而應該不斷接收來自客戶端的請求,所以程序可以通過循環,不斷調用ServerSocket的accept方法:
Socket 常用構造器
註:上面兩個構造器指定遠程主機時既可以使用InetAddress來指定,也可以直接使用String對象來指定遠程IP。本地主機只有一個IP地址時,使用第一個方法更簡單。
在與伺服器進行通訊的時候,無法判斷遠程的伺服器是否斷開連接。如果使用 OutputStream 發送數據則會影響正常的數據發送(無法區分)。所以就引入了一個心跳機制。
心跳機制實現,使用 Socket.sendUrgentData() 方法發送一個位元組流數據(緊急數據)。可以通過判斷服務端的 OOBINLINE 屬性是否打開,來確定是否斷開連接;
setSoTimeout()理解 :設置超時時間;例如:設置為2s,如果阻塞的時間>2s ,那麼就會報錯。
『肆』 Android-藍牙傳輸
通過藍牙傳輸數據與Socket類似。在網路中使用Socket和ServerSocket控制客戶端和服務端的數據讀寫。而藍牙通訊也由客戶端和服務端Socket來完成。藍牙客戶端Socket是BluetoothSocket,藍牙服務端Socket是BluetoothServerSocket。這兩個類都在android.bluetooth包中。
如果打算建議兩個藍牙設備之間的連接,則必須實現伺服器端與客戶端的機制。當兩個設備在同一個RFCOMM channel下分別擁有一個連接的BluetoothSocket,這兩個設備才可以說是建立了連接。
伺服器設備與客戶端設備獲取BluetoothSocket的途徑是不同的。伺服器設備是通過accepted一個incoming connection來獲取的,而客戶端設備則是通過打開一個到伺服器的RFCOMMchannel來獲取的。
通過調用BluetoothAdapter的(String, UUID) 方法來獲取
BluetoothServerSocket(UUID用於客戶端與伺服器端之間的配對)調用BluetoothServerSocket的 accept() 方法監聽連接請求,如果收到請求,則返回一個BluetoothSocket實例。
如果不想在accept其他的連接,則調用BluetoothServerSocket的 close() 方法釋放資源(調用該方法後,之前獲得的BluetoothSocket實例並沒有close。但由於RFCOMM一個時刻只允許在一條channel中有一個連接,則一般在accept一個連接後,便close掉BluetoothServerSocket)
通過搜索得到伺服器端的BluetoothService,調用BluetoothService的(String, UUID)方法獲取BluetoothSocket(該UUID應該同於伺服器端的UUID)。
調用BluetoothSocket的 connect() 方法(該方法為block方法),如果UUID同伺服器端的UUID匹配,並且連接被伺服器端accept,則 connect() 方法返回。
『伍』 android藍牙通訊Socket.connect()方法調用不成功。為什麼
UUID值出現錯誤。
看一下android有關bluetooth的API,用於普通藍牙適配器和android手機藍牙模塊連接的,而且這個UUID的值必須是00001101-0000-1000-8000-00805F9B34FB。
這個是android的API上面說明的.connect().在連接的時候,android手機作client(主動和電腦建立連接),如果電腦作為server(一直監聽是否有服務連接),則需要在手機端調用這樣一行代碼.兩邊的UUID必須是一樣的,這是一個服務的唯一標識。