1. 程式人生 > >Android 藍芽BLE開發詳解

Android 藍芽BLE開發詳解

轉載請註明出處。    https://blog.csdn.net/kong_gu_you_lan/article/details/81009800
1.介紹
藍芽是一種短距離的無線通訊技術,可以實現固定裝置、移動裝置之間的資料交換。一般將藍芽分為兩大類,藍芽3.0規範之前的版本稱為傳統藍芽,藍芽4.0規範之後的版本稱為低功耗藍芽,也就是常說的BLE(Bluetooth Low Energy)。

本文主要講解的是Android裝置與BLE裝置之間的通訊,Android 從4.3版本(API Level 18)開始支援BLE通訊。

2.開發流程
看圖說話:

首先要判斷當前的Android裝置是否支援藍芽,如果支援則再判斷當前藍芽是否處於開啟狀態,如果未開啟則傳送廣播通知系統開啟藍芽,藍芽開啟後開始搜尋周圍的藍芽裝置,注意搜尋一定要設定超時處理,搜尋到指定藍芽裝置後停止搜尋任務。

此時可以以列表的形式供使用者選擇需要連線的裝置,或者內部自動連線特定的裝置,連線成功後,搜尋此藍芽裝置提供的服務(特性、描述符的集合),搜尋完成後設定一些對應的引數,即可與藍芽裝置進行通訊了。

3.相關API
看下我們在開發過程中需要用到的一些API:

1.BluetoothAdapter
本地藍芽介面卡,用於一些藍芽的基本操作,比如判斷藍芽是否開啟、搜尋藍芽裝置等。

2.BluetoothDevice
藍芽裝置物件,包含一些藍芽裝置的屬性,比如裝置名稱、mac地址等。

3.BluetoothProfile
一個通用的藍芽規範,裝置之間按照這個規範來收發資料。

4.BluetoothGatt
藍芽通用屬性協議,定義了BLE通訊的基本規則,是BluetoothProfile的實現類,Gatt是Generic Attribute Profile的縮寫,用於連線裝置、搜尋服務等操作。

5.BluetoothGattCallback
藍芽裝置連線成功後,用於回撥一些操作的結果,必須連線成功後才會回撥。

6.BluetoothGattService
藍芽裝置提供的服務,是藍芽裝置特徵的集合。

7.BluetoothGattCharacteristic
藍芽裝置特徵,是構建GATT服務的基本資料單元。

8.BluetoothGattDescriptor
藍芽裝置特徵描述符,是對特徵的額外描述。

4.程式碼實現
需用用到的許可權:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
1
2
Demo中把藍芽的相關操作寫在了Service中,這樣即使應用退出也不會影響藍芽的連線,在Service中通過廣播的方式通知Activity做相應的處理,看下Service:

public class BleService extends Service {

    private final String TAG = BleService.class.getSimpleName();
    private BluetoothGatt mBluetoothGatt;

    // 藍芽連線狀態
    private int mConnectionState = 0;
    // 藍芽連線已斷開
    private final int STATE_DISCONNECTED = 0;
    // 藍芽正在連線
    private final int STATE_CONNECTING = 1;
    // 藍芽已連線
    private final int STATE_CONNECTED = 2;

    // 藍芽已連線
    public final static String ACTION_GATT_CONNECTED = "com.yl.ble.ACTION_GATT_CONNECTED";
    // 藍芽已斷開
    public final static String ACTION_GATT_DISCONNECTED = "com.yl.ble.ACTION_GATT_DISCONNECTED";
    // 發現GATT服務
    public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.yl.ble.ACTION_GATT_SERVICES_DISCOVERED";
    // 收到藍芽資料
    public final static String ACTION_DATA_AVAILABLE = "com.yl.ble.ACTION_DATA_AVAILABLE";
    // 連線失敗
    public final static String ACTION_CONNECTING_FAIL = "com.yl.ble.ACTION_CONNECTING_FAIL";
    // 藍芽資料
    public final static String EXTRA_DATA = "com.yl.ble.EXTRA_DATA";

    // 服務標識
    private final UUID SERVICE_UUID = UUID.fromString("0000ace0-0000-1000-8000-00805f9b34fb");
    // 特徵標識(讀取資料)
    private final UUID CHARACTERISTIC_READ_UUID = UUID.fromString("0000ace0-0001-1000-8000-00805f9b34fb");
    // 特徵標識(傳送資料)
    private final UUID CHARACTERISTIC_WRITE_UUID = UUID.fromString("0000ace0-0003-1000-8000-00805f9b34fb");
    // 描述標識
    private final UUID DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

    // 服務相關
    private final IBinder mBinder = new LocalBinder();

    public class LocalBinder extends Binder {
        public BleService getService() {
            return BleService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        release();
        return super.onUnbind(intent);
    }

    /**
     * 藍芽操作回撥
     * 藍芽連線狀態才會回撥
     */
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                // 藍芽已連線
                mConnectionState = STATE_CONNECTED;
                sendBleBroadcast(ACTION_GATT_CONNECTED);
                // 搜尋GATT服務
                mBluetoothGatt.discoverServices();

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                // 藍芽已斷開連線
                mConnectionState = STATE_DISCONNECTED;
                sendBleBroadcast(ACTION_GATT_DISCONNECTED);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            // 發現GATT服務
            if (status == BluetoothGatt.GATT_SUCCESS) {
                setBleNotification();
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            // 收到資料
            sendBleBroadcast(ACTION_DATA_AVAILABLE, characteristic);
        }
    };

    /**
     * 傳送通知
     *
     * @param action 廣播Action
     */
    private void sendBleBroadcast(String action) {
        Intent intent = new Intent(action);
        sendBroadcast(intent);
    }

    /**
     * 傳送通知
     *
     * @param action         廣播Action
     * @param characteristic 資料
     */
    private void sendBleBroadcast(String action, BluetoothGattCharacteristic characteristic) {
        Intent intent = new Intent(action);
        if (CHARACTERISTIC_READ_UUID.equals(characteristic.getUuid())) {
            intent.putExtra(EXTRA_DATA, characteristic.getValue());
        }
        sendBroadcast(intent);
    }

    /**
     * 藍芽連線
     *
     * @param bluetoothAdapter BluetoothAdapter
     * @param address          裝置mac地址
     * @return true:成功 false:
     */
    public boolean connect(BluetoothAdapter bluetoothAdapter, String address) {
        if (bluetoothAdapter == null || TextUtils.isEmpty(address)) {
            return false;
        }

        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            return false;
        }
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        mConnectionState = STATE_CONNECTING;
        return true;
    }

    /**
     * 藍芽斷開連線
     */
    public void disconnect() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.disconnect();
    }

    /**
     * 釋放相關資源
     */
    public void release() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

    /**
     * 設定藍芽裝置在資料改變時,通知App
     */
    public void setBleNotification() {
        if (mBluetoothGatt == null) {
            sendBleBroadcast(ACTION_CONNECTING_FAIL);
            return;
        }

        // 獲取藍芽裝置的服務
        BluetoothGattService gattService = mBluetoothGatt.getService(SERVICE_UUID);
        if (gattService == null) {
            sendBleBroadcast(ACTION_CONNECTING_FAIL);
            return;
        }

        // 獲取藍芽裝置的特徵
        BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(CHARACTERISTIC_READ_UUID);
        if (gattCharacteristic == null) {
            sendBleBroadcast(ACTION_CONNECTING_FAIL);
            return;
        }

        // 獲取藍芽裝置特徵的描述符
        BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(DESCRIPTOR_UUID);
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        if (mBluetoothGatt.writeDescriptor(descriptor)) {
            // 藍芽裝置在資料改變時,通知App,App在收到資料後回撥onCharacteristicChanged方法
            mBluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
        }
    }

    /**
     * 傳送資料
     *
     * @param data 資料
     * @return true:傳送成功 false:傳送失敗
     */
    public boolean sendData(byte[] data) {
        // 獲取藍芽裝置的服務
        BluetoothGattService gattService = null;
        if (mBluetoothGatt != null) {
            gattService = mBluetoothGatt.getService(SERVICE_UUID);
        }
        if (gattService == null) {
            return false;
        }

        // 獲取藍芽裝置的特徵
        BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(CHARACTERISTIC_WRITE_UUID);
        if (gattCharacteristic == null) {
            return false;
        }

        // 傳送資料
        gattCharacteristic.setValue(data);
        return mBluetoothGatt.writeCharacteristic(gattCharacteristic);
    }
}

當藍芽連線成功後,會回撥BluetoothGattCallback中的onConnectionStateChange方法,但是此時還不能與藍芽裝置進行通訊,還需要呼叫BluetoothGatt中的discoverServices方法搜尋藍芽裝置提供的服務,也就是我們上文中提到的BluetoothGattService,搜尋完成後會回撥BluetoothGattCallback中的onServicesDiscovered方法,這時就輪到setBleNotification方法大顯身手了。

首先通過一個UUID獲取到藍芽裝置提供的服務(BluetoothGattService),這個UUID是由硬體程式定義的,開發的過程中看文件就可以了,獲取到服務後,再通過一個UUID獲取到藍芽裝置的特徵(BluetoothGattCharacteristic),然後再獲取裝置特徵的描述符(BluetoothGattDescriptor),設定在藍芽裝置資料改變時,主動通知App,此時回撥BluetoothGattCallback中的onCharacteristicChanged方法,通過characteristic.getValue()可以獲取到通知的資料。

在BluetoothGattCallback還有很多回調方法,都是與BluetoothGatt的操作相對應的,很好理解,在這裡就不一一舉例了。

到這裡Service的邏輯就介紹完了,下面來看下在Activity中如何進行處理:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private final int REQUEST_ENABLE_BT = 1;
    private RecyclerView rvDeviceList;
    private Button btnScan;
    private BluetoothAdapter mBtAdapter;
    private BleService mBleService;
    private BroadcastReceiver mBleReceiver;
    private DeviceListAdapter mDeviceListAdapter;
    private List<BluetoothDevice> mBluetoothDeviceList;
    private List<String> mRssiList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rvDeviceList = findViewById(R.id.rv_device_list);
        btnScan = findViewById(R.id.btn_scan);
        btnScan.setOnClickListener(this);

        initBle();
        initData();
        registerBleReceiver();
    }

    /**
     * 初始化藍芽
     */
    private void initBle() {
        mBtAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBtAdapter == null) {
            Toast.makeText(this, "藍芽不可用", Toast.LENGTH_LONG).show();
            return;
        }

        if (!mBtAdapter.isEnabled()) {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, REQUEST_ENABLE_BT);
            return;
        }

        // 搜尋藍芽裝置
        scanBleDevice();
    }

    /**
     * 初始化資料
     */
    private void initData() {
        // 藍芽裝置列表
        mBluetoothDeviceList = new ArrayList<>();
        // 藍芽裝置RSSI列表
        mRssiList = new ArrayList<>();
        mDeviceListAdapter = new DeviceListAdapter(mBluetoothDeviceList, mRssiList);
        rvDeviceList.setLayoutManager(new LinearLayoutManager(this));
        rvDeviceList.setAdapter(mDeviceListAdapter);

        // 連線藍芽裝置
        mDeviceListAdapter.setOnItemClickListener(new DeviceListAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "開始連線", Toast.LENGTH_SHORT).show();
                mBtAdapter.stopLeScan(mLeScanCallback);
                mBleService.connect(mBtAdapter, mBluetoothDeviceList.get(position).getAddress());
            }
        });
    }

    /**
     * 註冊藍芽資訊接收器
     */
    private void registerBleReceiver() {
        // 繫結服務
        Intent intent = new Intent(this, BleService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        startService(intent);

        // 註冊藍芽資訊廣播接收器
        IntentFilter filter = new IntentFilter();
        filter.addAction(BleService.ACTION_GATT_CONNECTED);
        filter.addAction(BleService.ACTION_GATT_DISCONNECTED);
        filter.addAction(BleService.ACTION_GATT_SERVICES_DISCOVERED);
        filter.addAction(BleService.ACTION_DATA_AVAILABLE);
        filter.addAction(BleService.ACTION_CONNECTING_FAIL);
        mBleReceiver = new BleReceiver();
        registerReceiver(mBleReceiver, filter);
    }

    /**
     * 搜尋藍芽裝置
     */
    private void scanBleDevice() {
        mBtAdapter.stopLeScan(mLeScanCallback);
        mBtAdapter.startLeScan(mLeScanCallback);
        // 搜尋10s
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mBtAdapter.stopLeScan(mLeScanCallback);
            }
        }, 10000);
    }

    /**
     * 搜尋藍芽裝置回撥
     */
    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
            if (!mBluetoothDeviceList.contains(bluetoothDevice)) {
                mBluetoothDeviceList.add(bluetoothDevice);
                mRssiList.add(String.valueOf(i));
                mDeviceListAdapter.notifyDataSetChanged();
            }
        }
    };

    /**
     * 服務
     */
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder rawBinder) {
            mBleService = ((BleService.LocalBinder) rawBinder).getService();
        }

        public void onServiceDisconnected(ComponentName classname) {
            mBleService = null;
        }
    };

    /**
     * 藍芽資訊接收器
     */
    private class BleReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (TextUtils.isEmpty(action)) {
                return;
            }
            switch (action) {
                case BleService.ACTION_GATT_CONNECTED:
                    Toast.makeText(MainActivity.this, "藍芽已連線", Toast.LENGTH_SHORT).show();
                    break;

                case BleService.ACTION_GATT_DISCONNECTED:
                    Toast.makeText(MainActivity.this, "藍芽已斷開", Toast.LENGTH_SHORT).show();
                    mBleService.release();
                    break;

                case BleService.ACTION_CONNECTING_FAIL:
                    Toast.makeText(MainActivity.this, "藍芽已斷開", Toast.LENGTH_SHORT).show();
                    mBleService.disconnect();
                    break;

                case BleService.ACTION_DATA_AVAILABLE:
                    byte[] data = intent.getByteArrayExtra(BleService.EXTRA_DATA);
                    Log.i("藍芽", "收到的資料:" + ByteUtils.byteArrayToHexString(data));
                    break;

                default:
                    break;
            }
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_scan: // 搜尋藍芽
                // 搜尋藍芽裝置
                scanBleDevice();
                // 初始化資料
                initData();
                // 註冊藍芽資訊接收器
                registerBleReceiver();
                break;

            default:
                break;
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        switch (requestCode) {
            case REQUEST_ENABLE_BT:
                // 搜尋藍芽裝置
                scanBleDevice();
                break;

            default:
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mBleReceiver != null) {
            unregisterReceiver(mBleReceiver);
            mBleReceiver = null;
        }
        unbindService(mServiceConnection);
        mBleService = null;
    }
}

應用啟動後首先檢測當前裝置藍芽是否可用,以及藍芽是否開啟,然後開始搜尋藍芽裝置,注意一定要設定搜尋的超時時間,將搜尋到的藍芽裝置資訊存到一個集合中,搜尋時間為10s。

然後繫結BleService,以便呼叫Service中的方法,同時再通過startService的方式開啟服務,這樣Activity被銷燬後service依舊可以執行。

接下來再註冊一個廣播接收器,接收service發來的訊息並做一些處理,這時在藍芽裝置列表中點選你想要連線的裝置就可以正常通訊了。

在產品需求中,一般應用都是長時間連線一個硬體裝置,比如手環,此時就需要做一些特殊處理,比如藍芽連線斷開後主動搜尋之前已經連線的裝置,或者應用執行過程中手機藍芽被關閉之後的處理等。

5.寫在最後
到這裡藍芽BLE開發就介紹完了,如有錯誤或者遺漏的地方可以給我留言評論,謝謝!

程式碼已上傳至GitHub,歡迎Star、Fork!

GitHub地址:https://github.com/alidili/Demos/tree/master/BleDemo

本文Demo的Apk下載地址:https://github.com/alidili/Demos/raw/master/BleDemo/BleDemo.apk
--------------------- 
作者:容華謝後 
來源:CSDN 
原文:https://blog.csdn.net/kong_gu_you_lan/article/details/81009800 
版權宣告:本文為博主原創文章,轉載請附上博文連結!