android实现手机通过蓝牙连接使用socket与芯片进行数据交换

来源:互联网 发布:淘宝转化率 编辑:程序博客网 时间:2024/05/16 08:18

1.权限的申请

<!--经典蓝牙一样,应用使用蓝牙,需要声明BLUETOOTH权限,如果需要扫描设备或者操作蓝牙设置,则还需要BLUETOOTH_ADMIN权限--><uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
如果设备支持LE蓝牙模式,注明:只有Android手机4.0,并且系统是4.3版本以上才支持LE蓝牙模式
<uses-feature    android:name="android.hardware.bluetooth_le"    android:required="true" />

要注明一点,如果是android手机与手机之间的socket通信则需要另外添加一条权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

2.蓝牙连接通常都需要有一个service去实时实现与服务器端进行数据交互

a、b都是在Activity中的:

a.初始化并且判断设备是否支持BL技术

/** * 是否支持BLE蓝牙 */private void bltIsSupport() {    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {        Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();        finish();    }
获取蓝牙管理器和蓝牙适配器
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to // BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);    
mBluetoothAdapter = bluetoothManager.getAdapter();    // Checks if Bluetooth is supported on the device.    
if (mBluetoothAdapter == null) {        
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
        return;    
}
}
/** * 检查蓝牙是否开启 */public void checkBleDevice(Context context) {    if (mBluetoothAdapter != null) {        //蓝牙若没开启便让它开启        if (!mBluetoothAdapter.isEnabled()) {            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);            enableBtIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            context.startActivity(enableBtIntent);        }    } else {        Log.i("blueTooth", "该手机不支持蓝牙");    }}

b.在确认蓝牙开启以后,需要开始扫描周边蓝牙设备

/**     * 开始扫描,如果已经再扫描种便不会再进行     *     * @param enable 传入boolen值来决定扫描还是停止     */    private void scanLeDevice(final boolean enable) {        if (enable) {            // Stops scanning after a pre-defined scan period.            mHandler.postDelayed(new Runnable() {                @Override                public void run() {                    if (mScanning) {                        mScanning = false;                        mBluetoothAdapter.stopLeScan(mLeScanCallback);//                        invalidateOptionsMenu();                    }                }            }, SCAN_PERIOD);            mScanning = true;            //F000E0FF-0451-4000-B000-000000000000            mLeDevices.clear();            mHandler.sendEmptyMessage(1);            mBluetoothAdapter.startLeScan(mLeScanCallback);        } else {            mScanning = false;            mBluetoothAdapter.stopLeScan(mLeScanCallback);        }        if (!connectResult) {            mBluetoothLeService.connect(mDeviceAddress);        }    }
这里面主要逻辑就是,延迟10秒发送停止扫描命令,然后清空之前扫描的开始扫描,这里要注意的是mLeScanCallback,这是系统中
BluetoothAdapter中的一个回调函数,扫描命令都会默认执行该函数。
/** * 蓝牙扫描设备回调函数 */private BluetoothAdapter.LeScanCallback mLeScanCallback =        new BluetoothAdapter.LeScanCallback() {            @Override            public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        mLeDevices.add(device);                        mDeviceName = device.getName();                        mDeviceAddress = device.getAddress();                        mHandler.sendEmptyMessage(1);                        //如果找到对应地址的蓝牙,就绑定后台蓝牙服务,并注册各种广播接受者                        if (mDeviceName.equals("EC1601010002775533")) {                            Intent gattServiceIntent = new Intent(MainActivity.this, BluetoothLeService.class);                            Log.d(TAG, "Try to bindService=" + bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE));                            registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());                            if (mBluetoothLeService != null) {                                connectResult = mBluetoothLeService.connect(mDeviceAddress);                                Log.d(TAG, "Connect request result=" + connectResult);                            }                        }                    }                });            }        };
当扫描到可匹配的蓝牙设备时,就会执行该函数,该函数可以获取到蓝牙设备的具体信息,通常获取名字和MAC地址,如果希望指定某一个
蓝牙连接的话就可以加判断语句。当认为找到合适的蓝牙连接的话,就需要用到一个服务和一个广播接收者,接下来我先介绍这个服务里
面的内容。

Service中:

先自定义以下几个广播:
连接
public final static String ACTION_GATT_CONNECTED =        "com.example.xy005_8.feizhuchu.ACTION_GATT_CONNECTED";
无连接public final static String ACTION_GATT_DISCONNECTED =        "com.example.xy005_8.feizhuchu.ACTION_GATT_DISCONNECTED";
已发现蓝牙设备public final static String ACTION_GATT_SERVICES_DISCOVERED =        "com.example.xy005_8.feizhuchu.ACTION_GATT_SERVICES_DISCOVERED";
从服务器端接收到数据public final static String ACTION_DATA_AVAILABLE =        "com.example.xy005_8.feizhuchu.ACTION_DATA_AVAILABLE";
其他数据
public final static String EXTRA_DATA =        "com.example.xy005_8.feizhuchu.EXTRA_DATA";
用于与蓝牙特征识别码种的UUID比较
public final static UUID UUID_NOTIFY =        UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb");
用于与蓝牙服务种的UUID比较public final static UUID UUID_SERVICE =    UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb");
关于服务与activity的绑定,我就简单解释,贴代码,原理就是去继承Binder这个实现类,获取到IBinder对象,并返回给acitivity,

由activity种的onreceiver方法去接收到BluetoothLeService这个对象,方便调用服务种的方法

public class LocalBinder extends Binder {    public BluetoothLeService getService() {        return BluetoothLeService.this;    }}@Overridepublic IBinder onBind(Intent intent) {    return mBinder;}@Overridepublic 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 final IBinder mBinder = new LocalBinder();

c.初始化适配器对象和GATT回调函数的介绍

获取蓝牙管理器,并且获取到适配器对象mBluetoothAdapter ,用于查找到设备要执行connect方法时候使用
public boolean initialize() {    // For API level 18 and above, get a reference to BluetoothAdapter through    // BluetoothManager.    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;}

这是很关键的一个回调函数
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
当连接状态发生改变的时候就会执行该方法,先是判断是否GATT连接成功,当连接成功时,是的话发出一个连接成功的广播,
失败则发出连接失败的广播,并且关闭GATT
    @Override    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {        String intentAction;        Log.i(TAG, "oldStatus=" + status + " NewStates=" + newState);        if(status == BluetoothGatt.GATT_SUCCESS)        {                if (newState == BluetoothProfile.STATE_CONNECTED) {            intentAction = ACTION_GATT_CONNECTED;                        broadcastUpdate(intentAction);            Log.i(TAG, "Connected to GATT server.");            // Attempts to discover services after successful connection.            Log.i(TAG, "Attempting to start service discovery:" +                  mBluetoothGatt.discoverServices());        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {            intentAction = ACTION_GATT_DISCONNECTED;            mBluetoothGatt.close();            mBluetoothGatt = null;            Log.i(TAG, "Disconnected from GATT server.");            broadcastUpdate(intentAction);        }       }    }当蓝牙被匹配的时候执行该方法,这里要区分以下在前面定义的ACTION_GATT_SERVICES_DISCOVERED,这里是一旦发先有匹配的
就去判断该蓝牙的特征识别码的UUID是否与设备一致,一致则会发出ACTION_GATT_SERVICES_DISCOVERED。    @Override    public void onServicesDiscovered(BluetoothGatt gatt, int status) {        if (status == BluetoothGatt.GATT_SUCCESS) {           Log.w(TAG, "onServicesDiscovered received: " + status);           findService(gatt.getServices());        } else {           if(mBluetoothGatt.getDevice().getUuids() == null)            Log.w(TAG, "onServicesDiscovered received: " + status);        }    }特征识别码一旦被服务器端读取成功,就会发送数据送达广播,该特征识别码会携带数据    @Override    public void onCharacteristicRead(BluetoothGatt gatt,                                     BluetoothGattCharacteristic characteristic,                                     int status) {        if (status == BluetoothGatt.GATT_SUCCESS) {            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);        }    }一旦特征识别码改变也会发送该广播    @Override    public void onCharacteristicChanged(BluetoothGatt gatt,                                        BluetoothGattCharacteristic characteristic) {        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);        Log.e(TAG, "OnCharacteristicWrite");    }
还有其余的一些需要重写的方法就不一一列举了

d.findService方法和broadcastUpdate方法

用于判断特征识别码是否匹配
 public void findService(List<BluetoothGattService> gattServices) {   Log.i(TAG, "Count is:" + gattServices.size());   for (BluetoothGattService gattService : gattServices)   {      Log.i(TAG, gattService.getUuid().toString());Log.i(TAG, UUID_SERVICE.toString());      if(gattService.getUuid().toString().equalsIgnoreCase(UUID_SERVICE.toString()))      {         List<BluetoothGattCharacteristic> gattCharacteristics =                 gattService.getCharacteristics();         Log.i(TAG, "Count is:" + gattCharacteristics.size());         for (BluetoothGattCharacteristic gattCharacteristic :                 gattCharacteristics)          {            if(gattCharacteristic.getUuid().toString().equalsIgnoreCase(UUID_NOTIFY.toString()))            {               Log.i(TAG, gattCharacteristic.getUuid().toString());               Log.i(TAG, UUID_NOTIFY.toString());               mNotifyCharacteristic = gattCharacteristic;               setCharacteristicNotification(gattCharacteristic, true);               broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);               return;            }         }      }   } }
用于客户端连接时广播状态的改变
public void broadcastUpdate(final String action) {    final Intent intent = new Intent(action);    sendBroadcast(intent);}
主要用于服务器返回特征识别码的时候,将数据读取出来以广播的形式发送到activity中
private void broadcastUpdate(final String action,                             final BluetoothGattCharacteristic characteristic) {    final Intent intent = new Intent(action);    final byte[] data = characteristic.getValue();    if (data != null && data.length > 0) {            intent.putExtra(EXTRA_DATA, new String(data));    }    sendBroadcast(intent);}

Activity中的广播接收者和服务:

用于服务的连接
private final ServiceConnection mServiceConnection = new ServiceConnection() {    @Override    public void onServiceConnected(ComponentName componentName, IBinder service) {        mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();        if (!mBluetoothLeService.initialize()) {            Log.e(TAG, "Unable to initialize Bluetooth");        }        Log.e(TAG, "mBluetoothLeService is okay");        // Automatically connects to the device upon successful start-up initialization.        mBluetoothLeService.connect(mDeviceAddress);    }    @Override    public void onServiceDisconnected(ComponentName componentName) {        mBluetoothLeService = null;    }};
注册自己定义的几个广播
private static IntentFilter makeGattUpdateIntentFilter() {                        //注册接收的事件    final IntentFilter intentFilter = new IntentFilter();    intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);    intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);    intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);    intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);    intentFilter.addAction(BluetoothDevice.ACTION_UUID);    return intentFilter;}
到此,就可以接收到服务器发送来的消息了,其中WriteValue方法实际上是调用
mBluetoothGatt.writeCharacteristic(mNotifyCharacteristic);即GATT中发送的特征识别码的方法。
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)) {  //连接成功            Log.e(TAG, "Only gatt, just wait");        } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { //断开连接            mConnected = false;        } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) //已找到可以连接的蓝牙        {            mConnected = true;   //判断是否连接的boolean值,没有的话执行connect函数            Utils.showToast("连接成功,现在可以进行通信了", MainActivity.this);            Log.e(TAG, "In what we need");            mBluetoothLeService.WriteValue(Utils.addReturnStr("RBL=1"));        } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { //收到数据            Log.e(TAG, "RECV DATA");            data = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);//特征码数据            if (data == null) {                return;            }            Log.e(TAG, data);            if (data.equals(Utils.addReturnStr("TBL=1"))) {                mBluetoothLeService.WriteValue(Utils.addReturnStr("SBL=00006789"));                Log.e(TAG, "SBL=00006789 is send");            }         }    }};
顺带贴下连接的方法,实质就是获取到MAC地址,根据地址获取到设备,然后取执行系统的连接方法,执行时有执行GATT回调函数
 public boolean connect(final String address) {        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;        }        // We want to directly connect to the device, so we are setting the autoConnect        // parameter to false.        if(mBluetoothGatt != null)        {           mBluetoothGatt.close();            mBluetoothGatt = null;        }        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);        //mBluetoothGatt.connect();             Log.d(TAG, "Trying to create a new connection.");        return true;    }

结语

总算是搞定了,第一次写博客,算是记录以下这段时间的心得,有不对之处望大家斧正。

阅读全文
0 0