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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhq1314zhq/article/details/50740586

自从进入软件开发行业,都是一直在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;
    }
}


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

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










展开阅读全文

没有更多推荐了,返回首页