如果你还没有看上一篇 手把手教你Android手机与BLE终端通信--搜索,你就先看看吧,因为这一篇要接着讲搜索到蓝牙后的连接,和连接后的发送和接收数据。
评论里有很多人问如果一条信息特别长,怎么不丢包,或者怎么判断一个完整的信息发送完了呢。我写的时候连的串口是我们公司硬件工程师设计的,他定义好了信息的格式,什么字符开头,什么字符结尾,中间哪几位代表什么意思,我如果不能成功取到一对开头和结尾并且长度也符合我就会丢弃那点信息,取得的完整信息则会根据硬件工程师的文档取出app相应地方用到的相应信息,嗯,就是这样。如果你不知道一个串口发给你什么信息,那一定是你拿来玩的串口,工作中用到的都是定制的,不然连接串口干什么呢。
我的基本实现就是所有蓝牙操作都写在BluetoothController中,他有消息要发送时发送到BLEService中,service再发广播提示MainActivity更新页面。好了,切入正题。。
1,连接
首先点击搜索到的蓝牙的listview,连接点击的那个蓝牙:
- listview.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> arg0, View arg1, int index,
- long arg3) {
- BluetoothController.getInstance().connect(list.get(index));
- }
- });
connect方法仍然写在controller中,那个与蓝牙控制类。
- /**
- * 连接蓝牙设备
- *
- * @param device
- * 待连接的设备
- */
- public void connect(EntityDevice device) {
- deviceAddress=device.getAddress();
- deviceName=device.getName();
- BluetoothDevice localBluetoothDevice = bleAdapter
- .getRemoteDevice(device.getAddress());
- if (bleGatt != null) {
-
- bleGatt.disconnect();
- bleGatt.close();
- bleGatt = null;
- }
- bleGatt = localBluetoothDevice.connectGatt(App.app, false,
- bleGattCallback);
- }
bleGatt是与蓝牙沟通的控制类,系统自带的BluetoothGatt类,它可以连接,断开某设备,或者获取服务,写数据。蓝牙有很多服务,但我们要找那个可读写的服务,下面会有查找服务。
你应该注意到bleGattCallback,BluetoothGattCallback,也是系统自带的类,是连接回调类,连接后出现什么情况怎么处理就在这里了。它有很多方法需要重写,我们只重写两三个。关于连接我们需要重写的是onConnectionStateChange(BluetoothGatt paramAnonymousBluetoothGatt, int oldStatus,int newStatus),第一个参数不用管,我也不知道是什么,第二个参数是原来的状态,第三个参数是后来的状态,这本来就是状态改变回调方法嘛。对了,0表示未连接上,2表示已连接设备。当成功连接后我们要更新界面,未连接也要更新,因为可能是连接过程中意外中断,也可能有意中断,提醒下亲爱的用户还是比较好的。
- /**
- * 连接状态改变
- */
- public void onConnectionStateChange(
- BluetoothGatt paramAnonymousBluetoothGatt, int oldStatus,
- int newStatus) {
- if (newStatus == 2)// 已连接状态,表明连接成功
- {
- Message msg=new Message();
- msg.what=ConstantUtils.WM_BLE_CONNECTED_STATE_CHANGE;
- Bundle bundle=new Bundle();
- bundle.putString("address", deviceAddress);
- bundle.putString("name", deviceName);
- msg.obj=bundle;
- serviceHandler.sendMessage(msg);
- paramAnonymousBluetoothGatt.discoverServices();
- //连接到蓝牙后查找可以读写的服务,蓝牙有很多服务
- return;
- }
- if (newStatus == 0)// 断开连接或未连接成功
- {
- serviceHandler.sendEmptyMessage(ConstantUtils.WM_STOP_CONNECT);
- return;
- }
- paramAnonymousBluetoothGatt.disconnect();
- paramAnonymousBluetoothGatt.close();
- return;
- }
这样连接状态改变的消息就发到了service, service接收到消息后发广播提醒界面更新
- Handler handler = new Handler() {
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case ConstantUtils.WM_BLE_CONNECTED_STATE_CHANGE:// 连接上某个设备的消息
- Bundle bundle = (Bundle) msg.obj;
- String address = bundle.getString("address");
- String name = bundle.getString("name");
- // 连接状态改变广播
- Bundle bundle1 = new Bundle();
- bundle1.putString("address", address);
- bundle1.putString("name", name);
- Intent intentDevice = new Intent(
- ConstantUtils.ACTION_CONNECTED_ONE_DEVICE);
- intentDevice.putExtras(bundle1);
- sendBroadcast(intentDevice);
- break;
-
- case ConstantUtils.WM_STOP_CONNECT:
- Intent stopConnect = new Intent(
- ConstantUtils.ACTION_STOP_CONNECT);
- sendBroadcast(stopConnect);
- break;
然后主界面MainActivity接收到广播后更新页面。如果是连接就把连接的设备地址打印出来,如果是断开了,就清除打印并且弹一个toast.当然这些代码在一个receiver中。
- else if (intent.getAction().equalsIgnoreCase(ConstantUtils.ACTION_CONNECTED_ONE_DEVICE)){
- connectedDevice.setText("连接的蓝牙是:"+intent.getStringExtra("address"));
- }
-
- else if (intent.getAction().equalsIgnoreCase(ConstantUtils.ACTION_STOP_CONNECT)){
- connectedDevice.setText("");
- toast("连接已断开");
- }
为了测试断开,我关了蓝牙,你可以试试。
2,接收数据
首先你需要下载一个串口助手,可以看到串口接收到的数据,也可以通过串口发送数据到跟他连接的设备。
查看接收到的数据只需要重写上面串口回调BluetoothGattCallback的一个方法,public void onCharacteristicChanged(BluetoothGatt paramAnonymousBluetoothGatt, BluetoothGattCharacteristic paramAnonymousBluetoothGattCharacteristic)
- /**
- * 与蓝牙通信回调
- */
- public BluetoothGattCallback bleGattCallback = new BluetoothGattCallback() {
- /**
- * 收到消息
- */
- public void onCharacteristicChanged(
- BluetoothGatt paramAnonymousBluetoothGatt,
- BluetoothGattCharacteristic paramAnonymousBluetoothGattCharacteristic) {
-
- byte[] arrayOfByte = paramAnonymousBluetoothGattCharacteristic
- .getValue();
- if(BluetoothController.this.serviceHandler!=null){
- Message msg=new Message();
- msg.what=ConstantUtils.WM_RECEIVE_MSG_FROM_BLE;
- //byte数组转换为十六进制字符串
- msg.obj=ConvertUtils.getInstance().bytesToHexString(arrayOfByte);
- BluetoothController.this.serviceHandler.sendMessage(msg);
- }
- //也可以先打印出来看看
- Log.i("TEST",ConvertUtils.getInstance().bytesToHexString(arrayOfByte));
- }
接下来的操作还是一样,接受到数据发消息到service,service发广播更新到activity界面。
byteToHexString是把byte数组转化成16进制的数值的字符串。
3,发送数据
在输入框上填入要发送的数据,点按钮发送数据
- btnSend.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View arg0) {
- String str=editSend.getText().toString();
- if(str!=null&&str.length()>0){
- controller.write(str.getBytes());
- }
- else {
- toast("请填上要发送的内容");
- }
-
- }
- });
发送方法也在controller中
- /**
- * 传输数据
- *
- * @param byteArray
- * @return
- */
- public boolean write(byte byteArray[]) {
- if (bleGattCharacteristic == null)
- return false;
- if (bleGatt == null)
- return false;
- bleGattCharacteristic.setValue(byteArray);
- return bleGatt.writeCharacteristic(bleGattCharacteristic);
- }
-
- /**
- * 传输数据
- *
- * @param byteArray
- * @return
- */
- public boolean write(String str) {
- if (bleGattCharacteristic == null)
- return false;
- if (bleGatt == null)
- return false;
- bleGattCharacteristic.setValue(str);
- return bleGatt.writeCharacteristic(bleGattCharacteristic);
- }
这里又用来了一个新类,BluetoothGattCharacteristic,他封装了要发送数据,通过bleGatt发送就可以了,bleGatt管的就是连接,断开连接和发送。
最后,一定不要忘了蓝牙的服务,蓝牙有很多服务,要找到我们要的,你怎么知道要那个服务呢,把每个服务的属性都打印出来,你就发现只有一个服务的属性是可读可写的,找到它赋值给数据封装类bleGattCharacteristic就行了。
重写回调的onServicesDiscovered(BluetoothGatt paramAnonymousBluetoothGatt, int paramAnonymousInt)方法发现服务。
- public void onServicesDiscovered(
- BluetoothGatt paramAnonymousBluetoothGatt, int paramAnonymousInt) {
- BluetoothController.this.findService(paramAnonymousBluetoothGatt
- .getServices());
- }
- /**
- * 搜索服务
- *
- * @param paramList
- */
- public void findService(List<BluetoothGattService> paramList) {
-
- Iterator localIterator1 = paramList.iterator();
- while (localIterator1.hasNext()) {
- BluetoothGattService localBluetoothGattService = (BluetoothGattService) localIterator1
- .next();
- if (localBluetoothGattService.getUuid().toString()
- .equalsIgnoreCase(ConstantUtils.UUID_SERVER)) {
- List localList = localBluetoothGattService.getCharacteristics();
- Iterator localIterator2 = localList.iterator();
- while (localIterator2.hasNext()) {
- BluetoothGattCharacteristic localBluetoothGattCharacteristic = (BluetoothGattCharacteristic) localIterator2
- .next();
- if (localBluetoothGattCharacteristic.getUuid().toString()
- .equalsIgnoreCase(ConstantUtils.UUID_NOTIFY)) {
- bleGattCharacteristic = localBluetoothGattCharacteristic;
- break;
- }
- }
- break;
- }
-
- }
-
- bleGatt.setCharacteristicNotification(bleGattCharacteristic, true);
- }
服务号:
- public final static String UUID_SERVER="0000ffe0-0000-1000-8000-00805f9b34fb";
- public final static String UUID_NOTIFY="0000ffe1-0000-1000-8000-00805f9b34fb";
到哪儿都一样。
如果你看到这儿了,恭喜你,下面都是必备干货:
代码就是这样,包括上次的搜索都在下面的连接里,里面有.apk文件,你先跑跑看效果,还有串口助手exe文件,还有es里的代码,还有串口怎样使用,怎样配置,我真是太贴心了。
http://pan.baidu.com/s/1geCKYJL
(不要忘了在manifest中加一个权限,为了兼容6.0以上手机:
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
)
对了,前几篇文章怎么没人拍砖呢,让我看看哪里不对了好改呀,实在找不到不对的,怎么也没人说句好听的呢?总之,怎么一点互动都没有呢,唉。