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.代码实现

需用用到的权限:

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 mBluetoothDeviceList;    private List 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

更多相关文章

  1. android第一次启动时Settings的默认值
  2. 传感器概念
  3. android 获取当前设备的ip地址 mac地址 设备号 手机号 运营商信
  4. Android(安卓)Pie 引入 Keystore 新特性,安全防护再升级
  5. Android(安卓)9.0 网络配置http访问问题
  6. Android基本数据存储方法
  7. Android(安卓)简单的Http框架
  8. Android——“i分享”APP开发Day11
  9. 一 Android(安卓)O WiFi 框架变化

随机推荐

  1. android WARNING: Application does not
  2. 转:Android联系人数据库全解析
  3. 解决Eclipse3.6中Android(安卓)代码自动
  4. android studio AndroidManifest.xml命名
  5. Android(安卓)Universal Image Loader 源
  6. Android布局属性详解
  7. 编译代码报出Android(安卓)library proje
  8. android触屏手势识别全解析
  9. 模仿天天动听的seekbar
  10. Android(安卓)关于WebView的相关属性