android ble 蓝牙4.0多机通讯客户端实现

来源:互联网 发布:oracle数据库试题 编辑:程序博客网 时间:2024/06/02 04:04

自从进入软件开发行业,都是一直在CSDN上索取无数资料,一直没有贡献过,今天突然想写点什么,第一次写,写点简单的吧,不知道有没有人看。

蓝牙从4.0开始,支持了多设备通讯,android 4.3开始支持了蓝牙4.0,即 android ble关于ble 网上资料很多,也讲的很详细。只要对官方例子简单的改造就可以实现多机通讯了。

要理解蓝牙多机通讯,其实把他和网络通讯类比就知道了,在网络通讯中,需要一台设备作为服务器,开启了监听,其他电脑才能连接上。在ble中其实就是这样的,一台先创

建服务并监听,另一台扫描并建立连接。把BluetoothGatt当做socket就可以了,因此蓝牙多机通讯其实就是一台蓝牙客户端连接多台蓝牙服务器,蓝牙客户端需要创建多个

BluetoothGatt来一一匹配每个服务端。对于android手机蓝牙服务端编程需要涉及到:BLEPeripheral(android5.0以上才支持),这个网上也是有现成的demo的。


服务端代码如下:

创建了一个数据服务(并支持读和写),有服务端才好讲客户端,还是一句话,把ble当做是网络socket通讯就对了,只不过ble服务端只能支持一个连接。

在这里没有进行数据处理。需要自己实现

@SuppressLint("NewApi")public class BLEPeripheral { Context context;  BluetoothManager mManager; BluetoothAdapter mAdapter;  BluetoothLeAdvertiser  mLeAdvertiser;  BluetoothGattServer  mGattServer;    public static boolean isEnableBluetooth(){ return BluetoothAdapter.getDefaultAdapter().isEnabled(); } public BLEPeripheral(Context context){ this.context=context; }@SuppressLint("NewApi")public int init(){    if(null == mManager)   mManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);    if(null == mManager)   return -1;    if(false == context.getPackageManager().    hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))   return -2;                      if(null == mAdapter)   mAdapter = mManager.getAdapter();    if(false == mAdapter.isMultipleAdvertisementSupported())   return -3;   return 0; }  public void close() {   } public static String getAddress(){ return BluetoothAdapter.getDefaultAdapter().getAddress(); }  @SuppressLint("NewApi") private AdvertiseCallback mAdvCallback = new AdvertiseCallback() {    @Override  public void onStartFailure(int errorCode){   Log.d("advertise","onStartFailure");  }    @Override  public void onStartSuccess(AdvertiseSettings settingsInEffect){   Log.d("advertise","onStartSuccess");  }; };   @SuppressLint("NewApi")  private final BluetoothGattServerCallback mGattServerCallback= new BluetoothGattServerCallback(){     @SuppressLint("NewApi")@Override        public void onConnectionStateChange(BluetoothDevice device, int status, int newState){  Log.d("GattServer", "Our gatt server connection state changed, new state ");  Log.d("GattServer", Integer.toString(newState));            super.onConnectionStateChange(device, status, newState);        }     @SuppressLint("NewApi")@Override        public void onServiceAdded(int status, BluetoothGattService service) {            Log.d("GattServer", "Our gatt server service was added.");            super.onServiceAdded(status, service);        }        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)@SuppressLint("NewApi")@Override        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {            Log.d("GattServer", "Our gatt characteristic was read.");            super.onCharacteristicReadRequest(device, requestId, offset, characteristic);            mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,               characteristic.getValue());        }        @SuppressLint("NewApi")@Override        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {            Log.d("GattServer", "We have received a write request for one of our hosted characteristics");            Log.d("GattServer", "data = "+ value.toString());             super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);        }        @Override        public void onNotificationSent(BluetoothDevice device, int status)        {         Log.d("GattServer", "onNotificationSent");                   super.onNotificationSent(device, status);                          }                @Override        public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {            Log.d("GattServer", "Our gatt server descriptor was read.");            super.onDescriptorReadRequest(device, requestId, offset, descriptor);                    }        @Override        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {            Log.d("GattServer", "Our gatt server descriptor was written.");            super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);        }        @Override        public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {            Log.d("GattServer", "Our gatt server on execute write.");            super.onExecuteWrite(device, requestId, execute);        }    };  public void startAdvertise() {  if(null == mAdapter)   return;    if (null == mLeAdvertiser)    mLeAdvertiser = mAdapter.getBluetoothLeAdvertiser();          if(null == mLeAdvertiser)   return;     AdvertiseSettings.Builder settingBuilder;      settingBuilder = new AdvertiseSettings.Builder();   settingBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);   settingBuilder.setConnectable(true);      settingBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);                       AdvertiseData.Builder advBuilder;                  advBuilder = new AdvertiseData.Builder();                                                   mAdapter.setName("HUD"); //8 characters works, 9+ fails         advBuilder.setIncludeDeviceName(true);                 mGattServer = mManager.openGattServer(context, mGattServerCallback);                                           //         addDeviceInfoService(mGattServer);                                    final String  DATA_SERVICE = "<span style="font-family: Arial, Helvetica, sans-serif;">00003a06-0000-1000-8000-00805f9b34fb</span>";         final String  READ = "<span style="font-family: Arial, Helvetica, sans-serif;">00002a09-0000-1000-8000-00805f9b34fb</span>";         final String  WRITE = "<span style="font-family: Arial, Helvetica, sans-serif;">00002a05-0000-1000-8000-00805f9b34fb</span>";                               BluetoothGattCharacteristic read2Characteristic = new BluetoothGattCharacteristic(           UUID.fromString(READ),            BluetoothGattCharacteristic.PROPERTY_READ,           BluetoothGattCharacteristic.PERMISSION_READ           );           //       read2Characteristic.setValue(new String("this is read 2").getBytes());                           BluetoothGattCharacteristic writeCharacteristic = new BluetoothGattCharacteristic(           UUID.fromString(WRITE),            BluetoothGattCharacteristic.PROPERTY_WRITE,           BluetoothGattCharacteristic.PERMISSION_WRITE           );                                 BluetoothGattService AService = new BluetoothGattService(           UUID.fromString(DATA_SERVICE),            BluetoothGattService.SERVICE_TYPE_PRIMARY);                                      AService.addCharacteristic(read2Characteristic);         AService.addCharacteristic(writeCharacteristic);                 // Add notify characteristic here !!!                  mGattServer.addService(AService);                            mLeAdvertiser.startAdvertising(settingBuilder.build(),            advBuilder.build(), mAdvCallback);          } public void stopAdvertise() {  if(null != mLeAdvertiser)  mLeAdvertiser.stopAdvertising(mAdvCallback);    mLeAdvertiser = null;   }}


对于客户端:


打开蓝牙扫描蓝牙就不讲了,主要讲一下BluetoothGatt:

对于ble 客户端来说一个连接就是一个BluetoothGatt通道,要实现多机通讯,客户端要时实现多个BluetoothGatt,并实现多个BluetoothGattCallback。 

连接并创建通道:mBluetoothGatt = device.connectGatt(context, false,BluetoothGattCallback);

其中device 就是扫描到的 BluetoothDevice device,这里需要实现BluetoothGattCallback接口 并选择性的实现里面的方法:

BluetoothGattCallback 中的方法1:onConnectionStateChange,蓝牙连接和丢失状态回调,在这里就可以进行服务扫描操作。

BluetoothGattCallback 中的方法2:onServicesDiscovered扫描到服务后回调,扫描到服务后通过通过UUID提取服务(需要和服务端注册的UUID一致),并设置数据接收方式等。


这里附上我实现的一个通用方法,在里面实现连接,数据发送,数据接收等一系列操作,


只要创建一个BtGattCallback 的实例就可以实现通讯了,当然多机通讯就是实现多个实例就OK了


我用一个Tag字符串来唯一标示一个连接,当创建多个连接的,可以通过不同的Tag 来区分不同的连接,只要自己事先定义好


@SuppressLint("NewApi")public class BtGattCallback extends BluetoothGattCallback {private BluetoothGatt mBluetoothGatt;Context context;String Tag;String Addr="";public static boolean isread=false;    BluetoothGattCharacteristic writeCharacteristic;    BluetoothGatt blueToothGatt;        MessageGetInterface messageGet;        BluetoothAdapter mBluetoothAdapter;        public String getTag(){    return Tag;    }    public String getAddr(){    return Addr;    }       public void discoverServices(){    if(mBluetoothGatt!=null)    mBluetoothGatt.discoverServices();    }        public boolean SendMessage(byte[] msg){    isread=false;    if(blueToothGatt!=null){    writeCharacteristic.setValue(msg);                                 if(blueToothGatt.writeCharacteristic(writeCharacteristic)){        return true;        }    }    return false;            }    /**     * Connects to the GATT server hosted on the Bluetooth LE device.     *     * @param address The device address of the destination device.     *     * @return Return true if the connection is initiated successfully. The connection result     *         is reported asynchronously through the     *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}     *         callback.     */    public boolean connect(final String address) {        if (mBluetoothAdapter == null || address == null) {                       return false;        }        Addr=address;        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);        if (device == null) {                       return false;        }        // We want to directly connect to the device, so we are setting the autoConnect        // parameter to false.        mBluetoothGatt = device.connectGatt(context, false,this);                    return true;    }    /**     * Disconnects an existing connection or cancel a pending connection. The disconnection result     * is reported asynchronously through the     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}     * callback.     */    public void disconnect() {        if (mBluetoothAdapter == null || mBluetoothGatt == null) {            return;        }        if(mBluetoothGatt!=null)        mBluetoothGatt.disconnect();    }    /**     * 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;    }public BtGattCallback(Context context,String tag,MessageGetInterface messageGet,BluetoothAdapter mBluetoothAdapter){this.mBluetoothAdapter=mBluetoothAdapter;this.messageGet=messageGet;this.context=context;Tag=tag;} private void broadcastUpdate(final String action) {        final Intent intent = new Intent(action);        intent.putExtra("tag", Tag);        context.sendBroadcast(intent); }    @Override    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {             if (newState == BluetoothProfile.STATE_CONNECTED) {                    mBluetoothGatt.discoverServices();        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {                        broadcastUpdate(BluetoothLeService.ACTION_GATT_DISCONNECTED);        }    }    @Override    public void onServicesDiscovered(final BluetoothGatt gatt, int status) {        if (status == BluetoothGatt.GATT_SUCCESS) {                                 BluetoothGattService gattService = gatt.getService(UUID.fromString(SampleGattAttributes.Data_Service));            if (gattService != null) {                writeCharacteristic = gattService.getCharacteristic(UUID.fromString(SampleGattAttributes.sendData_Characteristic));                blueToothGatt=gatt;                BluetoothGattCharacteristic notifyCharacteristic = gattService.getCharacteristic(UUID.fromString(SampleGattAttributes.getData_Characteristic));                               BluetoothGattDescriptor descriptor = notifyCharacteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));                if(notifyCharacteristic != null && descriptor != null){                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);                    gatt.setCharacteristicNotification(notifyCharacteristic, true);                    gatt.writeDescriptor(descriptor);                    broadcastUpdate(BluetoothLeService.ACTION_GATT_CONNECTED);                    return;                }            }        }        broadcastUpdate(BluetoothLeService.ACTION_GATT_DISCONNECTED);    }        @Override    public void onCharacteristicRead(BluetoothGatt gatt,                                     BluetoothGattCharacteristic characteristic,                                     int status) {        messageGet.Read(characteristic.getValue(), Tag);        }    @Override    public void onCharacteristicChanged(BluetoothGatt gatt,                                        BluetoothGattCharacteristic characteristic) {    messageGet.Read(characteristic.getValue(), Tag);    }}


一个数据接收的接口


public interface MessageGetInterface {public void Read(byte[] msg,String tag);}


UUID定义:这里定义的这些UUID,必须和服务端定义的UUID一致。


public class SampleGattAttributes {    private static HashMap<String, String> attributes = new HashMap<String, String>();    public static String sendData_Characteristic = "00002a09-0000-1000-8000-00805f9b34fb";    public static String getData_Characteristic = "00002a05-0000-1000-8000-00805f9b34fb";    public static String Data_Service ="00003a06-0000-1000-8000-00805f9b34fb";      static {        // Sample Services.        //attributes.put(sendData_Service, "Send Data Service");        attributes.put(Data_Service, "Data Service");        // Sample Characteristics.        attributes.put(sendData_Characteristic, "发送");        attributes.put(getData_Characteristic, "接收");//        attributes.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement");    }    public static String lookup(String uuid, String defaultName) {        String name = attributes.get(uuid);        return name == null ? defaultName : name;    }}


可以把服务端跑在多个手机上,然后在一个手机上同时连接多个手机,具体扫描呀,之类的在这里省略了。

我连接的不是手机服务端,而是可穿戴设备,就是一个手机连接多个可穿戴设备。对客户端来说实现都是一样的。










1 0
原创粉丝点击