【转】android蓝牙开发---与蓝牙模块进行通信--不错
原文网址:http://www.cnblogs.com/wenjiang/p/3200138.html
近半个月来一直在搞android蓝牙这方面,主要是项目需要与蓝牙模块进行通信。开头的进展很顺利,但因为蓝牙模块不在我这里,所以只能用手机测试。一开头就发现手机的蓝牙不能用,为了证明这点,我刷了四次不同不同系统的官方包,正式宣布手机的蓝牙报销了,于是和朋友换手机。在测试的过程中也是非常痛苦,放假了,同学都几乎回家了,剩下的同学中竟然80%都是用非android手机!我和我的小伙伴都吓呆了!!就算借来了手机,测试过程中老是有人打电话过来,严重影响我的开发!!于是,我果断催促对方快点把蓝牙模块寄过来,等模块寄过来后,半个小时内就搞定了!!
于是,我得到了很好的教训:请确保项目中的最关键因素是否在我们的掌握中。像是蓝牙模块这种东西,应该今早催促对方拿过来才是,而不是自己一个人在那边瞎搞。
唠叨话就先到这里,正篇正式开始。
android蓝牙这方面还是很好搞的,因为大家的方式都是差不多的。先说说如何开启蓝牙设备和设置可见时间:

- private void search() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (!adapter.isEnabled()) {
- adapter.enable();
- }
- Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
- enable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3600); //3600为蓝牙设备可见时间
- startActivity(enable);
- Intent searchIntent = new Intent(this, ComminuteActivity.class);
- startActivity(searchIntent);
- }

首先,需要获得一个BluetoothAdapter,可以通过getDefaultAdapter()获得系统默认的蓝牙适配器,当然我们也可以自己指定,但这个真心没有必要,至少我是不需要的。然后我们检查手机的蓝牙是否打开,如果没有,通过enable()方法打开。接着我们再设置手机蓝牙设备的可见,可见时间可以自定义。
完成这些必要的设置后,我们就可以正式开始与蓝牙模块进行通信了:

- public class ComminuteActivity extends Activity {
- private BluetoothReceiver receiver;
- private BluetoothAdapter bluetoothAdapter;
- private List<String> devices;
- private List<BluetoothDevice> deviceList;
- private Bluetooth client;
- private final String lockName = "BOLUTEK";
- private String message = "000001";
- private ListView listView;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.search_layout);
- listView = (ListView) this.findViewById(R.id.list);
- deviceList = new ArrayList<BluetoothDevice>();
- devices = new ArrayList<String>();
- bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- bluetoothAdapter.startDiscovery();
- IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
- receiver = new BluetoothReceiver();
- registerReceiver(receiver, filter);
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- setContentView(R.layout.connect_layout);
- BluetoothDevice device = deviceList.get(position);
- client = new Bluetooth(device, handler);
- try {
- client.connect(message);
- } catch (Exception e) {
- Log.e("TAG", e.toString());
- }
- }
- });
- }
- @Override
- protected void onDestroy() {
- unregisterReceiver(receiver);
- super.onDestroy();
- }
- private final Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case Bluetooth.CONNECT_FAILED:
- Toast.makeText(ComminuteActivity.this, "连接失败", Toast.LENGTH_LONG).show();
- try {
- client.connect(message);
- } catch (Exception e) {
- Log.e("TAG", e.toString());
- }
- break;
- case Bluetooth.CONNECT_SUCCESS:
- Toast.makeText(ComminuteActivity.this, "连接成功", Toast.LENGTH_LONG).show();
- break;
- case Bluetooth.READ_FAILED:
- Toast.makeText(ComminuteActivity.this, "读取失败", Toast.LENGTH_LONG).show();
- break;
- case Bluetooth.WRITE_FAILED:
- Toast.makeText(ComminuteActivity.this, "写入失败", Toast.LENGTH_LONG).show();
- break;
- case Bluetooth.DATA:
- Toast.makeText(ComminuteActivity.this, msg.arg1 + "", Toast.LENGTH_LONG).show();
- break;
- }
- }
- };
- private class BluetoothReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (BluetoothDevice.ACTION_FOUND.equals(action)) {
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- if (isLock(device)) {
- devices.add(device.getName());
- }
- deviceList.add(device);
- }
- showDevices();
- }
- }
- private boolean isLock(BluetoothDevice device) {
- boolean isLockName = (device.getName()).equals(lockName);
- boolean isSingleDevice = devices.indexOf(device.getName()) == -1;
- return isLockName && isSingleDevice;
- }
- private void showDevices() {
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
- devices);
- listView.setAdapter(adapter);
- }
- }

要想与任何蓝牙模块进行通信,首先得搜到该设备:

- private class BluetoothReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (BluetoothDevice.ACTION_FOUND.equals(action)) {
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- if (isLock(device)) {
- devices.add(device.getName());
- }
- deviceList.add(device);
- }
- showDevices();
- }
- }

在这之前,我们得先调用一个方法:
- bluetoothAdapter.startDiscovery();
startDiscovery()方法是一个异步方法,它会对其他蓝牙设备进行搜索,持续时间为12秒。搜索过程其实是在System Service中进行,我们可以通过cancelDiscovery()方法来停止这个搜索。在系统搜索蓝牙设备的过程中,系统可能会发送以下三个广播:ACTION_DISCOVERY_START(开始搜索),ACTION_DISCOVERY_FINISHED(搜索结束)和ACTION_FOUND(找到设备)。ACTION_FOUND这个才是我们想要的,这个Intent中包含两个extra fields:EXTRA_DEVICE和EXTRA_CLASS,包含的分别是BluetoothDevice和BluetoothClass,BluetoothDevice中的EXTRA_DEVICE就是我们搜索到的设备对象。 确认搜索到设备后,我们可以从得到的BluetoothDevice对象中获得设备的名称和地址。
在android中使用广播需要我们注册,这里也不例外:
- IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
- receiver = new BluetoothReceiver();
- registerReceiver(receiver, filter);
广播注册后需要我们撤销,这个可以放在这里进行:
- @Override
- protected void onDestroy() {
- unregisterReceiver(receiver);
- super.onDestroy();
- }
这样在Activity结束的时候就会自动撤销该广播,而不需要我们手动执行。
我这里使用一个ListView来显示搜索到的蓝牙设备,但因为需要只限定一个蓝牙设备,所以这里进行了检查,检查该设备是否是我们的目标设备,如果是,就添加。当然,为了防止重复添加,有必要增加这么一句:
- boolean isSingleDevice = devices.indexOf(device.getName()) == -1;
搜索到该设备后,我们就要对该设备进行连接。

- public void connect(final String message) {
- Thread thread = new Thread(new Runnable() {
- public void run() {
- BluetoothSocket tmp = null;
- Method method;
- try {
- method = device.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
- tmp = (BluetoothSocket) method.invoke(device, 1);
- } catch (Exception e) {
- setState(CONNECT_FAILED);
- Log.e("TAG", e.toString());
- }
- socket = tmp;
- try {
- socket.connect();
- isConnect = true;
- } catch (Exception e) {
- setState(CONNECT_FAILED);
- Log.e("TAG", e.toString());
- }

连接设备之前需要UUID,所谓的UUID,就是用来进行配对的,全称是Universally Unique Identifier,是一个128位的字符串ID,用于进行唯一标识。网上的例子,包括谷歌的例子,它们的UUID都是说能用但是我用不了的,都会报出这样的错误:
Service discovery failed
原因可能是作为唯一标识的UUID没有发挥作用,所以,我就利用反射的原理,让设备自己提供UUID。
这个错误在我们把手机既当做客户端有当做服务端的时候,同样也有可能出现,因为作为服务器的时候,我们需要的也是同一个UUID:
- mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
作为客户端是这样的:
- device.createRfcommSocketToServiceRecord(MY_UUID);
当两个UUID想同时建立Rfcomm的通道时,我们的选择都是在两个线程中分别实现,但是忽略了一件最重要的事情:同一个时间只能充当一个角色!所以,解决这个问题的方法就是在我们相连接的设备上也安装同样的应用程序,谁先发起连接谁就是客户端,但我这里是蓝牙模块啊!!怎么能安装我的应用程序呢!!解决办法就在下面的通信中。
连接设备之前还有一件事必须确保:
- bluetoothAdapter.cancelDiscovery();
这是为了停掉搜索设备,否则连接可能会变得非常慢并且容易失败。
有关于Socket的编程都需要我们设置一些状态值来标识通信的状态,以方便我们调错,而且连接应该放在一个线程中进行,要让该线程与我们程序的主线程进行通信,我们需要使用Handle,关于Handle的使用,可以参考我的另一篇博客http://www.cnblogs.com/wenjiang/p/3180324.html,这里不多讲。
在使用Socket中,我注意到一个方法:isConnect(),它返回的是布尔值,但是根本就不需要使用到这个方法,Socket的连接如果没有报错,说明是已经连接上了。
在谷歌提供的例子中,我们可以看到谷歌的程序员的程序水平很高,一些好的编码习惯我们可以学习一下,像是在try..catch中才定义的变量,我们应该在try...catch之前声明一个临时变量,然后再在try...catch后赋值给我们真正要使用的变量。这种做法的好处就是:如果我们直接就是使用真正的变量,当出现异常的时候,该变量的使用就会出现问题,而且很难进行排查,如果是临时变量,我么可以通过检查变量的值来确定是否是赋值时出错。
谷歌的例子中最大的感想就是满满的异常检查,但也是因为这个,导致它的可读性不高。java的异常处理机制有时候对于代码的阅读真的不是一件舒服的事情,能避免就尽量避免。
如果连接没有问题,我们就可以和蓝牙模块进行通信:

- if (isConnect) {
- try {
- OutputStream outStream = socket.getOutputStream();
- outStream.write(getHexBytes(message));
- } catch (IOException e) {
- setState(WRITE_FAILED);
- Log.e("TAG", e.toString());
- }
- try {
- InputStream inputStream = socket.getInputStream();
- int data;
- while (true) {
- try {
- data = inputStream.read();
- Message msg = handler.obtainMessage();
- msg.what = DATA;
- msg.arg1 = data;
- handler.sendMessage(msg);
- } catch (IOException e) {
- setState(READ_FAILED);
- Log.e("TAG", e.toString());
- break;
- }
- }
- } catch (IOException e) {
- setState(WRITE_FAILED);
- Log.e("TAG", e.toString());
- }
- }
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e) {
- Log.e("TAG", e.toString());
- }
- }
- }
- }

这里包括写入和读取,用法和基本的Socket是一样的,但是写入的时候,需要将字符串转化为16进制:

- private byte[] getHexBytes(String message) {
- int len = message.length() / 2;
- char[] chars = message.toCharArray();
- String[] hexStr = new String[len];
- byte[] bytes = new byte[len];
- for (int i = 0, j = 0; j < len; i += 2, j++) {
- hexStr[j] = "" + chars[i] + chars[i + 1];
- bytes[j] = (byte) Integer.parseInt(hexStr[j], 16);
- }
- return bytes;
- }

当然,这里只是将手机当做客户端,但是接收蓝牙模块发送过来的信息是没有必要特意创建服务端的,我们只要一个不断监听并读取对方消息的循环就行。
很简单的程序就能实现像是蓝牙串口助手的功能,由于是项目的代码,不能贴完整的代码,但是基本上都在上面了,大家可以参考一下。要想使用蓝牙,相应的权限也是必不可少的:
- <uses-permission android:name="android.permission.BLUETOOTH"/>
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
【转】android蓝牙开发---与蓝牙模块进行通信--不错的更多相关文章
- android蓝牙开发---与蓝牙模块进行通信
近半个月来一直在搞android蓝牙这方面,主要是项目需要与蓝牙模块进行通信.开头的进展很顺利,但因为蓝牙模块不在我这里,所以只能用手机测试.一开头就发现手机的蓝牙不能用,为了证明这点,我刷了四次不同 ...
- android 蓝牙开发---与蓝牙模块进行通讯 基于eclipse项目
2017.10.20 之前参加一个大三学长的创业项目,做一个智能的车锁App,用到嵌入式等技术,App需要蓝牙.实时位置等技术,故查了几篇相关技术文章,以此参考! //先说 ...
- Android网络开发之蓝牙
蓝牙采用分散式网络结构以及快调频和短包技术,支持点对点及点对多点通信,工作在全球通用的2.4GHz ISM(I-工业.S-科学.M-医学)频段,其数据速率为1Mbps,采用时分双工传输方案. 蓝牙 ...
- 【原创】cocos2d-x3.9蓝牙开发之蓝牙开启
本人第一次搞android开发,很多东西都是只知道一点点,然而都没怎么实践过,所以这次就边学边做自己想要的功能,可能会花较长时间,不过肯定是值得的,有用词或哪里说得不对的请指正. 我自己有androi ...
- iOS 蓝牙开发资料记录
一.蓝牙基础认识: 1.iOS蓝牙开发: iOS蓝牙开发:蓝牙连接和数据读写 iOS蓝牙后台运行 iOS关于app连接已配对设备的问题(ancs协议的锅) iOS蓝牙空中 ...
- Qt on Android 蓝牙开发
版权声明:本文为MULTIBEANS ORG研发跟随文章,未经MLT ORG允许不得转载. 最近做项目,需要开发安卓应用,实现串口的收发,目测CH340G在安卓手机上非常麻烦,而且驱动都是Java版本 ...
- Android 蓝牙开发(整理大全)
Android蓝牙开发 鉴于国内Android蓝牙开发的例子很少,以及蓝牙开发也比较少用到,所以找的资料不是很全. (一): 由于Android蓝牙的通信都需要用到UUID,如果由手机发起搜索,当搜索 ...
- android ble蓝牙开发略解
Android 蓝牙4.0开发 1. 权限和相关属性 “android:required="true"表示apk只有在具有bluetooth_le属性的系统里运行,这个4.3之前 ...
- 【Android应用开发】Android 蓝牙低功耗 (BLE) ( 第一篇 . 概述 . 蓝牙低功耗文档 翻译)
转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/50515359 参考 : -- 官方文档 : https://develope ...
随机推荐
- 概率dp-九度-1546-迷宫问题
题目链接: http://ac.jobdu.com/problem.php?pid=1546 题目意思: 有一个起点S,多个出口E,#代表不能走,每次等概率的随机选择下一个可以行走的位置,求从S到出口 ...
- 关于Xcode的Other Linker Flags
背景 在ios开发过程中,有时候会用到第三方的静态库(.a文件),然后导入后发现编译正常但运行时会出现selector not recognized的错误,从而导致app闪退.接着仔细阅读库文件的说明 ...
- 记录jpcap在Ubuntu&Window下的配置过程
众所周知,Java虽然在TCP/UDP传输方面给予了良好的定义,但是标准库java.net对于网络层以下的控制是无能为力的.Jpcap就是为了处理这一问题而出现的中间件.它调用底层的winpcap/l ...
- objective c的注释规范
以前写android程序,每写一个函数或者类就添加对应的注释,后来领导让生成api文档,直接使用了eclipse的到处文档功能,强大又方便,这次换作ios开发,我想肯定也有对应的注释,于是上网查找了一 ...
- UITextField控件处理键盘弹出时遮住输入框的问题
原文连接: http://www.devdiv.com/thread-70159-1-1.html 实现以下三个方法,如果弹出的键盘会遮住输入框 ,整体的界面会向上移动,这样就不会遮住输入框了.自己增 ...
- codevs1099字串变换(Bfs)
/* 最少步数问题 妥妥的Bfs 很显然队列里存的是串(可能存个数也可以 就像8数码那样) 然后每次队首元素弄出来 能换的都换一遍 最后每次换完的新串入队前先判断到头了没 最后说一句 String大法 ...
- python文档字符串
#coding=utf-8 #文档字符串def d(i,j): """这个函数实现了一个乘法运算. 函数会返回一个乘法运算的结果.""" k ...
- HDU5317
题意:定义一个数K,最小质因数形式为K = a*b*c形式(如12 = 2*2*3),相同只取一个(所以12只能取2,3两个,既F[12]=2)给L,R区间,找出区间内最大的F[x](L<=x& ...
- JavasScript基数排序
基数排序 91, 46, 85, 15, 92, 35, 31, 22经过基数排序第一次扫描之后, 数字被分配到如下盒子中:Bin 0:Bin 1: 91, 31Bin 2: 92, 22Bin ...
- .Net程序员关于微信公众平台测试账户配置 项目总结
今天项目第一次验收,夜晚吃过晚饭后,想把项目中用到的关于微信配置总结一下,虽然网上关于这方面的资料很多很多,还有官方API,但是总感觉缺点什么,就像期初做这个项目时,各方面找了很久的资料,说说配置吧! ...