Android BLE浅析

这篇博客想写很久了,只是之前一直提不起劲,刚好最近还是一如既往的闲得蛋疼,那就写写吧,免得自己都忘了!   
 刚进公司的时候,做的就是BLE的项目,随着这个项目的不了了之,我也忘了这事。   
 BLE的全名是 Bluetooth Low Energy 就是低功耗蓝牙的意思,支持 API18(Android 4.3)及以上的设备,本文将说明如何通过BLE实现数据的收发

     如果你的app只是用BLE的话,在manifest.xml里加以下权限就好了: 

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

如果想让App在不支持 BLE(即API<18)的设备上也能运行,那么应该将上面权限中的required设成false。同时,加上下面的代码,
// Use this check to determine whether BLE is supported on the device. Then
// you can selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}
代码的作用就是当设备不支持BLE时,弹出Toast告诉用户添加完权限后,我们便可以敲代码了先把BLE给打开,这里涉及到BluetoothAdapter和BluetoothManger对象,一个支持BLE的android设备有且只有一个BluetoothAdapter,并通过BluetoothManger来获取,上代码
// Initializes Bluetooth adapter.
BluetoothAdapter mBluetoothAdapter;
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
然后是打开BLE:
private BluetoothAdapter mBluetoothAdapter;
...
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);//这时会弹出对话框,询问用户是否打开BLE
}
搜索周边的蓝牙设备:
/**
 * Activity for scanning and displaying available BLE devices.
 */
public class DeviceScanActivity extends ListActivity {

    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    ...
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);//mLeScanCallback是一个搜索设备的回调参数,待会贴它的代码
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);//开始搜索
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
        ...//
    }
...//这里的...是设置ListActivity的Adapter的代码,在mLeScanCallback的代码中会提到
}
LeScanCallback是设备搜索周边其他蓝牙设备时,回调的接口对象
private LeDeviceListAdapter mLeDeviceListAdapter;
...
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
        new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
            byte[] scanRecord) {
        runOnUiThread(new Runnable() {
           @Override
           public void run() {
               mLeDeviceListAdapter.addDevice(device);
               mLeDeviceListAdapter.notifyDataSetChanged();
           }
       });
   }
};
//
public class LeDeviceListAdapter extends BaseAdapter{
     
     private List<BluetoothDevice> devices;
     
     public LeDeviceListAdapter(){
          devices = new ArrayList<>();
          .....
     }

     public void addDevice(BluetoothDevice mdevice){
          if(!devices.contains(mdevice)){
           
           devices.add(mdevice);
     }

     }
.......
      @Override
      public View getView(int position ,View view ,ViewGroup viewGroup){
           BluetoothDevice bd = devices.get(position);
           String deviceName = bd.getName();//获取周边设备的名字,然后就可以在ListActivity上显示了
    
}

 
.....................//至于viewholder的代码就不写了

}

接下来就到了收发数据了
先来了解一下GATT是什么鬼,GATT是通过蓝牙收发数据的一种协议,包含Characteristic、DescriptorService三个属性值
BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。一个蓝牙4.0的终端可以
包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个Value和多个Descriptor
,一个Descriptor包含一个Value

先连GATT Server
mBluetoothGatt = device.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback mGattCallback);

BluetoothGattCallback主要是监控蓝牙的连接状态,并发出广播,下面一大段都是关于广播的处理
BluetoothGattCallback回调的实现:
// A service that interacts with the BLE device via the Android BLE API.
public class BluetoothLeService extends Service {
    private final static String TAG = BluetoothLeService.class.getSimpleName();

    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private String mBluetoothDeviceAddress;
    private BluetoothGatt mBluetoothGatt;
    private int mConnectionState = STATE_DISCONNECTED;

    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;

    public final static String ACTION_GATT_CONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
            "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE =
            "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
    public final static String EXTRA_DATA =
            "com.example.bluetooth.le.EXTRA_DATA";

    public final static UUID UUID_HEART_RATE_MEASUREMENT =
            UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

    // Various callback methods defined by the BLE API.
    private final BluetoothGattCallback mGattCallback =
            new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                Log.i(TAG, "Connected to GATT server.");
                Log.i(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                Log.i(TAG, "Disconnected from GATT server.");
                broadcastUpdate(intentAction);
            }
        }

        @Override
        // New services discovered
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        // Result of a characteristic read operation
        public void onCharacteristicRead(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic,
                int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }
     ...
    };
...
}
好长的代码,其实就是监听蓝牙的连接状态,并向系统发出广播
broadcastUpdate()的代码:
private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

private void broadcastUpdate(final String action,
                             final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);

    // This is special handling for the Heart Rate Measurement profile. Data
    // parsing is carried out as per profile specifications.
    if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        int flag = characteristic.getProperties();
        int format = -1;
        if ((flag & 0x01) != 0) {
            format = BluetoothGattCharacteristic.FORMAT_UINT16;
            Log.d(TAG, "Heart rate format UINT16.");
        } else {
            format = BluetoothGattCharacteristic.FORMAT_UINT8;
            Log.d(TAG, "Heart rate format UINT8.");
        }
        final int heartRate = characteristic.getIntValue(format, 1);
        Log.d(TAG, String.format("Received heart rate: %d", heartRate));
        intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
    } else {
        // For all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for(byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));
            intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
                    stringBuilder.toString());
        }
    }
    sendBroadcast(intent);
}
有广播发出,当然就要对广播进行处理了
// Handles various events fired by the Service.
// ACTION_GATT_CONNECTED: connected to a GATT server.
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
// ACTION_DATA_AVAILABLE: received data from the device. This can be a
// result of read or notification operations.
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
            mConnected = true;
            updateConnectionState(R.string.connected);
            invalidateOptionsMenu();//对菜单按钮的状态进行更新
        } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
            mConnected = false;
            updateConnectionState(R.string.disconnected);
            invalidateOptionsMenu();
            clearUI();
        } else if (BluetoothLeService.
                ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
            // Show all the supported services and characteristics on the
            // user interface.
            displayGattServices(mBluetoothLeService.getSupportedGattServices());
        } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
            displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
        }
    }
};

扯了一大堆,终于可以发数据了!!!官网没有介绍,只能手写了.......
<span style="font-family:Arial;">public boolean sendCharacteristic(){</span>
<span style="font-family:Arial;">
String kUUIDWriteCommand = "0000FEC7-0000-1000-8000-00805F9B34FB";//"0000FF01-0000-1000-8000-00805F9B34FB";
     
    
     UUID writeUUID = UUID.fromString(kUUIDWriteCommand);
     
     
     BluetoothGattCharacteristic btWriteGattChar = gattService.getCharacteristic(writeUUID);
     
 		
		   			
      boolean right = divideFrameBleSendData(data, btWriteGattChar);</span>
<span style="font-family:Arial;">
</span>
<span style="font-family:Arial;">      
}</span>

发送给蓝牙的数据要分包发,太长的数据一次性是发布过去的,
<span style="font-family:Arial;"></span>
private static boolean  divideFrameBleSendData(byte[] data, BluetoothGattCharacteristic btWriteGattChar){
boolean right = false;
 int tmpLen = data.length;
 int start = 0;
 int end = 0;
 while (tmpLen > 0){
 byte[] sendData = new byte[21];
 if (tmpLen >= 20){
 end += 20;
 sendData = Arrays.copyOfRange(data, start, end);
 start += 20;
 tmpLen -= 20;
 }
 else{
 end += tmpLen;
 sendData = Arrays.copyOfRange(data, start, end);
 tmpLen = 0;
 }
 
 right = btWriteGattChar.setValue(sendData);//发数据
   if (!right){
          MyUtil.writeLog("btWriteGattChar.setValue false! data=" + MyUtil.binToHex(data, 0, data.length));
  return false;
  }
 
  right = mBluetoothGatt.writeCharacteristic(btWriteGattChar);
  if (!right) return right;

 }  
 return right;
}
这样,就把数据给发过去了(参数中的data就是需要发送的数据)

然后就是收数据....












评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值