安卓蓝牙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

0 0
原创粉丝点击