Android中蓝牙通信的实现

0. 在Manifest文件中注册Service和蓝牙权限

略。

1. 创建Service的子类

public class BleService extends Service{

    public BleService() {
        mHandler = new IncomingHandler(this);
    }

    @Override
    public IBinder onBind(Intent intent) {
         return new BleBinder();
    }

    public class BleBinder extends Binder{  

        public IBinder getBinder(){
            //Messenger: reference to a Handler, which others can use to send messages to it.
            Messenger mMessenger = new Messenger(mHandler);
            //getBinder(): Retrieve the IBinder that this Messenger is using to communicate with its associated Handler.
            return mMessenger.getBinder();
        }
        public BleService getService(){  
            return BleService.this;  
        }  
    }  

    private static class IncomingHandler extends Handler {
        private final WeakReference mService;

        public IncomingHandler(BleService service) {
            mService = new WeakReference(service);
        }

        @Override
        public void handleMessage(Message msg) {
            BleService service = mService.get();
            if (service != null) {
                switch (msg.what) {
                    case MSG_START_SCAN:
                        service.startScan();
                        RunLog.i("BleService", "Start Scan");
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        }
    }
}

其中,BleBinder用于实现该Service和其它类的通信。它的getBinder()和getService()方法均可返回用于通信的对象(分别是一个Handler和一个Service)。

2.客户端代码

public static ServiceConnection bleServiceConnection = 
        new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, 
            IBinder service) {
            bleService = ((BleService.BleBinder)service).getService();  
            bleService.initiateBlueTooth();
            bleServiceMessenger = new Messenger(((BleService.BleBinder)service).getBinder());
            RunLog.i("BleServiceCommunication", "onServiceConnected");
            try {
                Message msg = Message.obtain(null, 
                    BleService.MSG_REGISTER);
                if (msg != null) {
                    msg.replyTo = activityMessenger;
                    bleServiceMessenger.send(msg);
                } else {
                    bleServiceMessenger = null;
                }
            } catch (Exception e) {
                RunLog.i("BleServiceCommunication", "Error connecting to BleService");
                bleServiceMessenger = null;
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bleServiceMessenger = null;
        }
    };

    public static void bindService(Context context){
        activityMessenger = new Messenger(new ClientHandler(context));
        Intent mServiceIntent = new Intent(context, BleService.class);
        context.bindService(mServiceIntent, bleServiceConnection, 
                Context.BIND_AUTO_CREATE);
    }

    public static class ClientHandler extends Handler {
        private final WeakReference mActivity;

        public ClientHandler(Context context) {
            mActivity = new WeakReference((Activity) context);
        }

        @Override
        public void handleMessage(Message msg) {
            Activity activity = mActivity.get();
            if (activity != null) {
                switch (msg.what) {
                    case BleService.MSG_STATE_CHANGED:
                        RunLog.i("BleServiceCommunication", "MSG_STATE_CHANGED");
                        RunLog.i("BleServiceCommunication", "arg1:" + msg.arg1);
                        if(msg.arg1 == 5){
                            if(switchOnGyro){
                                switchOnGyro();
                                switchOnGyro = false;
                            }
                            if(switchOffGyro){
                                switchOffGyro();
                                switchOffGyro = false;
                            }
                        }
                        break;
                    case BleService.MSG_DEVICE_FOUND:
                        RunLog.i("BleServiceCommunication", "MSG_DEVICE_FOUND");
                        Bundle data = msg.getData();
                        RunLog.i("BleServiceCommunication", data.getString("name"));
                        break;
                    case BleService.MSG_DEVICE_DATA:
                        RunLog.i("BleServiceCommunication", "MSG_DEVICE_DATA");
                        break;
                }
            }
            super.handleMessage(msg);
        }
    }

在客户端传入一个context,调用其bindService方法绑定Service, 并在ServiceConnection对象中指定连接成功后需要执行的操作。

3.在Service中添加用于扫描蓝牙连接的代码

enum State {
    UNKNOWN,
    IDLE,
    SCANNING,
    BLUETOOTH_OFF,
    CONNECTING,
    CONNECTED,
    DISCONNECTING
}

    public void startScan() {
        if(mState == State.CONNECTED){
            return;
        }
        if(mState == State.SCANNING){
            mBluetooth.stopLeScan(BleService.this);
            setState(State.IDLE);
        }
        setState(State.SCANNING);

        if (mBluetooth == null || !mBluetooth.isEnabled()) {
            setState(State.BLUETOOTH_OFF);
        } else {
            mBluetooth.startLeScan(this);
        }
    }

    @Override
    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
        if (device != null && device.getName() != null && mState != State.CONNECTED) {
            if(mState != State.SCANNING){
                mBluetooth.stopLeScan(BleService.this);
                return;
            }

            if (device != null
                    && device.getName() != null
                    && device.getName().equals(DEVICE_NAME)) {
                // rssi指信号强度
                if (rssi < -90) {
                    return;
                }
                setState(State.CONNECTING);
                mBluetooth.stopLeScan(BleService.this);
                RunLog.i(TAG, "connect " + device.getAddress());
                Message msg1 = new Message();
                msg1.what = 1;
                Bundle bundle1 = new Bundle();
                bundle1.putString("address", device.getAddress());
                bundle1.putParcelable("device", device);
                msg1.setData(bundle1);
                msg1.setTarget(BleService.this.uiHandler);
                msg1.sendToTarget();
            }
        }
    }

    Handler uiHandler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    BluetoothDevice mDevice = msg.getData().getParcelable("device");
                    connect(mDevice);
                    break;
            }
            super.handleMessage(msg);
        }
    };

    public void connect(BluetoothDevice mDevice) {
        RunLog.i("BleService", "Connecting");
        BluetoothDevice device = mDevice;
        RunLog.i("BleService", "mState:" + mState.name());
        if(device != null && mState != State.CONNECTED ) {
            RunLog.i("BleService", "ConnectGatt");
            mGatt = device.connectGatt(this, true, mGattCallback);
        }
    }

调用mBluetooth.startLeScan开始扫描,扫描到设备后执行onLeScan回调方法。
注意:在该方法中,使用Message将消息发送给主线程,才能用device.connectGatt执行连接。
什么是Gatt?
Generic Attribute Profile (GATT) is built on top of the Attribute Protocol (ATT) and establishes common operations and a framework for the data transported and stored by the Attribute Protocol[1].
其中, mGattCallback类需要实现的方法如下:

private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);

        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, 
            int status) {

        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, 
            BluetoothGattCharacteristic characteristic, 
            int status) {

        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, 
            BluetoothGattDescriptor descriptor, 
            int status) {

        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic) {

        }
    };

什么是BluetoothGattCharacteristic?
A GATT characteristic is a basic data element used to construct a GATT service, BluetoothGattService. The characteristic contains a value as well as additional information and optional GATT descriptors, BluetoothGattDescriptor[2].
什么是BluetoothGattDescriptor?
GATT Descriptors contain additional information and attributes of a GATT characteristic, BluetoothGattCharacteristic. They can be used to describe the characteristic’s features or to control certain behaviours of the characteristic[3].

4.通过蓝牙连接接收数据

private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

        ......

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, 
            int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                subscribe(gatt);
            }
        }
    };


private void subscribe(final BluetoothGatt gatt) {
        RunLog.i(TAG, "subscribe");
        final BluetoothGattService weCoachService = gatt
                .getService(UUID_WECOACH_SERVICE);
        if (weCoachService != null) {
            final BluetoothGattCharacteristic readData = weCoachService
                    .getCharacteristic(UUID_READ_DATA);
            final BluetoothGattCharacteristic conproConf = weCoachService
                    .getCharacteristic(UUID_CONPRO_CONF);
            final BluetoothGattCharacteristic workmodelConf = weCoachService
                    .getCharacteristic(UUID_WORKMODEL_CONF);
            final BluetoothGattCharacteristic rangeConf = weCoachService
                    .getCharacteristic(UUID_RANGE_CONF);

            int connectNum = 0;

            if (readData != null && conproConf != null) {
                final BluetoothGattDescriptor config = readData
                        .getDescriptor(UUID_CCC);
                if (config != null) {


                    int delaymissecond = 500+connectNum*100;
                    if(delaymissecond > 1000){
                        delaymissecond = 1000;
                    }
                    connectNum += 1;

                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            conproConf.setValue(CONPROMODEL);
                            write(conproConf);
                        }
                    }, delaymissecond);

                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            workmodelConf.setValue(CONFMODEL);
                            write(workmodelConf);
                        }


                    }, 100+ delaymissecond);

                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            rangeConf.setValue(RANGEMODEL);
                            write(rangeConf);
                        }


                    }, 200+ delaymissecond);

                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            gatt.setCharacteristicNotification(readData, true);
                            config.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                            write(config);
                        }

                    }, 300+ delaymissecond);
                }
            }
        }
    }

    private synchronized void write(Object o) {
        if (o instanceof BluetoothGattCharacteristic) {
            isWriting = true;
            mGatt.writeCharacteristic((BluetoothGattCharacteristic) o);
        } else if (o instanceof BluetoothGattDescriptor) {
            isWriting = true;
            mGatt.writeDescriptor((BluetoothGattDescriptor) o);
        }
    }

什么是UUID?
A universally unique identifier (UUID) is an identifier standard used in software construction. A UUID is simply a 128-bit value. The meaning of each bit is defined by any of several variants.
The intent of UUIDs is to enable distributed systems to uniquely identify information without significant central coordination[3].
setCharacteristicNotification有什么作用?
Enable or disable notifications/indications for a given characteristic. Once notifications are enabled for a characteristic, a onCharacteristicChanged(BluetoothGatt, BluetoothGattCharacteristic) callback will be triggered if the remote device indicates that the given characteristic has changed[4].

你可能感兴趣的:(android)