android蓝牙和网络通信项目总结(一)
最近在家没网络,用手机开热点做了一个蓝牙和网络通信的小项目,在这里总结一下。主要是和硬件通信的一个app啦,从同学那里拿个蓝牙模块做的测试,觉得挺有趣的,整个项目做下来,因为感觉有很多东西需要总结的,所以分几篇进行总结。
一,扫描二维码
这个app的其中一个功能是扫描二维码,然后获得设备信息,添加设备,这样以后app就可以直接连接设备了,对于二维码的操作,我使用的是比较出名的zxing框架。在github上很容易找到可以使用的例子。首先我是先和做硬件的同学商量好怎么定二维码信息的格式的,商量好就好说了,打开myeclipse,写个生成二维码的工具,然后生成二维码。然后使用手机扫描二维码,获得数据,保存到手机。主要代码如下:
Intent intent = new Intent();
intent.putExtra("result", rawResult.getText());
setResult(RESULT_OK, intent);
rw = rawResult.getText().split("&@");
SmartDevice smartDevice = new SmartDevice();
smartDevice.setName(rw[0]);
smartDevice.setPassword(rw[1]);
smartDevice.setPath(rw[2]);
for (int i = 3; i < rw.length-1; i+=2) {
SmartOrder order = new SmartOrder();
order.setSmartOrder(rw[i]);
order.setSmartRespense(rw[i+1]);
order.save();
smartDevice.getSmartOrders().add(order);
}
smartDevice.save();
Intent i = new Intent(Config.notif2adddevice);
i.putExtra("result", rawResult.getText());
mLocalBroadcastManager.sendBroadcast(i);
finish();
smartdevice就是要保存的设备的类,包括名字,密码,发送的指令,响应的指令,设备蓝牙物理地址等,这是根据信息特定的排列存入的,以避免随便扫一个二维码就加一个设备。在最后发送一个broadcast,通知上一个activity添加设备,然后退出扫描,设备添加成功。
mLocalBroadcastManager = LocalBroadcastManager.getInstance(getActivity());
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Config.notif2adddevice)) {
int drawable = 0;
SmartDevice smartDevice = DataSupport.findLast(SmartDevice.class, true);
devices.add(smartDevice);
if (smartDevice.getName().contains("灯")) {
drawable = R.drawable.light_on;
} else if (smartDevice.getName().contains("门")) {
drawable = R.drawable.door_on;
} else if (smartDevice.getName().contains("路由器")) {
drawable = R.drawable.luyouqi_on;
} else {
drawable = R.drawable.switch_on;
}
smartDevice.setDrawable(drawable);
smartDevice.update(DataSupport.count(SmartDevice.class));
datas.add(datas.size() - 1, new CellData(smartDevice.getName(), drawable));
adapter.notifyDataSetChanged();
}
}
};
mLocalBroadcastManager.registerReceiver(mReceiver, filter);
上面是接受broadcast的主要代码,我主要想在所有设备的最后加一个添加设备的按钮,所以每次添加设备都添加在倒数第二个,最后一个是添加按钮。
二,关于读取信息
关于读取信息是挺麻烦的事情,上次下载的例子是使用thread的,但是要实现异步,还是用asynctask。还要区分蓝牙连接,和网络连接,开始想到的一个方法是,分别写一个读取,到时后来发现要很多冗余的代码,所以把他写成一个,主要代码如下:
public class ReadMesThread extends AsyncTask {
private InputStream mmInStream;
int bytes;
boolean isReading = true;
public ReadMesThread(InputStream mmInStream){
this.mmInStream = mmInStream;
}
@Override
protected Object doInBackground(Object[] objects) {
while (!isCancelled()) {
byte[] buffer = new byte[256];
if (mmInStream != null) {
try {
bytes = mmInStream.read(buffer);
String readStr = new String(buffer, 0, bytes);
String str = bytes2HexString(buffer).replaceAll("00", "").trim();
Log.i("result", str);
} catch (IOException e) {
e.printStackTrace();
Log.i("result", "standbyfail");
return null;
}
}
}
return null;
}
private String bytes2HexString(byte[] b) {
String ret = "";
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
ret += hex.toUpperCase();
}
return ret;
}
}
一个线程控制两个信息读取,非常方便。在最后的一个方法是将字节转换成字符用的,因为发送到设备的数据必须是十六进制的,所以必须要转换。但上面的代码其实是不够严谨的,为什么呢?假设现在是使用网络连接,读到的数据有可能会出现StringIndexOutOfBoundsException这个异常,这里必须捕捉,并处理异常,代码要改成这样:
try {
bytes = mmInStream.read(buffer);
String readStr = new String(buffer, 0, bytes);
String str = bytes2HexString(buffer).replaceAll("00", "").trim();
Log.i("result", str);
} catch (IOException e) {
handler.obtainMessage(Config.TIMEOUT).sendToTarget();
e.printStackTrace();
Log.i("result", "standbyfail");
return null;
}catch (StringIndexOutOfBoundsException siob){
handler.obtainMessage(Config.SIOOBE).sendToTarget();
}
使用handler在线程之间的通信,处理异常。
在读取信息的过程中,我还遇到一个很常见的错误,就是在一个activity中要是有一个执行长时操作的asynctask,activity被销毁的时候,那个asynctask是不会被销毁的,以至于下次再打开那个activity的时候,所有asynctask都跑不起来了,asyntask的生命周期被改变了,所以activity退出的时候一定要cancel掉asyntask,并且置空,例如:
if(connectThread!=null) {
connectThread.cancel(true);
connectThread = null;
}
这种错误编译器是不报错的,非常隐秘,所以要切记!