Android 藍牙BLE開發從官方源碼demo開始(二)

一、前言

在上一篇文章Android 藍牙BLE開發從官方源碼demo開始(一)我們已經看了官方的demo,知道了怎么開始配置Android藍牙4.0,并且也成功地進行掃描并獲取回調的藍牙設備參數,然后對參數進行處理展示,其中第一個參數device,表示一個遠程藍牙設備,里面有它獨有的藍牙地址Address和Name;我們要拿到這個設備Address進行藍牙連接和讀寫操作。

谷歌給我們提供了官方源碼demo:
https://github.com/googlesamples/android-BluetoothLeGatt
接下來我們繼續來學習谷歌官方給我們提供的藍牙BLE源碼

二、創建BluetoothLeService服務類并初始化藍牙連接

在官方demo中,藍牙ble的連接和讀寫操作都是在DeviceControlActivity中實現,可以下載demo源碼,編譯運行一遍!
來到此Activity,我們先看onCreate()方法可知,程序先執行bindService開啟了一個服務

 Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
 bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

并在服務回調已經成功連接時,獲取了BlueToohtLeService的實例,接著就執行藍牙連接操作:

    // Code to manage Service lifecycle.
    private final ServiceConnection mServiceConnection = new ServiceConnection() {
 
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
            if (!mBluetoothLeService.initialize()) {
                Log.e(TAG, "Unable to initialize Bluetooth");
                finish();
            }
            // Automatically connects to the device upon successful start-up initialization.
            mBluetoothLeService.connect(mDeviceAddress);
        }
 
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBluetoothLeService = null;
        }
    };

這個BlueToohtLeService類既然是服務類,那它父類肯定是繼承于Service;接著實現了Service的先進入onBind()方法;
1.onBind()是使用bindService開啟的服務才會有回調的一個方法。
這里官方demo在onBind()方法給我們的Activity返回了BluetoothLeService實例,方便Activity后續的連接和讀寫操作;

    private final IBinder mBinder = new LocalBinder();
 
    public class LocalBinder extends Binder {
        BluetoothLeService getService() {
            return BluetoothLeService.this;
        }
    }
 
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

2.當服務調用unbindService時,服務的生命周期將會進入onUnbind()方法;接著執行了關閉藍牙的方法;

    @Override
    public boolean onUnbind(Intent intent) {
        // After using a given device, you should make sure that BluetoothGatt.close() is called
        // such that resources are cleaned up properly.  In this particular example, close() is
        // invoked when the UI is disconnected from the Service.
        close();
        return super.onUnbind(intent);
    }

3.initialize() 初始化藍牙適配器;接著在這demo里這個方法是在服務建立后在Activity通過拿到BlueToohtLeService實例調用的。

    /**
     * Initializes a reference to the local Bluetooth adapter.
     *
     * @return Return true if the initialization is successful.
     */
    public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }
 
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
            return false;
        }
 
        return true;
    }

4.connect()方法 傳入藍牙地址進行連接藍牙操作;先判斷藍牙適配器是否為空,然后判斷是否剛斷開需要重連的設備,否則就通過藍牙適配器獲取BluetoothGatt實例去連接藍牙操作,后續還會使用到BluetoothGatt去讀寫操作和斷開、關閉操作;

    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }
 
        // Previously connected device.  Try to reconnect.
        // 以前連接的設備。 嘗試重新連接。
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
            if (mBluetoothGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                return true;
            } else {
                return false;
            }
        }
 
        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the autoConnect 我們想直接連接到設備,因此我們設置autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

5.BluetoothGattCallback 回調;這個回調可以說很重要,核心部分,主要對BluetoothGatt的藍牙連接、斷開、讀、寫、特征值變化等的回調監聽,然后我們可以將這些回調信息通過廣播機制傳播回給廣播監聽器。

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
 
        }
 
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
 
        }
 
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
        }
 
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
 
        }
    };

三、廣播監聽器

在這個官方demo中,就是使用了廣播來作為activity和service之間的數據傳遞;繼續回到源碼:activity開啟前面所說的服務之后,就注冊了這個mGattUpdateReceiver廣播;

    registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
        if (mBluetoothLeService != null) {
            final boolean result = mBluetoothLeService.connect(mDeviceAddress);
            Log.d(TAG, "Connect request result=" + result) ;
        }
   private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
        intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
        return intentFilter;
    }

關于這個廣播的回調監聽如下:有注釋就不多解釋了,它的作用就是接收從service發送回來的信息;上文有說到BluetoothGattCallback,就是從這里發送廣播的。

   // Handles various events fired by the Service. 處理服務部門發起的各種事件
    // ACTION_GATT_CONNECTED: connected to a GATT server. 連接到GATT服務器。
    // ACTION_GATT_DISCONNECTED: disconnected from a GATT server. 與GATT服務器斷開連接。
    // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.發現GATT服務
    // ACTION_DATA_AVAILABLE: received data from the device.  This can be a result of read
    //                        or notification operations. 從設備接收數據。 這可能是閱讀的結果
    //     //或通知操作。
    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                mConnected = true;
                updateConnectionState(R.string.connected);
                invalidateOptionsMenu();
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                mConnected = false;
                updateConnectionState(R.string.disconnected);
                invalidateOptionsMenu();
                clearUI();
            } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                // Show all the supported services and characteristics on the user interface.
                displayGattServices(mBluetoothLeService.getSupportedGattServices());
            } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
                displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
            }
        }
    };

這里留意一下:當連接成功后,首先service那邊會發現服務特征值,通過廣播傳輸回來,然后執行下面的方法:

   displayGattServices(mBluetoothLeService.getSupportedGattServices());

四、其他方法

  1. setCharacteristicNotification();調用此方法開啟特征值的通知;
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

2.開啟讀,我們可以用如下方法,但是此方法有個缺點:要不斷輪詢 才能達到不斷監聽;

mBluetoothGatt.readCharacteristic(characteristic);

如果成功,將返回BluetoothGattCallback回調 進入其如下方法

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

使用如下方法,傳入特征值,true

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
如果成功,將返回BluetoothGattCallback回調 進入其如下方法

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

具體選擇哪種方法就要看具體需求了。

3.還有一個很重要的方法,demo沒有給出例子的:
mBluetoothGatt.writeCharacteristic(characteristic);
這是向藍牙設備寫入數據,幾乎都會用到的。
如果成功,將返回BluetoothGattCallback回調 進入其如下方法

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt,
                                      BluetoothGattCharacteristic characteristic,
                                      int status) {
 
    }
  1. disconnect();調用BluetoothGatt.disconnect()斷開藍牙連接;
    public void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.disconnect();
    }

5.close();關閉藍牙

    /**
     * After using a given BLE device, the app must call this method to ensure resources are
     * released properly.
     */
    public void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

最后,關于Android藍牙BLE的使用在此結束,我從下載官方demo到一步一步地去理解具體的調用,最后已經算是走通了整個藍牙開發流程,如果想再深入點的話就要考慮具體的底層實現,和真實項目中怎么去更好地封裝!

掃一掃關注我的微信公眾號:程序猿在廣東

my二維碼.jpg
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容