Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯

之前的文章简单实现了使用传统蓝牙进行通讯的DEMO,说是最简单其实只是夸张的写法~毕竟标题党横行,我们也得学学点~至少没有UC震惊部那么夸张。

然后,本来是要写Android开发之BlueTooth--最简单的Andorid低功耗(BLE)蓝牙通讯Demo的,写了一半发现BLE简单不起来,所以分两篇来写BLE相关的应用。因此大家可能发现,你下篇的编写时间别上篇还早?这只是假象~CSDN的发布时间是建立草稿的时间,我也是醉了。实际发布时间比上一篇迟将近一周...

上一篇已经简单的写了一下关于GATT服务端的一些知识以及搭建。毕竟这个部分很少要我们做的,所以网络上这方面的资源相对较少。通常外围设备都是有固定产品的,用不着我们开发,例如心率器,血压器等等各种感应器,直接把相关数据格式以及UUID给我们在手机上进行就可以了。但是了解一下也没什么坏处。那么这篇就是简单实现如何用手机连接这些产品获取到对应数据了。

 

服务端

上一篇文章我们写了一个服务端,这篇就是基于那个服务端程序来建立的连接。所以大家有必要先看一下上一篇,否则这里对应数据的UUID怎么来都不知道。还需要说明的一点是,上一篇我们仅仅成功开启了服务,但是数据更新的逻辑没写。假如他是一个心率传感器,那么我们心率改变时,理应要更新数据,并通知客户端,所以上一篇还缺少了一段代码:

 

 //4.模拟数据更新
    private void updateCharacteristic() {
        if (mBluetoothGattServer == null) {
            return;
        }
        final Handler updateHandler = new Handler();
        updateHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                for (BluetoothDevice d : mRegisteredDevices) {
                    BluetoothGattCharacteristic newCharacteristic = mBluetoothGattServer
                            .getService(TIME_SERVICE)
                            .getCharacteristic(CURRENT_TIME);
                    byte[] data = ("数据更新" + System.currentTimeMillis()).getBytes();
                    newCharacteristic.setValue(data);
                    mBluetoothGattServer.notifyCharacteristicChanged(d, newCharacteristic, false);
                }
                updateHandler.postDelayed(this, 5000);

            }
        }, 5000);//5s更新一次
        outputLog("数据模拟更新启动",MSG_TYPE_NORMAL);

    }


当广播成功后就启动这个数据更新的函数就行了。接下来开始编写客户端了。编写之前先说明几点:

 

 

BLE客户端和传统蓝牙代码上的差异

1.获取适配器

传统蓝牙是这么获取适配器的:

 

if (mBluetoothAdapter == null) {
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }

BLE蓝牙是这样的:

 

 

if (mBluetoothAdapter == null) {
            /**这里和传统的蓝牙有什么区别呢?*/
            final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
            mBluetoothAdapter = bluetoothManager.getAdapter();
          
        }

 

到底这算不算区别?反正官方文档这么写,其实这两个互换没感觉到有什么变化,反正都是获取到适配器。具体有没有影响,我还没得测试。有兴趣的试试看,然后告诉我哈~

 

2.搜索回调

传统蓝牙是需要通过广播来获取搜索结果的:

 

 private void registerRec() {
        //3.注册蓝牙广播
        mReceiver = new BlueToothStateReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothDevice.ACTION_FOUND);//搜索到蓝牙
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索结束
        registerReceiver(mReceiver, filter);
    }

 class BlueToothStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(MainActivity.this, "触发广播", Toast.LENGTH_SHORT).show();
            String action = intent.getAction();
            switch (action) {
                case BluetoothDevice.ACTION_FOUND:
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    Toast.makeText(MainActivity.this, "找到设备" + device.getName(), Toast.LENGTH_SHORT).show();
                    if (mRvAdapter != null) {
                        mRvAdapter.addDevice(device);
                    }
                    break;
                case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                    mMessageAdapter.addMessage("搜索结束");
                    break;
            }
        }
    }


BLE蓝牙是直接以回调方法进行监听的:

 

 

 private MyLeScanCallback1 mLeScanCallback1 = new MyLeScanCallback1();

private void scanBleDevices1(boolean enable) {
        if (enable) {
            outputLog("正在搜索设备");
            outputLog("当前搜索回调:callback 1");
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback1);
                    outputLog("停止搜索");
                }
            }, 10000);//设置10秒超时
            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback1);
        } else {
            outputLog("停止搜索");
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback1);
        }
    }

class MyLeScanCallback1 implements BluetoothAdapter.LeScanCallback {
        @Override
        public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
            mDeviceAdapter.addDevice(bluetoothDevice);
        }
    }

 

 

3.连接方式

传统蓝牙连接别的设备:

 

 public void run() {
            if (mDevice != null) {
                try {
                    //获取套接字
                    BluetoothSocket temp = mDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));
                    //mDevice.createRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));//sdk 2.3以下使用
                    mSocket = temp;
                    //发起连接请求
                    if (mSocket != null) {
                        mSocket.connect();
                    }
                    sendHandlerMsg("连接 " + mDevice.getName() + "成功!");
                    //获取输入输出流
                    btIs = mSocket.getInputStream();
                    btOs = mSocket.getOutputStream();

                    //通讯-接收消息
                    BufferedReader reader = new BufferedReader(new InputStreamReader(btIs, "UTF-8"));
                    String content = null;
                    while (canRecv) {
                        content = reader.readLine();
                        sendHandlerMsg("收到消息:" + content);
                    }


                } catch (IOException e) {
                    e.printStackTrace();
                    sendHandlerMsg("错误:" + e.getMessage());
                } finally {
                    try {
                        if (mSocket != null) {
                            mSocket.close();
                        }
                        //btIs.close();//两个输出流都依赖socket,关闭socket即可
                        //btOs.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        sendHandlerMsg("错误:" + e.getMessage());
                    }
                }
            }
        }


BLE连接服务端:

 

 

private void connectGatt(BluetoothDevice device) {
        outputLog("正在连接到:"+device.getName());
        //新建一个链接
        mBluetoothGatt = device.connectGatt(BleMainActivity.this, false, mBluetoothGattCallback);
        //mBluetoothGatt.connect();//如果是断开重连
        //mBluetoothGatt.disconnect();//断开当前连接
    }

然后在回调里处理连接成功或者失败的逻辑。

 

 

4.通讯方式

传统蓝牙是获取到socket只有以类似于TCP的方式进行通讯。

BLE蓝牙则是通过读写特性来进行通讯。

 

5.其他

代码上的区别我目前也就总结了这几点,至于其他只能在更详细的开发中慢慢发现了。

 

 

搜索回调的方式居然过时了?

相信不少人在按照谷歌文档或者其他翻译文档的博客来写demo得时候发现:

Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯_第1张图片
Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯_第2张图片
 

不对啊!我明明按照官方的来,怎么会过时!这官方文档到底自己没有进行更新我就不知道了,反正他在API>=21时,确实过时了。那我们能用什么方法来代替他?我们又应该怎么修改?这里涉及到一个新的类:BluetoothLeScanner 蓝牙扫描器。先看他的获取实例代码

 

 

if (mBluetoothAdapter == null) {
            final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
            mBluetoothAdapter = bluetoothManager.getAdapter();
            //mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
            //如果调用scanBleDevices2(),请加上这句。 Call requires API level 21
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mBleScanner = mBluetoothAdapter.getBluetoothLeScanner();
            }
        }

然后把把搜索方法修改如下:

 

Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯_第3张图片

 

Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯_第4张图片

 

这么修改就不会提示过时了,而且第二种回调提供更丰富的处理结果。

 

 

 

开始搭建客户端(中央设备)

说了这么多还没开始真正的写客户端呢,那现在开始一步一步来写一个简单的客户端。

1.首先看看需要准备东西有哪些:

 

private static final String TAG = "BleMainActivity";
    public static final int REQUEST_BT_ENABLE_CODE = 200;

    private BluetoothAdapter mBluetoothAdapter;//蓝牙适配器
    private MyLeScanCallback1 mLeScanCallback1;//搜索回调1
    private MyLeScanCallback2 mLeScanCallback2;//搜索回调2

    private BluetoothGatt mBluetoothGatt;//GATT客户端
    private BluetoothLeScanner mBleScanner;//BLE扫描器
    private boolean mScanning;//是否正在搜索
    private RecyclerView devicesView, msgsView;//设备列表、日志列表
    private RvAdapter mDeviceAdapter;//设备搜索结果适配器
    private MsgAdapter mMessageAdapter;//日志适配器
    private List mServiceList;//服务列表

    private SimpleDateFormat mDateFormat;
    private boolean isCallback1=true;

2.前面几步和传统蓝牙差不多,首先打开蓝牙,最好能判断是否支持BLE

 

 

private void openBT() {
        if (mBluetoothAdapter == null) {
            /**这里和传统的蓝牙有什么区别呢?*/
            final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
            mBluetoothAdapter = bluetoothManager.getAdapter();
            //mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
            //如果调用scanBleDevices2(),请加上这句。 Call requires API level 21
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mBleScanner = mBluetoothAdapter.getBluetoothLeScanner();
            }
        }
        //1.设备不支持蓝牙,结束应用
        if (mBluetoothAdapter == null) {
            finish();
            return;
        }
        //2.判断蓝牙是否打开
        if (!mBluetoothAdapter.enable()) {
            //没打开请求打开
            Intent btEnable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(btEnable, REQUEST_BT_ENABLE_CODE);
            outputLog("正在打开蓝牙");
        }
    }


2.然后是搜索设备,注意,搜索是非常耗电的,最好是搜索到设备之后立马结束搜索,或者10s后停止。

 

API<21

 

 private void scanBleDevices1(boolean enable) {
        if (enable) {
            outputLog("正在搜索设备");
            outputLog("当前搜索回调:callback 1");
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback1);
                    outputLog("停止搜索");
                }
            }, 10000);//设置10秒超时
            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback1);
        } else {
            outputLog("停止搜索");
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback1);
        }
    }

API>=21

 

 

private void scanBleDevices2(boolean enable) {
        if (mBleScanner == null) {
            outputLog("搜索器初始化失败");
            return;
        }
        if (enable) {
            outputLog("当前搜索回调:callback 2");
            outputLog("正在搜索设备");
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBleScanner.stopScan(mLeScanCallback2);
                    outputLog("停止搜索");
                }
            }, 10000);//设置10秒超时
            mScanning = true;
            mBleScanner.startScan(mLeScanCallback2);
        } else {
            mScanning = false;
            mBleScanner.stopScan(mLeScanCallback2);
            outputLog("停止搜索");
        }
    }

3.处理搜索结果

 

API<21

 

 class MyLeScanCallback1 implements BluetoothAdapter.LeScanCallback {
        @Override
        public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
            mDeviceAdapter.addDevice(bluetoothDevice);
        }
    }

 

 

 

 

 

API>=21

 

 class MyLeScanCallback2 extends ScanCallback {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            mDeviceAdapter.addDevice(result.getDevice());
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
        }

        @Override
        public void onBatchScanResults(List results) {
            super.onBatchScanResults(results);
            //批量结果
        }
    }


4.在搜索结果,点击链接某个设备

 

 

  private void connectGatt(BluetoothDevice device) {
        outputLog("正在连接到:"+device.getName());
        //新建一个链接
        mBluetoothGatt = device.connectGatt(BleMainActivity.this, false, mBluetoothGattCallback);
        //mBluetoothGatt.connect();//如果是断开重连
        //mBluetoothGatt.disconnect();//断开当前连接
    }


5.处理发起连接后回调

 

 

 private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
        }

        @Override
        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyRead(gatt, txPhy, rxPhy, status);
        }

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                //成功连接
                outputLog("连接蓝牙服务成功");
                mBluetoothGatt.discoverServices();//搜索服务器中的包含服务
                outputLog("搜索外围服务");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                //断开连接
                outputLog("断开蓝牙服务");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                outputLog("成功搜索到服务");
                //找到服务
                //获取服务列表
                mServiceList = gatt.getServices();
                //设置特性更改通知
                setCharacteristicNotification();
            } else {
                outputLog("服务搜索失败");
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //读取特性
                //characteristic.getValue();
                outputLog("读取到特性:"+new String(characteristic.getValue()));
            }
        }
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //反写成功
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            //服务端特性改变 读取新数据
            outputLog("接收到特性变化:"+new String(characteristic.getValue()));
            gatt.readCharacteristic(characteristic);
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //参考特性的处理
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //反写成功
            }
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
        }
    };


6.设置监听某个特性的的变化

 

 

 /**
     * 找到自己想要获取的特性,并设置更改通知
     * 例如,服务里面提供了心率检测和血压监测,我现在只想获取心率
     */
    private void setCharacteristicNotification() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        //找到对应服务
        BluetoothGattCharacteristic characteristic = null;
        for (BluetoothGattService s : mServiceList) {
            if (s.getUuid().equals(GattServerActivity.TIME_SERVICE)) {
                //找到对应服务的特性
                List cList = s.getCharacteristics();
                for (BluetoothGattCharacteristic c : cList) {
                    if (c.getUuid().equals(GattServerActivity.CURRENT_TIME)) {
                        //找出需要通知改变的特性
                        characteristic = c;
                    }
                }
            }
        }
        if (characteristic == null) {
            return;//服务中不包含我们需要获取的特性
        }
        //启动通知:BLE应用程序通常在设备上的特定特性发生变化时要求收到通知
        //一旦为特性启用通知,如果远程设备上的特性发生变化,则触发回调onCharacteristicChanged()
        mBluetoothGatt.setCharacteristicNotification(characteristic, true);
        outputLog("开启特性变化通知成功");
        // This is specific to Heart Rate Measurement.
        //这里要对应我服务端所使用的UUID,详情请查看上一篇博客
        //更改特性描述
        if (GattServerActivity.CURRENT_TIME.equals(characteristic.getUuid())) {
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(GattServerActivity.CLIENT_CONFIG);
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
        }

        //然后读一下这个特性
        mBluetoothGatt.readCharacteristic(characteristic);//会触发回调,这里就到回调中处理
    }


7.其他操作,读写特性、读写描述等

 

 

//写特性,其他操作类似,直接使用mBluetoothGatt调用对应的方法即可
    private boolean writeCharacteristic(BluetoothGattCharacteristic characteristic){
        if (mBluetoothGatt!=null){
            return mBluetoothGatt.writeCharacteristic(characteristic);
        }
        return false;
    }


8.关闭连接

 

 

 /**
     * 关闭蓝牙或者退出应用别忘了关闭客户端哦
     */
    private void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }


大概的的流程就是这样,关于特性更多操作还需要自己去看文档。这里只是写个demo,让大家了解一下流程,下面给出全部带代码,结合步骤来看:

 

 

全部代码

 

package cn.small_qi.bluetoothtest.ble;

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.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import cn.small_qi.bluetoothtest.R;
import cn.small_qi.bluetoothtest.classic.MsgAdapter;
import cn.small_qi.bluetoothtest.classic.RvAdapter;
import cn.small_qi.bluetoothtest.gattserver.GattServerActivity;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class BleMainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "BleMainActivity";
    public static final int REQUEST_BT_ENABLE_CODE = 200;

    private BluetoothAdapter mBluetoothAdapter;//蓝牙适配器
    private MyLeScanCallback1 mLeScanCallback1;//搜索回调1
    private MyLeScanCallback2 mLeScanCallback2;//搜索回调2

    private BluetoothGatt mBluetoothGatt;//GATT客户端
    private BluetoothLeScanner mBleScanner;//BLE扫描器
    private boolean mScanning;//是否正在搜索
    private RecyclerView devicesView, msgsView;
    private RvAdapter mDeviceAdapter;
    private MsgAdapter mMessageAdapter;
    private List mServiceList;//服务列表

    private SimpleDateFormat mDateFormat;
    private boolean isCallback1=true;

    private static Handler mHandler = new Handler() {
        @Override
        public void dispatchMessage(Message msg) {
            super.dispatchMessage(msg);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ble_main);
        mDateFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
        initUI();
        initList();
    }
    private void outputLog(String msg){
        if (mMessageAdapter!=null){
            mMessageAdapter.addMessage(mDateFormat.format(System.currentTimeMillis())+"  "+msg);
        }
    }

    private void initUI() {
        findViewById(R.id.open).setOnClickListener(this);
        findViewById(R.id.close).setOnClickListener(this);
        findViewById(R.id.start).setOnClickListener(this);
        findViewById(R.id.stop).setOnClickListener(this);
        findViewById(R.id.callback_type).setOnClickListener(this);


        mLeScanCallback1 = new MyLeScanCallback1();
        mLeScanCallback2 = new MyLeScanCallback2();
    }

    private void initList() {

        mDeviceAdapter = new RvAdapter(this);
        mMessageAdapter = new MsgAdapter(this);

        devicesView = (RecyclerView) findViewById(R.id.devices);
        devicesView.setLayoutManager(new LinearLayoutManager(this));
        devicesView.setAdapter(mDeviceAdapter);

        msgsView = (RecyclerView) findViewById(R.id.msglist);
        msgsView.setLayoutManager(new LinearLayoutManager(this));
        msgsView.setAdapter(mMessageAdapter);

        mDeviceAdapter.setOnItemClickListener(new RvAdapter.OnItemClickListener() {
            @Override
            public void onClick(BluetoothDevice device) {
                connectGatt(device);
            }
        });

    }

    private void connectGatt(BluetoothDevice device) {
        outputLog("正在连接到:"+device.getName());
        //新建一个链接
        mBluetoothGatt = device.connectGatt(BleMainActivity.this, false, mBluetoothGattCallback);
        //mBluetoothGatt.connect();//如果是断开重连
        //mBluetoothGatt.disconnect();//断开当前连接
    }

    private void openBT() {
        if (mBluetoothAdapter == null) {
            /**这里和传统的蓝牙有什么区别呢?*/
            final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
            mBluetoothAdapter = bluetoothManager.getAdapter();
            //mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
            //如果调用scanBleDevices2(),请加上这句。 Call requires API level 21
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mBleScanner = mBluetoothAdapter.getBluetoothLeScanner();
            }
        }
        //1.设备不支持蓝牙,结束应用
        if (mBluetoothAdapter == null) {
            finish();
            return;
        }
        //2.判断蓝牙是否打开
        if (!mBluetoothAdapter.enable()) {
            //没打开请求打开
            Intent btEnable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(btEnable, REQUEST_BT_ENABLE_CODE);
            outputLog("正在打开蓝牙");
        }
    }

    private void scanBleDevices1(boolean enable) {
        if (enable) {
            outputLog("正在搜索设备");
            outputLog("当前搜索回调:callback 1");
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback1);
                    outputLog("停止搜索");
                }
            }, 10000);//设置10秒超时
            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback1);
        } else {
            outputLog("停止搜索");
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback1);
        }
    }

    private void scanBleDevices2(boolean enable) {
        if (mBleScanner == null) {
            outputLog("搜索器初始化失败");
            return;
        }
        if (enable) {
            outputLog("当前搜索回调:callback 2");
            outputLog("正在搜索设备");
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBleScanner.stopScan(mLeScanCallback2);
                    outputLog("停止搜索");
                }
            }, 10000);//设置10秒超时
            mScanning = true;
            mBleScanner.startScan(mLeScanCallback2);
        } else {
            mScanning = false;
            mBleScanner.stopScan(mLeScanCallback2);
            outputLog("停止搜索");
        }
    }

    /**
     * 找到自己想要获取的特性,并设置更改通知
     * 例如,服务里面提供了心率检测和血压监测,我现在只想获取心率
     */
    private void setCharacteristicNotification() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        //找到对应服务
        BluetoothGattCharacteristic characteristic = null;
        for (BluetoothGattService s : mServiceList) {
            if (s.getUuid().equals(GattServerActivity.TIME_SERVICE)) {
                //找到对应服务的特性
                List cList = s.getCharacteristics();
                for (BluetoothGattCharacteristic c : cList) {
                    if (c.getUuid().equals(GattServerActivity.CURRENT_TIME)) {
                        //找出需要通知改变的特性
                        characteristic = c;
                    }
                }
            }
        }
        if (characteristic == null) {
            return;//服务中不包含我们需要获取的特性
        }
        //启动通知:BLE应用程序通常在设备上的特定特性发生变化时要求收到通知
        //一旦为特性启用通知,如果远程设备上的特性发生变化,则触发回调onCharacteristicChanged()
        mBluetoothGatt.setCharacteristicNotification(characteristic, true);
        outputLog("开启特性变化通知成功");
        // This is specific to Heart Rate Measurement.
        //这里要对应我服务端所使用的UUID,详情请查看上一篇博客
        //更改特性描述
        if (GattServerActivity.CURRENT_TIME.equals(characteristic.getUuid())) {
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(GattServerActivity.CLIENT_CONFIG);
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
        }

        //然后读一下这个特性
        mBluetoothGatt.readCharacteristic(characteristic);//会触发回调,这里就到回调中处理
    }

    //写特性,其他操作类似,直接使用mBluetoothGatt调用对应的方法即可
    private boolean writeCharacteristic(BluetoothGattCharacteristic characteristic){
        if (mBluetoothGatt!=null){
            return mBluetoothGatt.writeCharacteristic(characteristic);
        }
        return false;
    }

    /**
     * 关闭蓝牙或者退出应用别忘了关闭客户端哦
     */
    private void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

    @Override
    protected void onDestroy() {
        close();
        super.onDestroy();
    }

    /**
     * GATT操作回调
     */
    private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
        }

        @Override
        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyRead(gatt, txPhy, rxPhy, status);
        }

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                //成功连接
                outputLog("连接蓝牙服务成功");
                mBluetoothGatt.discoverServices();//搜索服务器中的包含服务
                outputLog("搜索外围服务");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                //断开连接
                outputLog("断开蓝牙服务");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                outputLog("成功搜索到服务");
                //找到服务
                //获取服务列表
                mServiceList = gatt.getServices();
                //设置特性更改通知
                setCharacteristicNotification();
            } else {
                outputLog("服务搜索失败");
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //读取特性
                //characteristic.getValue();
                outputLog("读取到特性:"+new String(characteristic.getValue()));
            }
        }
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //反写成功
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            //服务端特性改变 读取新数据
            outputLog("接收到特性变化:"+new String(characteristic.getValue()));
            gatt.readCharacteristic(characteristic);
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //参考特性的处理
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //反写成功
            }
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
        }
    };
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.open:
                openBT();
                break;
            case R.id.close:
                if (mBluetoothAdapter!=null){
                    mBluetoothAdapter.disable();
                    close();
                }
                break;
            case R.id.start:
                if (isCallback1) {
                    scanBleDevices1(true);
                }else {
                    scanBleDevices2(true);
                }
                break;
            case R.id.stop:
                if (isCallback1) {
                    scanBleDevices1(false);
                }else {
                    scanBleDevices2(false);
                }
                break;
            case R.id.callback_type:
                isCallback1=!isCallback1;
                break;
        }
    }

    /**
     * 搜索回调1
     * 不同于传统蓝牙:这里不需要使用广播接收结果,而是使用回调
     */
    class MyLeScanCallback1 implements BluetoothAdapter.LeScanCallback {
        @Override
        public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
            mDeviceAdapter.addDevice(bluetoothDevice);
        }
    }

    /**
     * 搜索回调2
     * 不同于传统蓝牙:这里不需要使用广播接收结果,而是使用回调
     * 

* callbackType:CALLBACK_TYPE_ALL_MATCHES, CALLBACK_TYPE_FIRST_MATCH or CALLBACK_TYPE_MATCH_LOST */ class MyLeScanCallback2 extends ScanCallback { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); mDeviceAdapter.addDevice(result.getDevice()); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); } @Override public void onBatchScanResults(List results) { super.onBatchScanResults(results); //批量结果 } } }


测试结果截图:

 

非常可惜~现在暂时不能提供截图!我手上只有一台支持ble的手机做不了,带来了妹纸已经淘汰华为荣耀7,欢天喜地拿来想做测试。结果发现在荣耀7上开启服务时,其他都正常,但是无法广播出来。无法获取广播实例,坑爹的,估计是华为把这个api给修改或者去掉了。

那好,它当不了服务器,能当客户端吧~

我用我的一加3T,成功开启服务,并且广播出来。在荣耀7上开启客户端程序,什么打开蓝牙,扫描,连接都成功。已经提示“连接服务成功”了。可是迟迟不出现“搜索外围服务”,证明mBluetoothGatt.discoverServices();又没执行!我真是不知道说什么好了~不知道是不是手机问题。等我去借(坑)别人的手机来试试,再给大家截图。如果是代码有问题呢~我会直接在这篇博客中说明~

 

好了~关于蓝牙也就到这里结束,感谢您来看安卓小白小汪汪的博客~小白毕竟小白,有问题在所难免,希望各位老司机在评论给出意见或者建议~

 

----------------------------------------------------------------------------最近更新--------------------------------------------------------------------------------------------------

之前说华为荣耀7  mBluetoothGatt.discoverServices();  不执行是我错了!但是荣耀7没有广播的功能是真的!它当不了服务器!换别的品牌都可以。先来看看之前的代码:

 

@Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                //成功连接
                outputLog("连接蓝牙服务成功");
                mBluetoothGatt.discoverServices();//搜索服务器中的包含服务
                outputLog("搜索外围服务");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                //断开连接
                outputLog("断开蓝牙服务");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                outputLog("成功搜索到服务");
                //找到服务
                //获取服务列表
                mServiceList = gatt.getServices();
                //设置特性更改通知
                setCharacteristicNotification();
            } else {
                outputLog("服务搜索失败");
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

 


我的本意是连接成功之后,立马搜索外围设备提供的服务。实际证明,连接成功之后要等待几秒才能发起服务搜索,猜测可能是什么东西没初始化完成,所以发起搜索失败。1):要么你就连接成功之后,等待3秒在执行搜索操作。1):要么点击连接之后,过三秒在发起一次连接(原理一样)。3):不用全局变量发起搜索,使用回调参数中的gatt发起搜索。代码修改如下:

 

 

 

@Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                //成功连接
                outputLog("连接蓝牙服务成功");
                //修改方式1 连接成功之后3秒再发起一次连接。
                mBluetoothGatt.discoverServices();//搜索服务器中的包含服务
                //修改方式2
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mBluetoothGatt.discoverServices();
                    }
                },3000);
                //修改方式3
                gatt.discoverServices();
                outputLog("搜索外围服务");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                //断开连接
                outputLog("断开蓝牙服务");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                outputLog("成功搜索到服务");
                //找到服务
                //获取服务列表
                mServiceList = gatt.getServices();
                //设置特性更改通知
                setCharacteristicNotification();
            } else {
                outputLog("服务搜索失败");
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }


然后就可以正常进行通讯了。可能有人发现,搜索时,列表中会有很多相同的设备出现!BLE搜索是接收广播,外围设备一直推送,所以就出现几个一样的。所以大家在日常开发都要判断排除相同的设备。

 

下面附上运行截图 服务器:坚果pro(偷妹子的手机来测试了)  客户端:一加3T /荣耀7:

服务器:

Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯_第5张图片
 

客户端:

Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯_第6张图片

Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯_第7张图片

Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯_第8张图片

 

你可能感兴趣的:(Android小知识)