Android與藍牙Ble之間的通訊

首先,先簡單介紹一下ble的特徵(注意:藍牙4.0只有android4.3或4.3以上才支持)html

1.BLE(Bluetooth Low Energy),藍牙4.0核心profile,主要特色是快速搜索,快速鏈接,超低功耗保持鏈接和數據傳輸,缺點:數據傳輸速率低,因爲其具備低功耗特色,因此常常用在可穿戴設備之中。android

2.關於BLE數據傳輸:api

a.profile能夠理解爲一種規範,一個標準的通訊協議,其存在於手機中,藍牙組織規定了一些標準的profile:HID OVER GATT ,防丟器等,每一個profile中包含了多個service。app

b.service 能夠理解爲一個服務,在BLE從機中有多個服務,電量信息,系統服務信息等,每個service中包含了多個characteristic特徵值,每個具體的characteristic特徵值纔是BLE通訊的主題。ide

c.characteristic特徵值:BLE主機從機通訊均是經過characteristic進行,能夠將其理解爲一個標籤,經過該標籤能夠讀取或寫入相關信息。函數

d. UUID(統一標識碼):service和characteristic均須要這個惟一的UUID進行標識。工具

e. 角色和職責,看到這裏我以爲您老人家應該知道了,就是中心設備和外圍設備(GATT server vs. GATT client.),不過呢在這裏我就不這樣說了,其實也不太懂那麼的官方說法,我就簡單粗暴點就是運用了ble的app和ble設備之間的通訊,當app搜索到了ble設備,app會收到ble反饋過來的信息好比電量什麼的,對於怎麼得來的不用說都知道確定是在一個規範好的一個對象中啦(先不須要知道這個對象是什麼鬼東西由於我也不知道,官方叫characteristic也就是上面說的特徵值),相反咱們app也能夠經過寫入一些信息到這個對象(characteristic)發送給設備,設備收到以後就會執行咱們的要它作的動做了。其實也就和日常咱們的對象賦值同樣(setValue("fuzhi")),只是方法不一樣而已,一個是set一個是write。ui

最後來舉個例子簡答說明一下:其實說白了characteristic特徵值就比如一支球隊的各個球員,service比如這個球隊的名稱,profile就當國家吧,假如咱們要找到某一個球員,世界這麼多國家第一須要知道是哪一個國家的,第二須要知道是哪一個球隊的,最後才知道是那個球員,只有這樣最後才能瞭解這個球員的一些信息(相似從ble設備獲取信息),相反也只有這樣找到球員後告訴這個球員應該怎麼打球的(相似對ble設備設置信息)。例子很難懂吧,若是你看懂了說明你和我同樣小學畢業,看不懂的話就直接看代碼吧,由於本人小學畢業,識字很少,腦殼不能轉太快,怕翻車,,,this

 

/****************************************************代碼塊*****************************************************.net

 

一、 權限和相關屬性這是最基本的啦

<uses-featureandroid:name="android.hardware.bluetooth_le"android:required="true"/>

<uses-permissionandroid:name="android.permission.BLUETOOTH"/>

<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>

 

二、初始化藍牙,這個方法通常能夠寫在項目的啓動activity,否則怕在寫完ble代碼執行後發現沒有打開藍牙還須要手動去打開對項目體驗感很差,固然只要您老人家開心,寫不寫均可以。

private void initBluetooth() {
             BluetoothManager mBluetoothManager = (BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE);
             if (mBluetoothManager != null) {
                       BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
                           if (mBluetoothAdapter != null) {
                                     if (!mBluetoothAdapter.isEnabled()) {
                                          mBluetoothAdapter.enable();  //打開藍牙
                             }
                     }
               }
   }

 

三、獲取本地ble對象(BluetoothAdapter),它對應本地Android設備的藍牙模塊,可能這麼稱呼爲本地ble對象不太準確,但我老人家開心這麼稱呼。今後段代碼開始能夠把這些有關ble通訊的代碼寫到一個class中當作一個ble工具class,以便代碼清晰查看和方便調用。這裏咱們就當這個工具類叫BleManager

private BluetoothAdapter mBluetoothAdapter; 

private BluetoothDevice mBluetoothDevice; 

private BluetoothGatt mBluetoothGatt;

private boolean isScanning = false;

//以上所定義的對象在下面的方法中都有用到,(建議在看藍牙這方面的東西時,無論看誰的文章,都先把以上或者還有些藍牙基本用的對象先熟悉是什麼意思和基本做用)。

 

private BleManager(Context context) {
           this.mContext = context;
           BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);  //BluetoothManager只在android4.3以上有
           if (bluetoothManager == null) {
                       TLog.e(TAG, "Unable to initialize BluetoothManager.");
                        return;
           }

          mBluetoothAdapter = bluetoothManager.getAdapter();
}

 

四、既然得到了BluetoothAdapter對象,那麼接下來就能夠搜索ble設備了,這時就須要用到BluetoothAdapter的startLeScan()這個方法了

public void startLeScan() {
if (mBluetoothAdapter == null) {
          return;
   }

  if (isScanning) {
    return;
  }
    isScanning = true;

   mBluetoothAdapter.startLeScan(mLeScanCallback);   //此mLeScanCallback爲回調函數

   mHandler.sendEmptyMessageDelayed(STOP_LESCAN, 10000);  //這個搜索10秒,若是搜索不到則中止搜索
}

在4.3以前的api是經過註冊廣播來處理搜索時發生的一些事件,而支持ble的新的api中,是經過回調的方式來處理的,而mLeScanCallback就是一個接口對象

private LeScanCallback mLeScanCallback = new LeScanCallback() {
    @Override
          public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
                   TLog.i(TAG, "onLeScan() DeviceName------>"+device.getName());  //在這裏可經過device這個對象來獲取到搜索到的ble設備名稱和一些相關信息
                   if(device.getName() == null){  
                           return;
                   }
                  if (device.getName().contains("Ble_Name")) {    //判斷是否搜索到你須要的ble設備
                             TLog.i(TAG, "onLeScan() DeviceAddress------>"+device.getAddress());
                             mBluetoothDevice = device;   //獲取到周邊設備
                             stopLeScan();   //一、當找到對應的設備後,當即中止掃描;二、不要循環搜索設備,爲每次搜索設置適合的時間限制。避免設備不在可用範圍的時候持續不停掃描,消耗電量。

                             connect();  //鏈接
                 } 
      }
};

//

private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case STOP_LESCAN:
T.showLong(mContext, mContext.getResources().getString(R.string.msg_connect_failed));
mBluetoothAdapter.stopLeScan(mLeScanCallback);
broadcastUpdate(Config.ACTION_GATT_DISCONNECTED);
isScanning = false;
TLog.i(TAG, "Scan time is up");
break;
}
};
};

 

五、搜索到固然就是鏈接了,就是上面那個connect()方法了

public boolean connect() {
       if (mBluetoothDevice == null) {
       TLog.i(TAG, "BluetoothDevice is null.");
       return false;
}

//兩個設備經過BLE通訊,首先須要創建GATT鏈接。這裏咱們講的是Android設備做爲client端,鏈接GATT Server

mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false, mGattCallback);  //mGattCallback爲回調接口

if (mBluetoothGatt != null) {

if (mBluetoothGatt.connect()) {
TLog.d(TAG, "Connect succeed.");
return true;
} else {
TLog.d(TAG, "Connect fail.");
return false;
}
} else {
TLog.d(TAG, "BluetoothGatt null.");
return false;
}
}

 

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                    if (newState == BluetoothProfile.STATE_CONNECTED) {
                                  gatt.discoverServices(); //執行到這裏其實藍牙已經鏈接成功了

                                  TLog.i(TAG, "Connected to GATT server.");
                           } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                                      if(mBluetoothDevice != null){
                                              TLog.i(TAG, "從新鏈接");
                                                    connect();
                                               }else{
                                                   TLog.i(TAG, "Disconnected from GATT server.");
                          }
               }
}

 

public void onServicesDiscovered(BluetoothGatt gatt, int status) {
           if (status == BluetoothGatt.GATT_SUCCESS) {
                TLog.i(TAG, "onServicesDiscovered");
                getBatteryLevel();  //獲取電量
      } else {
              TLog.i(TAG, "onServicesDiscovered status------>" + status);
     }
}

@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
       TLog.d(TAG, "onCharacteristicRead------>" + Utils.bytesToHexString(characteristic.getValue()));

//判斷UUID是否相等
       if (Values.UUID_KEY_BATTERY_LEVEL_CHARACTERISTICS.equals(characteristic.getUuid().toString())) { 
}
}

@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
      TLog.d(TAG, "onCharacteristicChanged------>" + Utils.bytesToHexString(characteristic.getValue()));

//判斷UUID是否相等
      if (Values.UUID_KEY_BATTERY_LEVEL_CHARACTERISTICS.equals(characteristic.getUuid().toString())) {
}
}

//接受Characteristic被寫的通知,收到藍牙模塊的數據後會觸發onCharacteristicWrite
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
       TLog.d(TAG,"status = " + status);
       TLog.d(TAG, "onCharacteristicWrite------>" + Utils.bytesToHexString(characteristic.getValue()));
}
};

 

public void getBatteryLevel() {
BluetoothGattCharacteristic batteryLevelGattC = getCharcteristic(
Values.UUID_KEY_BATTERY_LEVEL_SERVICE, Values.UUID_KEY_BATTERY_LEVEL_CHARACTERISTICS);
if (batteryLevelGattC != null) {
readCharacteristic(batteryLevelGattC);
setCharacteristicNotification(batteryLevelGattC, true); //設置當指定characteristic值變化時,發出通知。
}
}

 

六、獲取服務與特徵

//a.獲取服務

public BluetoothGattService getService(UUID uuid) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
TLog.e(TAG, "BluetoothAdapter not initialized");
return null;
}
return mBluetoothGatt.getService(uuid);
}

//b.獲取特徵

private BluetoothGattCharacteristic getCharcteristic(String serviceUUID, String characteristicUUID) {

//獲得服務對象
BluetoothGattService service = getService(UUID.fromString(serviceUUID));  //調用上面獲取服務的方法

if (service == null) {
TLog.e(TAG, "Can not find 'BluetoothGattService'");
return null;
}

//獲得此服務結點下Characteristic對象
final BluetoothGattCharacteristic gattCharacteristic = service.getCharacteristic(UUID.fromString(characteristicUUID));
if (gattCharacteristic != null) {
return gattCharacteristic;
} else {
TLog.e(TAG, "Can not find 'BluetoothGattCharacteristic'");
return null;
}
}

 

//獲取數據

public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
TLog.e(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}

 

public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
TLog.e(TAG, "BluetoothAdapter not initialized");
return false;
}
return mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}

 

七、寫入數據,在上面的方法中咱們已經獲得了設備服務和服務裏的特徵characteristic,那麼就能夠對這個特徵寫入或者說是賦值

public void write(byte[] data) {   //通常都是傳byte
          //獲得可寫入的characteristic Utils.isAIRPLANE(mContext) && 
            if(!mBleManager.isEnabled()){
                TLog.e(TAG, "writeCharacteristic 開啓飛行模式");
                //closeBluetoothGatt();
                isGattConnected = false;
                broadcastUpdate(Config.ACTION_GATT_DISCONNECTED);
                return;
           }
         BluetoothGattCharacteristic writeCharacteristic = getCharcteristic(Values.UUID_KEY_SERVICE, Values.UUID_KEY_WRITE);  //這個UUID都是根據協議號的UUID
         if (writeCharacteristic == null) {
         TLog.e(TAG, "Write failed. GattCharacteristic is null.");
         return;
     }
     writeCharacteristic.setValue(data); //爲characteristic賦值
     writeCharacteristicWrite(writeCharacteristic);

}

 

public void writeCharacteristicWrite(BluetoothGattCharacteristic characteristic) {
            if (mBluetoothAdapter == null || mBluetoothGatt == null) {
                TLog.e(TAG, "BluetoothAdapter not initialized");
                return;
             }
              TLog.e(TAG, "BluetoothAdapter 寫入數據");
              boolean isBoolean = false;
              isBoolean = mBluetoothGatt.writeCharacteristic(characteristic);
             TLog.e(TAG, "BluetoothAdapter_writeCharacteristic = " +isBoolean);  //若是isBoolean返回的是true則寫入成功
}

 

 

八、寫到這其實基本差很少了,最後再就是在activity中調用了,其實在調用的時候直接在須要搜索藍牙的activity調用startLeScan()這個方法就能夠了,至於搜索到的結果也顯示在這個方法裏面了,找到了對應的設備名稱而後再鏈接,對於中間一系列的回調函數,不論是成功仍是失敗都是根據您老人家的實際項目需求去寫的,你能夠用一個廣播把回調信息發送出來到activity,告訴activity應該作什麼........

舉個簡單的例子:

             在MainActivity調用搜索藍牙的方法

            public class MainActivity extends Activity{ 

            private BleManager manager;            

                  @Override
             protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);

             manager = new BleManager(this);  //這個this是一個上下文,只要在上面的BleManager工具類定義一個有參數就好

             manager.startLeScan();  //調用此方法,若是搜索到有設備則天然的去鏈接它了,到時候鏈接時回回調mGattCallback 這個回調函數,若是成功你能夠發送一個廣播出來提醒用戶藍牙與設備鏈接成功

         }

         搜索成功後,你就能夠根據你的須要寫入信息傳送了。簡單說說,就是你在一個button事件中調用上面的write(byte[] byte)這個方法,對於裏面的參數byte就是根據大家所設置的byte了,最後寫入的成功與否就看writeCharacteristicWrite這個方法下面的返回值是true仍是false了,true表示成成功。

 

九、總結了,菜鳥我其實也剛接觸藍牙不久,也是看了一些文章,而後結合本身的一些總結,寫出了這麼一篇小學生文章(也是想爲本身增長一些印象),請各位老人家們多多理解,固然要是有哪位大神看不過去或者以爲那裏不對了請指教出來,不要讓小菜鳥一直錯下去,謝謝!

相關文章
相關標籤/搜索