最近歼跟,公司有一個(gè)項(xiàng)目時(shí)關(guān)于手機(jī)藍(lán)牙和硬件藍(lán)牙相互通信的需求和媳。基于之前很久沒有學(xué)習(xí)硬件的知識(shí)哈街,這次記錄下來留瞳,以備下次需要時(shí)使用。
首先骚秦,需要搞清楚一些基本的概要她倘,藍(lán)牙3.0以前的是傳統(tǒng)藍(lán)牙璧微,4.0以后的是低功耗藍(lán)牙,Android藍(lán)牙在不同手機(jī)系統(tǒng)版本也有不同的藍(lán)牙硬梁,當(dāng)然也有不一樣的調(diào)用方法前硫。android4.3(API18)版本以下的對(duì)應(yīng)的是傳統(tǒng)藍(lán)牙,在掃描的時(shí)候等待時(shí)間會(huì)比較長(zhǎng)荧止。android4.3以上的是低功耗藍(lán)牙屹电,但是android4.3版本到5.0版本的調(diào)用方法和android5.0以上的調(diào)用方法是不一樣的。android用傳統(tǒng)的藍(lán)牙連接方式是無法和低功耗藍(lán)牙模塊建立通信通道的跃巡,因?yàn)橥ㄐ诺膮f(xié)議是不一樣的危号。低功耗藍(lán)牙是用GATT這種屬性傳輸協(xié)議,而傳統(tǒng)藍(lán)牙則是通過Socket的方式進(jìn)行數(shù)據(jù)的傳輸素邪。
android藍(lán)牙權(quán)限在6.0以上增加了一個(gè)模糊定位的權(quán)限外莲,不開啟部分手機(jī)無法進(jìn)行掃描藍(lán)牙發(fā)出的廣播。
藍(lán)牙權(quán)限:
為了能夠在你開發(fā)的應(yīng)用設(shè)備中使用藍(lán)牙功能兔朦,必須聲明藍(lán)牙的權(quán)限"BLUETOOTH"偷线。在進(jìn)行藍(lán)牙的通信,例如請(qǐng)求連接沽甥,接受連接以及交換數(shù)據(jù)中声邦,需要用到該權(quán)限。
<use-permission android:name="android.permission.BLUETOOTH"/>
如果你的應(yīng)用程序需要實(shí)例化藍(lán)牙設(shè)備的搜索或者對(duì)藍(lán)牙的設(shè)置進(jìn)行操作安接,那么必須聲明BLUETOOTH_ADMIN權(quán)限翔忽。大多數(shù)應(yīng)用需要該權(quán)限對(duì)本地的藍(lán)牙設(shè)備進(jìn)行搜索。該權(quán)限的其他能力并不應(yīng)當(dāng)被使用盏檐,除非你的應(yīng)用是一個(gè)電源管理的應(yīng)用歇式,需要對(duì)藍(lán)牙的設(shè)置進(jìn)行修改
<use-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
android 6.0版本之后增加模糊定位授權(quán),如果不增加這個(gè)權(quán)限部分6.0以上的手機(jī)無法掃描出藍(lán)牙胡野。
<use-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
開啟關(guān)閉藍(lán)牙:
在使用藍(lán)牙通信之前材失,需要先開啟,使用BluetoothAdapter來進(jìn)行完成
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter ==null) {// Device does not support Bluetooth}
如果BluetoothAdapter為null值硫豆,則表示此手機(jī)不支持藍(lán)牙功能龙巨。
if(!mBluetoothAdapter.isEnabled()){
Intent enableBtIntent =newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
REQUEST_ENABLE_BT大于0的自己定義的int變量。isEnable()判斷藍(lán)牙是否可用熊响。推薦使用這種方式進(jìn)行開啟藍(lán)牙旨别。
還有另一種方式開啟,但是在部分手機(jī)不會(huì)彈出藍(lán)牙的授權(quán)限頁(yè)汗茄。
mBluetoothAdapter.enable()也能直接開啟藍(lán)牙秸弛。
搜索設(shè)備:
首先找到手機(jī)找出已匹配的藍(lán)牙設(shè)備:
// 將已配對(duì)的設(shè)備添加到列表中
Set pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
mDevicesArray.add(device.getName() + "\n" + device.getAddress());
deviceList.add(device);
}
}
注冊(cè)廣播接收器并開啟掃描:
IntentFilter filter = new IntentFilter();
//注冊(cè)設(shè)備被發(fā)現(xiàn)時(shí)的廣播
filter.addAction(BluetoothDevice.ACTION_FOUND);
//注冊(cè)一個(gè)搜索結(jié)束時(shí)的廣播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
廣播接收器:
public class BluetoothReceiver extends BroadcastReceiver {
public final String TAG = getClass().getName();
private BluetoothInterface bluetoothInterface;
private List<BluetoothDevice> beans = null;
public BluetoothReceiver(BluetoothInterface bluetoothInterface) {
this.bluetoothInterface = bluetoothInterface;
beans = new ArrayList<>();
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//發(fā)現(xiàn)藍(lán)牙廣播
if (action.equals(BluetoothDevice.ACTION_FOUND)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
beans.add(device);
//顯示已配對(duì)設(shè)備
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
Log.e(TAG, "\n" + device.getName() + "==>" + device.getAddress() + "\n");
} else if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
Log.e(TAG, "\n" + device.getName() + "==>" + device.getAddress() + "\n");
}
//搜索完成回調(diào)
} else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
bluetoothInterface.discoveryCompleted(beans);
}
}
}
只要搜索沒有完成,會(huì)一直接收外部藍(lán)牙設(shè)備發(fā)送的廣播。如果找到了對(duì)應(yīng)的藍(lán)牙設(shè)備递览,可以取消搜索叼屠。
mBluetoothAdapter.cancelDiscovery();
匹配和連接藍(lán)牙
匹配藍(lán)牙
//MY_UUID是藍(lán)牙串口服務(wù)對(duì)應(yīng)的應(yīng)用UUID,網(wǎng)上有說不能寫在UI線程绞铃,但我寫在UI線程也沒問題
BluetoothSocket socket =device.createRfcommSocketToServiceRecord(MY_UUID);
連接藍(lán)牙
public class ConnectedThread extends Thread {
private final BluetoothSocket mSocket;
private final InputStream mInStream;
private final OutputStream mOutStream;
private Handler mHandler;
public final static int MESSAGE_READ = 1;
public final static int MESSAGE_EXCEPTIONS = 2;
public final static int MESSAGE_SUCCESS = 3;
public ConnectedThread(BluetoothSocket socket, Handler handler) {
mSocket = socket;
mHandler = handler;
InputStream temIn = null;
OutputStream temOut = null;
try {
temIn = socket.getInputStream();
temOut = socket.getOutputStream();
} catch (IOException e) {
}
mInStream = temIn;
mOutStream = temOut;
}
@Override
public void run() {
try {
mSocket.connect();
mHandler.sendEmptyMessage(MESSAGE_SUCCESS);
} catch (IOException e) {
e.printStackTrace();
mHandler.sendEmptyMessage(MESSAGE_EXCEPTIONS);
try {
mSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
int bytes;
//對(duì)輸入流保持監(jiān)聽直至一個(gè)異常發(fā)生
while (true) {
byte[] buffer = new byte[20];
try {
bytes = mInStream.read(buffer);
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
/**
* 向藍(lán)牙模塊發(fā)射數(shù)據(jù)
*
* @param bytes
*/
public void write(byte[] bytes) {
try {
mOutStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 關(guān)閉Socket的連接
*/
public void cancel() {
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
藍(lán)牙在發(fā)送數(shù)據(jù)時(shí)有大小限制镜雨,一般為20字節(jié),在6.0之后的版本可以進(jìn)行設(shè)置大小儿捧。
如果有不正確的地方荚坞,可以在下面評(píng)論一起學(xué)習(xí)。