android 的blt僅僅支持api 18 android4.3以上,有的功能甚至須要api 19 android4.4;
因此咱們在作blt項目以前必定要清楚可用的版本範圍。android
我要講述的是打開blt大門的操做。這些操做就是如何打開blt、如何搜索到其餘設備、如何配對選中設備、如何經過mac地址鏈接以前已經配對過的設備以及鏈接成功的兩個(或一對多個)設備如何通信。編程
在學習blt知識前要先搞清楚,blt是如何讓兩個設備之間通信的。咱們先來看一下基本步驟:api
1,打開blt。
——1)權限
——2)監聽設備是否打開blt
——3)操做,打開或者關閉blt數組
2,搜索附近設備&讓附近設備搜索到
——1)讓自身設備能夠被附近搜索到,時間最長300s;(隨着api的逐漸升級可能不止300s,作了必定的優化處理)
——2)搜索附近可鏈接的設備;過期的api在接口中回調搜索結果,新的api則要求用廣播接收搜索結果。(咱們這裏使用廣播接收結果)服務器
3,與目標設備配對
——1)對於目標設備進行配對,android系統自己會完成配對動做,而且將已經成功配對的設備的mac地址保存起來,以供下次進行自動配對使用。
——2)對進行配對過的設備自動配對。這個動做也是系統幫咱們完成的,咱們只須要根據系統給咱們的狀態來判斷這個是否已經配對就好了。app
4,與成功配對的設備進行鏈接
——1)若是要對成功配對的設備進行鏈接,則必須先創建服務器端。服務器端創建只有會線程阻塞等待客戶端的鏈接。
——2)確保創建了服務器端以後再創建客戶端。客戶端的鏈接過程也是線程阻塞的。知道鏈接成功後,服務器端收到消息,則表示配對設備已鏈接成功。socket
5,注意事項:
——1)配對成功過的設備無需再次配對,只須要從系統中拿到mac地址進行鏈接。這個根據系統返回的狀態值去判斷。
——2)搜索附近設備的操做是一個消耗內存的動做,咱們務必在鏈接設備‘以前‘中止搜索。
——3)設備的配對成功不表明鏈接成功。配對和鏈接是兩回事且配對在鏈接動做以前。
——4)當咱們的程序明確指出「不須要blt操做了」的時候,及時反註冊blt廣播,中止blt鏈接,註銷藍牙對象。而這個操做例如:程序退出、用戶無操做超時、邏輯不須要blt的鏈接了。學習
以上就是一個藍牙鏈接成功以後能夠正常傳輸信息的步驟。
接下來咱們再來看看相應的步驟在代碼邏輯中的表明。優化
1,權限。ui
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
2,得到藍牙管理對象。BluetoothManager的主要做用是從中獲得咱們須要的對象
//@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
//首先獲取BluetoothManager
BluetoothManager bluetoothManager=(BluetoothManager) context.getService(Context.BLUETOOTH_SERVICE);
3,得到藍牙適配器。藍牙適配器是咱們操做藍牙的主要對象,能夠從中得到配對過的藍牙集合,能夠得到藍牙傳輸對象等等
//獲取BluetoothAdapter
if (bluetoothManager != null)
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
3,註冊廣播來接收藍牙配對信息。在藍牙開啓前調用
// 用BroadcastReceiver來取得搜索結果
IntentFilter intent = new IntentFilter();
intent.addAction(BluetoothDevice.ACTION_FOUND);//搜索發現設備
intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//狀態改變
intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);//行動掃描模式改變了
intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//動做狀態發生了變化
context.registerReceiver(searchDevices, intent);
/**
* 藍牙接收廣播
*/
private BroadcastReceiver searchDevices = new BroadcastReceiver() {
//接收
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle b = intent.getExtras();
Object[] lstName = b.keySet().toArray();
// 顯示全部收到的消息及其細節
for (int i = 0; i < lstName.length; i++) {
String keyName = lstName[i].toString();
Log.e("bluetooth", keyName + ">>>" + String.valueOf(b.get(keyName)));
}
BluetoothDevice device;
// 搜索發現設備時,取得設備的信息;注意,這裏有可能重複搜索同一設備
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
onRegisterBltReceiver.onBluetoothDevice(device);
}
//狀態改變時
else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (device.getBondState()) {
case BluetoothDevice.BOND_BONDING://正在配對
Log.d("BlueToothTestActivity", "正在配對......");
onRegisterBltReceiver.onBltIng(device);
break;
case BluetoothDevice.BOND_BONDED://配對結束
Log.d("BlueToothTestActivity", "完成配對");
onRegisterBltReceiver.onBltEnd(device);
break;
case BluetoothDevice.BOND_NONE://取消配對/未配對
Log.d("BlueToothTestActivity", "取消配對");
onRegisterBltReceiver.onBltNone(device);
default:
break;
}
}
}
};
4,反註冊廣播和清楚藍牙鏈接。在不須要使用藍牙的時候調用
/**
* 反註冊廣播取消藍牙的配對
*
* @param context
*/
public void unregisterReceiver(Context context) {
context.unregisterReceiver(searchDevices);
if (mBluetoothAdapter != null)
mBluetoothAdapter.cancelDiscovery();
}
6,搜索藍牙。搜索後添加到自定義的集合中以供自身程序的邏輯使用。注意:搜索到的結果會在廣播中回調過來,這裏不作回調。若使用過期的接口,則在當前邏輯裏回調結果。咱們這裏使用廣播,不建議使用過期的方法。
/**
* 搜索藍牙設備
* 經過調用BluetoothAdapter的startLeScan()搜索BLE設備。
* 調用此方法時須要傳入 BluetoothAdapter.LeScanCallback參數。
* 所以你須要實現 BluetoothAdapter.LeScanCallback接口,BLE設備的搜索結果將經過這個callback返回。
* <p/>
* 因爲搜索須要儘可能減小功耗,所以在實際使用時須要注意:
* 一、當找到對應的設備後,當即中止掃描;
* 二、不要循環搜索設備,爲每次搜索設置適合的時間限制。避免設備不在可用範圍的時候持續不停掃描,消耗電量。
* <p/>
* 若是你只須要搜索指定UUID的外設,你能夠調用 startLeScan(UUID[], BluetoothAdapter.LeScanCallback)方法。
* 其中UUID數組指定你的應用程序所支持的GATT Services的UUID。
* <p/>
* 注意:搜索時,你只能搜索傳統藍牙設備或者BLE設備,二者徹底獨立,不可同時被搜索。
*/
private boolean startSearthBltDevice(Context context) {
//開始搜索設備,當搜索到一個設備的時候就應該將它添加到設備集合中,保存起來
checkBleDevice(context);
//若是當前發現了新的設備,則中止繼續掃描,當前掃描到的新設備會經過廣播推向新的邏輯
if (getmBluetoothAdapter().isDiscovering())
stopSearthBltDevice();
Log.i("bluetooth", "本機藍牙地址:" + getmBluetoothAdapter().getAddress());
//開始搜索
mBluetoothAdapter.startDiscovery();
//這裏的true並非表明搜索到了設備,而是表示搜索成功開始。
return true;
}
7,中止搜索藍牙設備
public boolean stopSearthBltDevice() {
//暫停搜索設備
if(mBluetoothAdapter!=null)
return mBluetoothAdapter.cancelDiscovery();
}
8,鏈接藍牙。注意:鏈接藍牙的前提是咱們已經創建好了藍牙服務器端。因此,這裏咱們先創建藍牙服務器
——1)先創建藍牙服務器端
/**
* 這個操做應該放在子線程中,由於存在線程阻塞的問題
*/
public void run(Handler handler) {
//服務器端的bltsocket須要傳入uuid和一個獨立存在的字符串,以便驗證,一般使用包名的形式
BluetoothServerSocket bluetoothServerSocket = tmBluetoothAdapter.listenUsingRfcommWithServiceRecord("com.bluetooth.demo", BltContant.SPP_UUID);
while (true) {
try {
//注意,當accept()返回BluetoothSocket時,socket已經鏈接了,所以不該該調用connect方法。
//這裏會線程阻塞,直到有藍牙設備連接進來纔會往下走
socket = getBluetoothServerSocket().accept();
if (socket != null) {
BltAppliaction.bluetoothSocket = socket;
//回調結果通知
Message message = new Message();
message.what = 3;
message.obj = socket.getRemoteDevice();
handler.sendMessage(message);
//若是你的藍牙設備只是一對一的鏈接,則執行如下代碼
getBluetoothServerSocket().close();
//若是你的藍牙設備是一對多的,則應該調用break;跳出循環
//break;
}
} catch (IOException e) {
try {
getBluetoothServerSocket().close();
} catch (IOException e1) {
e1.printStackTrace();
}
break;
}
}
}
——2)在藍牙服務器創建以後,再進行鏈接藍牙的操做。
/**
* 嘗試鏈接一個設備,子線程中完成,由於會線程阻塞
*
* @param btDev 藍牙設備對象
* @param handler 結果回調事件
* @return
*/
private void connect(BluetoothDevice btDev, Handler handler) {
try {
//經過和服務器協商的uuid來進行鏈接
mBluetoothSocket = btDev.createRfcommSocketToServiceRecord(BltContant.SPP_UUID);
if (mBluetoothSocket != null)
//全局只有一個bluetooth,因此咱們能夠將這個socket對象保存在appliaction中
BltAppliaction.bluetoothSocket = mBluetoothSocket;
//經過反射獲得bltSocket對象,與uuid進行鏈接獲得的結果同樣,但這裏不提倡用反射的方法
//mBluetoothSocket = (BluetoothSocket) btDev.getClass().getMethod("createRfcommSocket", new Class[]{int.class}).invoke(btDev, 1);
Log.d("blueTooth", "開始鏈接...");
//在創建以前調用
if (getmBluetoothAdapter().isDiscovering())
//中止搜索
getmBluetoothAdapter().cancelDiscovery();
//若是當前socket處於非鏈接狀態則調用鏈接
if (!getmBluetoothSocket().isConnected()) {
//你應當確保在調用connect()時設備沒有執行搜索設備的操做。
// 若是搜索設備也在同時進行,那麼將會顯著地下降鏈接速率,並很大程度上會鏈接失敗。
getmBluetoothSocket().connect();
}
Log.d("blueTooth", "已經連接");
if (handler == null) return;
//結果回調
Message message = new Message();
message.what = 4;
message.obj = btDev;
handler.sendMessage(message);
} catch (Exception e) {
Log.e("blueTooth", "...連接失敗");
try {
getmBluetoothSocket().close();
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
9,自動鏈接以往配對成功的設備。注意:鏈接的前提是服務器端已開啓,若沒開啓則進行8.1的操做
/**
* 嘗試配對和鏈接
*
* @param btDev
*/
public void createBond(BluetoothDevice btDev, Handler handler) {
if (btDev.getBondState() == BluetoothDevice.BOND_NONE) {
//若是這個設備取消了配對,則嘗試配對
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
btDev.createBond();
}
} else if (btDev.getBondState() == BluetoothDevice.BOND_BONDED) {
//若是這個設備已經配對完成,則嘗試鏈接
connect(btDev, handler);
}
}
/**
* 得到系統保存的配對成功過的設備,並嘗試鏈接
*/
public void getBltList() {
if (getmBluetoothAdapter() == null) return;
//得到已配對的遠程藍牙設備的集合
Set<BluetoothDevice> devices = getmBluetoothAdapter().getBondedDevices();
if (devices.size() > 0) {
for (Iterator<BluetoothDevice> it = devices.iterator(); it.hasNext(); ) {
BluetoothDevice device = it.next();
//自動鏈接已有藍牙設備
createBond(device, null);
}
}
}
注意:外界只須要調用getBltList();方法便可進行自動鏈接。
10,輸入mac地址自動鏈接設備。前提是系統原來鏈接過該地址。
/**
* 輸入mac地址進行自動配對
* 前提是系統保存了該地址的對象
*
* @param address
*/
public void autoConnect(String address, Handler handler) {
if (getmBluetoothAdapter().isDiscovering()) getmBluetoothAdapter().cancelDiscovery();
BluetoothDevice btDev = getmBluetoothAdapter().getRemoteDevice(address);
connect(btDev, handler);
}
11,藍牙鏈接狀態。用於咱們判斷該藍牙設備是出於:未配對仍是配對未鏈接仍是已鏈接狀態
public String bltStatus(int status) {
String a = "未知狀態";
switch (status) {
case BluetoothDevice.BOND_BONDING:
a = "鏈接中";
break;
case BluetoothDevice.BOND_BONDED:
a = "鏈接完成";
break;
case BluetoothDevice.BOND_NONE:
a = "未鏈接/取消鏈接";
break;
}
return a;
}
11,藍牙點擊事件。包括藍牙的打開,關閉,被搜索,斷開鏈接
/**
* 藍牙操做事件
*
* @param context
* @param status
*/
public void clickBlt(Context context, int status) {
switch (status) {
case BltContant.BLUE_TOOTH_SEARTH://搜索藍牙設備,在BroadcastReceiver顯示結果
startSearthBltDevice(context);
break;
case BltContant.BLUE_TOOTH_OPEN://本機藍牙啓用
if (getmBluetoothAdapter() != null)
getmBluetoothAdapter().enable();//啓用
break;
case BltContant.BLUE_TOOTH_CLOSE://本機藍牙禁用
if (getmBluetoothAdapter() != null)
getmBluetoothAdapter().disable();//禁用
break;
case BltContant.BLUE_TOOTH_MY_SEARTH://本機藍牙能夠在300s內被搜索到
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
context.startActivity(discoverableIntent);
break;
case BltContant.BLUE_TOOTH_CLEAR://本機藍牙關閉當前鏈接
try {
if (getmBluetoothSocket() != null)
getmBluetoothSocket().close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
到此,藍牙從打開到鏈接的方法都寫完了。最後咱們來總結一下。
當咱們得到到了bluetoothsocket對象的時候,咱們就能夠像使用socket編程同樣,讓兩個藍牙之間傳輸數據。甚至能夠在程序內部監聽藍牙耳機的暫停/播放/音量鍵等的點擊事件。
具體的藍牙操做,我將放在demo裏供你們學習。
demo下載