Android BLE最完整的工具類(掃描/連接/讀寫/通知設備)

這裏只要是Android設備與BLE設備的通訊都可以共用,只需要該的就是uuid的值,還有就是ble設備提供商要出文檔協議,看看是如何發命令跟接收命令的。

bleUtil工具中,有些地方我感覺還是要提示下

1、characterUUID1 、characterUUID2 、descriptorUUID 這三個是對應的收發命令的,跟找到要操作的BluetoothGattDescriptor(特性)。

在做這個項目的時候ble設備提供商給的文檔中uuid,是短的,我實際我打印出來的是長的。其實短的那種是ios來的,而android就不一樣咯。可以參考:Android與IOS的UUID的區別

2、sendWorkModel、sendStrength 這兩個方法是用來發送命令的,傳值都是byte[]數組;而接收命令的話通過receiveData方法進行解析得到的byte[]。這三個方法都是需要根據不同設備協議進行修改,這裏只是爲了案例展示。總的來說就是收發命令都是byte[]

一丶關鍵概念:

Generic Attribute Profile (GATT)

通過BLE連接,讀寫屬性類小數據的Profile通用規範。現在所有的BLE應用Profile都是基於GATT的。

Attribute Protocol (ATT)

GATT是基於ATT Protocol的。ATT針對BLE設備做了專門的優化,具體就是在傳輸過程中使用盡量少的數據。

每個屬性都有一個唯一的UUID,屬性將以characteristics and services的形式傳輸。

Characteristic

Characteristic可以理解爲一個數據類型,它包括一個value和0至多個對次value的描述(Descriptor)。

Descriptor

對Characteristic的描述,例如範圍、計量單位等。

Service

Characteristic的集合。例如一個service叫做“Heart Rate Monitor”,

它可能包含多個Characteristics,其中可能包含一個叫做“heart rate measurement"的Characteristic。

二丶角色和職責:

Android設備與BLE設備交互有兩組角色:

中心設備和外圍設備(Central vs. peripheral);

GATT server vs. GATT client.

Central vs. peripheral:

中心設備和外圍設備的概念針對的是BLE連接本身。

Central角色負責scan advertisement。而peripheral角色負責make advertisement。

GATT server vs. GATT client:

這兩種角色取決於BLE連接成功後,兩個設備間通信的方式。

舉例說明:

現有一個活動追蹤的BLE設備和一個支持BLE的Android設備。

Android設備支持Central角色,而BLE設備支持peripheral角色。

創建一個BLE連接需要這兩個角色都存在,都僅支持Central角色或者都僅支持peripheral角色則無法建立連接。

當連接建立後,它們之間就需要傳輸GATT數據。

誰做server,誰做client,則取決於具體數據傳輸的情況。

例如,如果活動追蹤的BLE設備需要向Android設備傳輸sensor數據,則活動追蹤器自然成爲了server端;

而如果活動追蹤器需要從Android設備獲取更新信息,則Android設備作爲server端可能更合適。

三丶權限及feature:

和經典藍牙一樣,應用使用藍牙,需要聲明BLUETOOTH權限,

如果需要掃描設備或者操作藍牙設置,則還需要BLUETOOTH_ADMIN權限:

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

除了藍牙權限外,如果需要BLE feature則還需要聲明uses-feature:

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

按時required爲true時,則應用只能在支持BLE的Android設備上安裝運行;

required爲false時,Android設備均可正常安裝運行,需要在代碼運行時判斷設備是否支持BLE feature

四丶通訊設備的主要步驟有:
設置權限--->打開藍牙--->掃描設備--->連接設備--->讀寫數據+通知設備的狀態改變--->斷開設備

五丶BluetoothGatt的服務層次:
BluetoothGatt--->BluetoothGattService(服務)--->BluetoothGattCharacteristic(特徵)--->BluetoothGattDescriptor(特性)

下面的就是重點啦!工具類,工具類,工具類好聽的話說三遍

import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * Created by shaolin on 6/17/16.
 */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class BleUtil {

    private static final String TAG = "BleUtil";
    private static final long SCAN_PERIOD = 10000;

    public static String characterUUID1 = "0000fff2-0000-1000-8000-00805f9b34fb";//APP發送命令
    public static String characterUUID2 = "0000fff1-0000-1000-8000-00805f9b34fb";//BLE用於回覆命令
    private static String descriptorUUID = "00002902-0000-1000-8000-00805f9b34fb";//BLE設備特性的UUID

    public static byte[] workModel = {0x02, 0x01};

    private Context mContext;
    private static BleUtil mInstance;

    //作爲中央來使用和處理數據;
    private BluetoothGatt mGatt;

    private BluetoothManager manager;
    private BTUtilListener mListener;
    private BluetoothDevice mCurDevice;
    private BluetoothAdapter mBtAdapter;
    private List<BluetoothDevice> listDevice;
    private List<BluetoothGattService> serviceList;//服務
    private List<BluetoothGattCharacteristic> characterList;//特徵

    private BluetoothGattService service;
    private BluetoothGattCharacteristic character1;
    private BluetoothGattCharacteristic character2;


    public static synchronized BleUtil getInstance() {
        if (mInstance == null) {
            mInstance = new BleUtil();
        }
        return mInstance;
    }


    public void setContext(Context context) {
        mContext = context;
        init();
    }

    public void init() {
        listDevice = new ArrayList<>();
        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            showToast("BLE不支持此設備!");
            ((Activity) mContext).finish();
        }
        manager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
        //注:這裏通過getSystemService獲取BluetoothManager,
        //再通過BluetoothManager獲取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API level 18)。
        if (manager != null) {
            mBtAdapter = manager.getAdapter();
        }
        if (mBtAdapter == null || !mBtAdapter.isEnabled()) {
            mBtAdapter.enable();
            /*Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            mContext.startActivity(enableBtIntent);*/
        }
    }

    //掃描設備的回調
    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

        @Override
        public void onLeScan(final BluetoothDevice device, int rssi,
                             byte[] scanRecord) {
            ((Activity) mContext).runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (!listDevice.contains(device)) {
                        //不重複添加
                        listDevice.add(device);
                        mListener.onLeScanDevices(listDevice);
                        Log.e(TAG, "device:" + device.toString());
                    }
                }
            });
        }
    };

    //掃描設備
    public void scanLeDevice(final boolean enable) {
        if (enable) {
            Handler mHandler = new Handler();
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    stopScan();
                    Log.e(TAG, "run: stop");
                }
            }, SCAN_PERIOD);
            startScan();
            Log.e(TAG, "start");
        } else {
            stopScan();
            Log.e(TAG, "stop");
        }
    }

    //開始掃描BLE設備
    private void startScan() {
        mBtAdapter.startLeScan(mLeScanCallback);
        mListener.onLeScanStart();
    }

    //停止掃描BLE設備
    private void stopScan() {
        mBtAdapter.stopLeScan(mLeScanCallback);
        mListener.onLeScanStop();
    }

    //返回中央的狀態和周邊提供的數據
    private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                            int newState) {
            Log.e(TAG, "onConnectionStateChange");
            switch (newState) {
                case BluetoothProfile.STATE_CONNECTED:
                    Log.e(TAG, "STATE_CONNECTED");
                    mListener.onConnected(mCurDevice);
                    gatt.discoverServices(); //搜索連接設備所支持的service
                    break;
                case BluetoothProfile.STATE_DISCONNECTED:
                    mListener.onDisConnected(mCurDevice);
                    disConnGatt();
                    Log.e(TAG, "STATE_DISCONNECTED");
                    break;
                case BluetoothProfile.STATE_CONNECTING:
                    mListener.onConnecting(mCurDevice);
                    Log.e(TAG, "STATE_CONNECTING");
                    break;
                case BluetoothProfile.STATE_DISCONNECTING:
                    mListener.onDisConnecting(mCurDevice);
                    Log.e(TAG, "STATE_DISCONNECTING");
                    break;
            }
            super.onConnectionStateChange(gatt, status, newState);
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.d(TAG, "onServicesDiscovered");
            if (status == BluetoothGatt.GATT_SUCCESS) {
                serviceList = gatt.getServices();
                for (int i = 0; i < serviceList.size(); i++) {
                    BluetoothGattService theService = serviceList.get(i);

                    Log.e(TAG, "ServiceName:" + theService.getUuid());
                    characterList = theService.getCharacteristics();
                    for (int j = 0; j < characterList.size(); j++) {
                        String uuid = characterList.get(j).getUuid().toString();
                        Log.e(TAG, "---CharacterName:" + uuid);
                        if (uuid.equals(characterUUID1)) {
                            character1 = characterList.get(j);
                        } else if (uuid.equals(characterUUID2)) {
                            character2 = characterList.get(j);
                            setNotification();
                        }
                    }
                }
            }
            super.onServicesDiscovered(gatt, status);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.e(TAG, "onCharacteristicRead");
            super.onCharacteristicRead(gatt, characteristic, status);
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.e(TAG, "onCharacteristicWrite");
            super.onCharacteristicWrite(gatt, characteristic, status);
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            Log.e(TAG, "onCharacteristicChanged");
//            這裏是可以監聽到設備自身或者手機改變設備的一些數據修改h通知
            receiveData(characteristic);
            super.onCharacteristicChanged(gatt, characteristic);
        }
    };

    //獲取設備指定的特徵中的特性,其中對其進行監聽, setCharacteristicNotification與上面的回調onCharacteristicChanged進行一一搭配
    private void setNotification() {
        mGatt.setCharacteristicNotification(character2, true);
        BluetoothGattDescriptor descriptor = character2.getDescriptor(UUID.fromString(descriptorUUID));
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mGatt.writeDescriptor(descriptor);
    }

    //接收數據,對其進行處理
    private void receiveData(BluetoothGattCharacteristic ch) {
        byte[] bytes = ch.getValue();
        int cmd = bytes[0];
        int agree = bytes[1];
        switch (cmd) {
            case 1:
                mListener.onStrength(agree);
                Log.e(TAG, "手機通知BLE設備強度:" + agree);
                break;
            case 2:
                mListener.onModel(agree);
                Log.e(TAG, "工作模式:" + agree);
                break;
            case 3:
                mListener.onStrength(agree);
                Log.e(TAG, "設備自身通知改變強度:" + agree);
                break;
        }
    }

    //連接設備
    public void connectLeDevice(int devicePos) {
        mBtAdapter.stopLeScan(mLeScanCallback);
        mCurDevice = listDevice.get(devicePos);
        checkConnGatt();
    }

    //發送進入工作模式請求
    public void sendWorkModel() {
        if (character1 != null) {
            character1.setValue(workModel);
            mGatt.writeCharacteristic(character1);
        }
    }

    //發送強度
    public void sendStrength(int strength) {
        byte[] strengthModel = {0x01, (byte) strength};
        if (character1 != null) {
            character1.setValue(strengthModel);
            mGatt.writeCharacteristic(character1);
        }
    }

    //檢查設備是否連接了
    private void checkConnGatt() {
        if (mGatt == null) {
            mGatt = mCurDevice.connectGatt(mContext, true, mGattCallback);
            mListener.onConnecting(mCurDevice);
        } else {
            mGatt.connect();
            mGatt.discoverServices();
        }
    }

    //  斷開設備連接
    private void disConnGatt() {
        if (mGatt != null) {
            mGatt.disconnect();
            mGatt.close();
            mGatt = null;
            listDevice = new ArrayList<>();
            mListener.onLeScanDevices(listDevice);
        }
    }

    private void showToast(String message) {
        Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
    }

    public void setBTUtilListener(BTUtilListener listener) {
        mListener = listener;
    }

    public interface BTUtilListener {
        void onLeScanStart(); // 掃描開始

        void onLeScanStop();  // 掃描停止

        void onLeScanDevices(List<BluetoothDevice> listDevice); //掃描得到的設備

        void onConnected(BluetoothDevice mCurDevice); //設備的連接

        void onDisConnected(BluetoothDevice mCurDevice); //設備斷開連接

        void onConnecting(BluetoothDevice mCurDevice); //設備連接中

        void onDisConnecting(BluetoothDevice mCurDevice); //設備連接失敗

        void onStrength(int strength); //給設備設置強度

        void onModel(int model); //設備模式
    }
}

參考:http://www.myext.cn/android/a_4699.html

重點:  Android與IOS的UUID的區別

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章