安卓蓝牙4.0和蓝牙多通道
来源:互联网 发布:oracle java api 编辑:程序博客网 时间:2024/06/06 10:48
安卓蓝牙4.0和蓝牙多通道
1.首先连接蓝牙的时候需要在配置文件manifest中增加权限
<uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
这个时候我们才算正式开始进入主题
2.判断你的移动设备是否支持蓝牙
如果你想声明你的应用程序只能在支持BLE的设备上运行,可以将下面声明包含进你的应用程序manifest文<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
想让你的应用程序也能够在不支持BLE的设备上运行,你就应该将上面标签中的属性设置为required=”false”。然后在运行的过程中使用PackageManager.hasSystemFeature()方法来判断设备是否支持BLE:
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){ Toast.makeText(this, “没有可提供的蓝牙设备”, Toast.LENGTH_SHORT).show(); finish();}
3.获取蓝牙适配器 BluetoothAdapter
先声明 private BluetoothAdapter mBluetoothAdapter;
然后
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();
//如果蓝牙设配器里面没有数据的话就让它返回
if(mBluetoothAdapter == null){ Toast.makeText(this, “没有可提供的蓝牙设备”, Toast.LENGTH_SHORT).show(); finish(); return;}
4.搜索蓝牙
/**
* 1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
*/
//我这里使用了需要连接多台蓝牙设备 所有放在list里面
private LinkedList mDeviceContainer = new LinkedList();
private ArrayList mDeviceList = new ArrayList();
private void scanLeDevice(final boolean enable){ if(enable){ new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(SCAN_PERIOD); if(mScanning){ //10秒钟后停止搜索 mScanning = false; //状态标记为false mBluetoothAdapter.stopLeScan(mLeScanCallback); invalidateOptionsMenu(); } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索 }else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); //停止搜索 } invalidateOptionsMenu();}
5.搜到设备的时候有一个回调接口
private BluetoothAdapter.LeScanCallback mLeScanCallback = new LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { runOnUiThread(new Runnable() { @Override public void run() { if(!mDeviceContainer.isEmpty()){ if(!isEquals(device)){ connectBle(device); } }else{ connectBle(device); } } }); } };
6.启用蓝牙模块
private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { mBluetoothLeService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { mBluetoothLeService = ((TemperatureServices.LocalBinder)service).getService(); if(!mBluetoothLeService.initialize()){ Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } Log.e(TAG, "mBluetoothLeService is okay"); }};/***接收从BlutoothLeService发送过来的广播包扣 连接 未连接 以及发送过来的数据 */private final BroadcastReceiver mGattUpdateRecevicer = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); Bundle extras = intent.getExtras(); if(TemperatureServices.ACTION_GATT_CONNECTED.equals(action)){ Log.i(TAG, "Only gatt, just wait"); }else if(TemperatureServices.ACTION_GATT_DISCONNECTED.equals(action)){ if(!mDeviceList.isEmpty()){ String strAddress = intent.getStringExtra("DEVICE_ADDRESS"); if(removeDevice(strAddress)){ int deviceNum = mDeviceList.size() - 1; numDevice.setText(deviceText+deviceNum); } } invalidateOptionsMenu(); }else if(TemperatureServices.ACTION_GATT_SERVICES_DISCOVERED.equals(action)){ if(!mDeviceContainer.isEmpty()){ String strAddress =intent.getStringExtra("DEVICE_ADDRESS"); for(BluetoothDevice bluetoothDevice:mDeviceContainer){ if(bluetoothDevice.getAddress().equals(strAddress)){ mDeviceList.add(bluetoothDevice); } } } numDevice.setText(deviceText+mDeviceList.size()); Log.e(TAG, "Discover GATT Services"); invalidateOptionsMenu(); }else if(TemperatureServices.ACTION_DATA_AVAILABLE.equals(action)){ Log.i(TAG, "ACTION_DATA_AVAILABLE"); String data = intent.getStringExtra(TemperatureServices.EXTRA_DATA_TEMP); if(extras.containsKey(TemperatureServices.EXTRA_DATA_TEMP)){ if (data != null) { if (mDataField.length() > 500) { mDataField.setText(""); } mDataField.append(data); Log.i("==temp_data==", data); // 打印温度 } }else if (extras.containsKey(TemperatureService.EXTRA_DATA)) { // 如果是蓝牙的读取通知到显示 } } }};
/** * 连接蓝牙 * @param device */ private void connectBle(BluetoothDevice device){ mDeviceContainer.add(device); while (true) { if(mBluetoothLeService!=null){ mBluetoothLeService.connect(device.getAddress(), this); break; }else{ try{ Thread.sleep(250); }catch(InterruptedException e){ e.printStackTrace(); } } } }
7.最主要的 服务
从上面代码中我们可以看到 有一个mBluetoothLeSerivice 它正是我们创建的BluetoothLeService他继承了Service。接下来这个类我详细讲解。
public class BluetoothLeService extends Service{ private static final String TAG="BluetoothLeService"; //这里是用来打印的 private String mBluetoothDeviceAddress; //蓝牙地址 private BluetoothManager mBluetoothManager; //通过BluetoothManager来获取BluetoothAdapter private BluetoothGatt mBluetoothGatt; //通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback //常用的广播 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"; //官方的demo里面有这四个 //这2个是我自己需要读取的所以增加了 public final static String EXTRA_DATA = "com.example.bluetooth.le.EXTRA_DATA"; //有数据的时候传输数据 public final static String EXTRA_DATA_TEMP = "com.example.bluetooth.le.EXTRA_DATA_TEMP"; //温度类型变量 private BluetoothAdapter mBluetoothAdapter = null; private ArrayList<BluetoothGatt> connectionQueue = new ArrayList<BluetoothGatt>(); //创建一个BluetoothGatt的队列 //相当于一个数据类型,它包括一个value和0~n个value的描述(BluetoothGattDescriptor) public BluetoothGattCharacteristic mNotifyCharacteristic; private Context mContext; //上下文 //查找服务 public void findService(BluetoothGatt gatt){ List<BluetoothGattService> gattservices = gatt.getServices(); Log.i(TAG,"Count is:"+gattservices.size()); for(BluetoothGattService gattservice : gattservices){ Log.i(TAG,gattservice.getUuid().toString()); //如果服务要等于温度服务 if(gattservice.getUuid().toString().equalsIgnoreCase(GattAttributes.HEALTH_THERMO_SERVICE)){ List<BluetoothGattCharacteristic> gattCharacteristics = gattservice.getCharacteristics(); Log.i(TAG,"Count is:"+gattCharacteristics.size()); for(BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics){ String uuidchara = gattCharacteristic.getUuid().toString(); //如果是温度计的类型的话 /** 注意:这里的GattAttributes 这个值相当于 //实际用你们自己的 public static final String HEALTH_TEMP_MEASUREMENT = "0000xxxx-0000-1000-8000-00805f9b34fb"; */ if(uuidchara.equalsIgnoreCase(GattAttributes.TEMPERATURE_TYPE)){ prepareBroadcastDataRead(gattCharacteristic); } //如果是温度计的度数的时候 else if(uuidchara.equalsIgnoreCase(GattAttributes.HEALTH_TEMP_MEASUREMENT)){ prepareBroadcastDataIndicate(gattCharacteristic); //发现服务获取设备的地址 brocastUpdate(ACTION_GATT_SERVICES_DISCOVERED, gatt.getDevice().getAddress()); } } } } } /** * 最重要的回调 */ private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt,int status,int newState){ String initAction; Log.i(TAG,"oldStatus = "+status+"NewStates"+newState); //打印出来 if(status == BluetoothGatt.GATT_SUCCESS){ //这个值一般返回的为0 //如果连接成功 if(newState == BluetoothProfile.STATE_CONNECTED){ initAction = ACTION_GATT_CONNECTED; brocastUpdate(initAction); //连接之后马上去发现服务 gatt.discoverServices(); } }else if(newState == BluetoothProfile.STATE_DISCONNECTED){ //断开连接 initAction = ACTION_GATT_DISCONNECTED; brocastUpdate(initAction, gatt.getDevice().getAddress()); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if(status == BluetoothGatt.GATT_SUCCESS){ Log.w(TAG, "onServicesDiscovered received: " + status); findService(gatt); }else{ if(gatt.getDevice().getUuids() == null){ Log.w(TAG, "onServicesDiscovered received: " + status); } } }; //这个是读取数据 @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if(status == BluetoothGatt.GATT_SUCCESS){ brocastUpdate(ACTION_DATA_AVAILABLE, characteristic); } }; //当数据发生改变的时候 @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { brocastUpdate(ACTION_DATA_AVAILABLE, characteristic); }; //写入数据 @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { System.out.println("onDescriptorWriteonDescriptorWrite = " + status + ", descriptor =" + descriptor.getUuid().toString()); } }; }; /** * 接收广播可读的特征 */ public void prepareBroadcastDataRead(BluetoothGattCharacteristic gattCharacteristic){ if((gattCharacteristic.getProperties()|BluetoothGattCharacteristic.PROPERTY_READ)>0){ readCharacteristic(gattCharacteristic); //当特征为可读的特征的时候就直接可以读取 } } /** * 接收广播通知特性 */ public void prepareBroadcastDataIndicate(BluetoothGattCharacteristic gattCharacteristic){ if((gattCharacteristic.getProperties()|BluetoothGattCharacteristic.PROPERTY_INDICATE)>0){ setCharacteristicIndication(gattCharacteristic, true); //当特征为不可读的时候哪么就是为通知 } } /** * 处理连接蓝牙后进入这里面操作 */ private final IBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder{ public TemperatureServices getService(){ return TemperatureServices.this; } } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return mBinder; } @Override public boolean onUnbind(Intent intent) { // After using a given device, you should make sure that BluetoothGatt.close() is called // such that resources are cleaned up properly. In this particular example, close() is // invoked when the UI is disconnected from the Service. close(); return super.onUnbind(intent); } /** * 对外事件处理类 * 更新广播事件 */ private void brocastUpdate(final String action){ final Intent intent = new Intent(action); sendBroadcast(intent); } /** * 传地址广播 * @param action * @param strAddress */ private void brocastUpdate(final String action,final String strAddress){ final Intent intent = new Intent(action); intent.putExtra("DEVICE_ADDRESS", strAddress); sendBroadcast(intent); } private void brocastUpdate(final String action,final BluetoothGattCharacteristic characteristic){ final Intent intent = new Intent(action); //当温度计为温度类型的时候 if(characteristic.getUuid().equals(UUIDDatabase.UUID_HEALTH_THERMOMETER_SENSOR_LOCATION)){ String a = HTMParser.getHealthThermoSensorLocation(characteristic, mContext); intent.putExtra(EXTRA_DATA, a); } else if(characteristic.getUuid().equals(UUIDDatabase.UUID_HEALTH_THERMOMETER)){ String health_temp = HTMParser.getHealthThermo(characteristic, mContext); intent.putExtra(EXTRA_DATA_TEMP, health_temp); } sendBroadcast(intent); } /** * 初始化一个参考本地蓝牙适配器 * @return */ public boolean initialize(){ if(mBluetoothManager == null){ mBluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); if(mBluetoothManager == null){ Log.e(TAG,"Unable to initialize BluetoothManager."); return false; } } mBluetoothAdapter = mBluetoothManager.getAdapter(); if(mBluetoothAdapter == null){ Log.e(TAG, "Unable to obtain a BluetoothAdapter."); return false; } return true; } /** * 连接代码 * @param address * @param context * @return */ public boolean connect(final String address,Context context){ mContext = context; if(mBluetoothAdapter == null && address == null){ Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); if(device == null){ Log.w(TAG,"Device not found. Unable to connect."); return false; } BluetoothGatt bluetoothGatt; bluetoothGatt = device.connectGatt(mContext, false, mGattCallback); if(checkGatt(bluetoothGatt)){ connectionQueue.add(bluetoothGatt); } Log.d(TAG, "Trying to create a new connection."); return true; } /** * 检查是否加入了相同的特征 如果相同就移除掉 * @param bluetoothGatt * @return */ private boolean checkGatt(BluetoothGatt bluetoothGatt){ if(!connectionQueue.isEmpty()){ for(BluetoothGatt btg:connectionQueue){ if(btg.equals(bluetoothGatt)){ return false; } } } return true; } /** * 断开蓝牙连接 一按断开是全部都断开 */ public void disConnect(){ if(mBluetoothAdapter == null && connectionQueue.isEmpty()){ Log.w(TAG, "BluetoothAdapter not initialized"); return; } for(BluetoothGatt bluetoothGatt:connectionQueue){ bluetoothGatt.disconnect(); } } public void close(){ if(connectionQueue.isEmpty()){ return; } listClose(null); } /** * 清除连接蓝牙的数据 * 我这里用的是连接多台的设备,所以清除的时候需要一个一个的移除掉 * @param gatt */ private synchronized void listClose(BluetoothGatt gatt){ if(!connectionQueue.isEmpty()){ if(gatt!=null){ for(final BluetoothGatt bluetoothGatt:connectionQueue){ if(bluetoothGatt.equals(gatt)){ bluetoothGatt.close(); new Thread(new Runnable() { @Override public void run() { try{ Thread.sleep(250); connectionQueue.remove(bluetoothGatt); }catch(Exception ex){ ex.printStackTrace(); } } }).start(); } } }else{ for(BluetoothGatt bluetoothGatt:connectionQueue){ bluetoothGatt.close(); } connectionQueue.clear(); } } } /** * 读取蓝牙数据 */ public void readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || connectionQueue.isEmpty()) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } for (BluetoothGatt bluetoothGatt : connectionQueue) { bluetoothGatt.readCharacteristic(characteristic); } } /** * Enables or disables notification on a give characteristic. * * @param characteristic Characteristic to act on. * @param enabled If true, enable notification. False otherwise. */ public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if (mBluetoothAdapter == null || connectionQueue.isEmpty()) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } for(BluetoothGatt bluetoothGatt:connectionQueue){ bluetoothGatt.setCharacteristicNotification(characteristic, enabled); } } /** * 蓝牙温度计最关键的一步 有些特征只能通过这个 读取 并启用通知更新数据 * Enables or disables indications on a give characteristic. * 启用或禁用一个给定特性的指示 * @param characteristic Characteristic to act on. * @param enabled If true, enable indications. False otherwise. */ public void setCharacteristicIndication( BluetoothGattCharacteristic characteristic, boolean enabled) { String serviceUUID = characteristic.getService().getUuid().toString(); String characteristicUUID = characteristic.getUuid().toString(); Log.i("==TAG==",serviceUUID+" "+characteristicUUID); for(BluetoothGatt mBluetoothGatt:connectionQueue){ if (mBluetoothAdapter == null || mBluetoothGatt == null) { return; } if (characteristic.getDescriptor(UUID .fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG)) != null) { if (enabled == true) { BluetoothGattDescriptor descriptor = characteristic .getDescriptor(UUID .fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); descriptor .setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } else { BluetoothGattDescriptor descriptor = characteristic .getDescriptor(UUID .fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); descriptor .setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } } mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); } }}
其中我里面用了一些外部的方法代码如下
/**
* Class used for parsing Health temperature related information
* 用于解析温度相关信息的类
*/
public class HTMParser { private static ArrayList<String> mTempInfo = new ArrayList<String>(); //Byte character format private static final String BYTE_CHAR_FORMAT = "%02X "; //Switch case Constants private static final int CASE_ARMPIT = 1; private static final int CASE_BODY = 2; private static final int CASE_EAR_LOBE = 3; private static final int CASE_FINGER = 4; private static final int CASE_GIT = 5; private static final int CASE_MOUTH = 6; private static final int CASE_RECTUM = 7; private static final int CASE_TYMPANUM = 8; private static final int CASE_TOE = 9; private static final int CASE_TOE_REP = 10; /** * Get the thermometer reading * 温度计的读数 * * @param characteristic * @return */ public static String getHealthThermo( BluetoothGattCharacteristic characteristic, Context context) { String tempUnit = ""; // 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); byte flagByte = data[0]; if ((flagByte & 0x01) != 0) { tempUnit = context.getString(R.string.tt_fahren_heit); } else { tempUnit = context.getString(R.string.tt_celcius); } for (byte byteChar : data) stringBuilder.append(String.format(BYTE_CHAR_FORMAT, byteChar)); } final float temperature = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, 1); //Logger.i("tempRate " + temperature); String ss = temperature+","+tempUnit; //mTempInfo.add(1, tempUnit); return ss; } /** * Get the thermometer sensor location * * @param characteristic * @return */ public static String getHealthThermoSensorLocation( BluetoothGattCharacteristic characteristic, Context context) { String healthTherSensorLocation = ""; 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(BYTE_CHAR_FORMAT, byteChar)); int healthBodySensor = Integer.valueOf(stringBuilder.toString() .trim()); switch (healthBodySensor) { case CASE_ARMPIT: //这个R.string.armpit 里面就是这个airpit的英文意思 healthTherSensorLocation = context.getString(R.string.armpit); break; case CASE_BODY: healthTherSensorLocation = context.getString(R.string.body); break; case CASE_EAR_LOBE: healthTherSensorLocation = context.getString(R.string.ear); break; case CASE_FINGER: healthTherSensorLocation = context.getString(R.string.finger); break; case CASE_GIT: healthTherSensorLocation = context.getString(R.string.intestine); break; case CASE_MOUTH: healthTherSensorLocation = context.getString(R.string.mouth); break; case CASE_RECTUM: healthTherSensorLocation = context.getString(R.string.rectum); break; case CASE_TYMPANUM: healthTherSensorLocation = context.getString(R.string.tympanum); break; case CASE_TOE: healthTherSensorLocation = context.getString(R.string.toe_1); break; case CASE_TOE_REP: healthTherSensorLocation = context.getString(R.string.toe_2); break; default: healthTherSensorLocation = context.getString(R.string.reserverd); break; } } return healthTherSensorLocation; }}
8.总结:
蓝牙连接最主要的是要掌握 BluetoothLeService这个类里面的内容,主要包扣BluetoothGattCallback 回调
这里面是处理蓝牙数据的核心,包扣连接设备回调 断开设备回调 发现服务回调 发现数据回调。多台连接蓝牙设备的时候 把BluetoothGatt 放到一个List里面去。断开的时候也要移次的移除掉 list里面的BluetoothGatt数据。
源码下载地址 http://download.csdn.net/detail/a1989214/9709664
- 安卓蓝牙4.0和蓝牙多通道
- android 蓝牙4.0多通道
- 安卓4.0蓝牙开发
- 安卓蓝牙4.0开发
- 安卓 手机和蓝牙通信连接
- 安卓蓝牙开发
- 安卓蓝牙通信
- 安卓蓝牙总结
- 安卓蓝牙通信
- 安卓蓝牙通信
- 安卓蓝牙通信
- 安卓蓝牙通讯
- 安卓蓝牙通信
- 安卓蓝牙开发
- 安卓蓝牙流程
- 安卓蓝牙开发
- 安卓蓝牙4.0以上连接多台设备并接收蓝牙设备数据
- 安卓蓝牙4.0BLE 通信
- java 自动装箱与拆箱
- 查询数据库库名表名字段名
- Python中元组,列表
- golang语言环境搭建
- 每天一个linux命令(14):head 命令
- 安卓蓝牙4.0和蓝牙多通道
- 文件对比工具中如何删除表格中的列表数据
- 如何让es6 写的 文件 import 起来
- 【C#】54. .Net中的并发集合——ConcurrentDictionary
- Trafodion表与HBase表的关系
- AES 之128位加密与解密
- Jmeter实现WebSocket协议的接口和性能测试方法
- Spring 整合 Redis
- 安卓TabLayout加小红点提示内容更新