自从进入软件开发行业,都是一直在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;
}
}
可以把服务端跑在多个手机上,然后在一个手机上同时连接多个手机,具体扫描呀,之类的在这里省略了。
我连接的不是手机服务端,而是可穿戴设备,就是一个手机连接多个可穿戴设备。对客户端来说实现都是一样的。