Android 蓝牙 BR/EDR 的关于串口通信的学习
2015-03-31 00:22
246 查看
周末又是偷懒 打了两把DOTA2,想写的系列还没有动笔。这两天狠下功夫把蓝牙研究了个明白,因为同学有需求,他的小车上要用到。搞懂了自然就记下来,网上有用的太少了,做个小整理,免得再出问题。
首先呢,这篇只对BR/EDR类型的蓝牙进行讨论,即普通蓝牙。对于4.0,即BLE以后再说。大致结构如下:
那么开始吧!!
第一步:加入权限,并且检查设备是否支持蓝牙
清单中需加入的两个权限
第二步:打开蓝牙和关闭蓝牙
这个嘛,用两个按钮来显示就好。打开蓝牙的话,我用的这种方法,会提示你要不要打开蓝牙(我觉得这样好些)
第三步:查找设备并进行配对
这里要用到listView来表示存放查找到的设备,涉及到listView的知识这里不做解释。可以查阅相关资料。
首先先注册广播,注册好之后就可以查找了
发现了自然就将device添加到list里去(我在MainActivity里使用了get方法来获得这个list对象)
接下来,就是一个ListViewAdapter来存放设备名和设备地址
接着,我在主类里注册了一个新的广播,用来监听BondState,这样可以根据配对的状态,动态改变按钮上的文字。未配对就显示配对,配对过的就显示连接。嘿嘿嘿,这边和上边的广播一样的。不多解释,上代码
第四步:连接设备
这里用到了一个UUID码,用来辨识设备提供的UUID服务的。当然了,我们这边只针对蓝牙串口这一种情况讨论,别的UUID码可以上网找哦~
String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";这个呢,就是串口蓝牙服务的UUID
提示:因为这里我使用的是蓝牙串口,所有只用到了客户端。如果是两个手机蓝牙之间之类的话,还要有服务端哦~~
第五步:发送数据
这边一开始不是太理解,下面就是我自己的理解。非术语,可能还是有点形象的。首先,你要有个输出流。这个输出流给传给小车,就像履带一样,然后你往输出流里写东西,就像往履带上放东西。这样就可以把东西传过去了(原谅我以前没接触过流之类的,一开始搞得真的晕晕的)
源代码链接(百度云网盘):http://pan.baidu.com/s/1dD6dPvV
首先呢,这篇只对BR/EDR类型的蓝牙进行讨论,即普通蓝牙。对于4.0,即BLE以后再说。大致结构如下:
那么开始吧!!
第一步:加入权限,并且检查设备是否支持蓝牙
清单中需加入的两个权限
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { Toast.makeText(MainActivity.this, "该设备不支持蓝牙", Toast.LENGTH_SHORT) .show(); finish(); }调用BluetoothAdapter类(用来管理bluetooth的)getDefaultAdapter可以得到本机蓝牙。加个判定,如果本机不支持蓝牙设备的话
第二步:打开蓝牙和关闭蓝牙
这个嘛,用两个按钮来显示就好。打开蓝牙的话,我用的这种方法,会提示你要不要打开蓝牙(我觉得这样好些)
if (!bluetoothAdapter.isEnabled()) { Intent openBluetoothIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE); openBluetoothIntent.putExtra( BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120); startActivityForResult(openBluetoothIntent, REQUEST_OPEN_BLUETOOTH);稍稍分析一下,isEnable方法用来判断蓝牙是否打开。Action当然是请求打开了。另外的Extra_discoverable_duration表示可被发现的时间长。记得定义一个int型的request_code
private static final int REQUEST_OPEN_BLUETOOTH = 1;关闭的就简单多了,一句话搞定
bluetoothAdapter.disable();
第三步:查找设备并进行配对
这里要用到listView来表示存放查找到的设备,涉及到listView的知识这里不做解释。可以查阅相关资料。
首先先注册广播,注册好之后就可以查找了
IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); filter.addAction(BluetoothDevice.ACTION_FOUND); registerReceiver(broadcastReceiver, filter);
<pre name="code" class="java"><span style="white-space:pre"> </span>bluetoothAdapter.startDiscovery();
<span style="white-space:pre"> </span>openSearchDialog();这边openSearchDialog方法是我用来查找的时候,显示一个等待的提示框
private void openSearchDialog() { dialog = new AlertDialog.Builder(MainActivity.this).create(); dialog.show(); dialog.setContentView(R.layout.search_dialog); dialog.setCancelable(false); }下面的就是自定义的BroadcastReceiver类
public class BlueToothBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { case BluetoothDevice.ACTION_FOUND: BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); ((MainActivity) context).getDeviceItems().add(device); ((MainActivity) context).haveFoundDevice(); break; case BluetoothAdapter.ACTION_DISCOVERY_FINISHED: context.unregisterReceiver(this); ((MainActivity) context).closeSearchDialog(); break; default: break; } } }两个行为,找到设备时以及搜索终了时都会受到广播。
发现了自然就将device添加到list里去(我在MainActivity里使用了get方法来获得这个list对象)
private List<BluetoothDevice> deviceItems;另外的haveFoundDevice方法是用来判定我的另一个线程进行的,涉及到异步进程的知识。这边也不作讨论。直接上代码块。
public void haveFoundDevice() { mThread = new NewThread(); mThread.start(); }启动线程
class NewThread extends Thread implements Runnable { @Override public void run() { super.run(); try { adapter = new BlueToothDeviceAdapter(MainActivity.this, R.layout.listview_bluetooth_devices, deviceItems); Message message = new Message(); message.what = UPDATA_LISTVIEW_UI; mHandler.sendMessage(message); } catch (Exception e) { // TODO: handle exception } } }内部类
mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case UPDATA_LISTVIEW_UI: listView.setAdapter(adapter); break; case START_CONNECT_DEVICE: btnCarUp.setVisibility(View.VISIBLE); break; default: break; } } };Handler的处理
接下来,就是一个ListViewAdapter来存放设备名和设备地址
</pre>先上代码,再细细说。<pre name="code" class="java">public class BlueToothDeviceAdapter extends ArrayAdapter<BluetoothDevice> { private Context mContext; private int resourceId; private BluetoothDevice device; private ViewHolder holder; public BlueToothDeviceAdapter(Context context, int resource, List<BluetoothDevice> devices) { super(context, resource, devices); this.mContext = context; this.resourceId = resource; } @SuppressLint("NewApi") @Override public View getView(int position, View convertView, ViewGroup parent) { device = getItem(position); View view; if (convertView == null) { view = LayoutInflater.from(mContext).inflate(resourceId, null); holder = new ViewHolder(); holder.deviceName = (TextView) view.findViewById(R.id.device_name); holder.deviceAddress = (TextView) view .findViewById(R.id.device_address); holder.btnConnect = (Button) view.findViewById(R.id.btn_listView); holder.isPair = (TextView) view.findViewById(R.id.textView_isPair); view.setTag(holder); } else { view = convertView; holder = (ViewHolder) view.getTag(); } if (Integer.toHexString(device.getBluetoothClass().getDeviceClass()) .length() < 4) { Log.i("device", "0" + Integer.toHexString(device.getBluetoothClass() .getDeviceClass())); } else { Log.i("device", Integer.toHexString(device.getBluetoothClass() .getDeviceClass())); } holder.deviceName.setText(device.getName()); holder.deviceAddress.setText(device.getAddress()); switch (device.getBondState()) { case BluetoothDevice.BOND_BONDED: holder.isPair.setText("已配对"); holder.btnConnect.setText("连接"); break; case BluetoothDevice.BOND_NONE: holder.isPair.setText("未配对"); break; case BluetoothDevice.BOND_BONDING: holder.isPair.setText("配对中"); break; default: break; } holder.btnConnect.setTag(position); holder.btnConnect.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { BluetoothDevice device = getItem((int) v.getTag()); switch (device.getBondState()) { case BluetoothDevice.BOND_NONE: try { Method createBondMethod = BluetoothDevice.class .getMethod("createBond"); createBondMethod.invoke(device); } catch (Exception e) { Toast.makeText(mContext, "配对失败", Toast.LENGTH_SHORT) .show(); } break; case BluetoothDevice.BOND_BONDED: ((MainActivity) mContext).startConnectThread((int)v.getTag()); break; default: break; } } }); return view; } class ViewHolder { TextView deviceName; TextView deviceAddress; Button btnConnect; TextView isPair; } }关于ListViewAdapter的我就不说了,之后更新的文章会详细讲解。这里,运用device的getName方法,getAddress方法可以分别得到查找到的设备的昵称和MAC地址。并且,我加入了一个判定。判断当前设备的配对状态,未配对的话,按下配对按钮就可以配对
case BluetoothDevice.BOND_NONE: try { Method createBondMethod = BluetoothDevice.class .getMethod("createBond"); createBondMethod.invoke(device); } catch (Exception e) { Toast.makeText(mContext, "配对失败", Toast.LENGTH_SHORT) .show(); } break;这边用到了反射机制来进行配对
接着,我在主类里注册了一个新的广播,用来监听BondState,这样可以根据配对的状态,动态改变按钮上的文字。未配对就显示配对,配对过的就显示连接。嘿嘿嘿,这边和上边的广播一样的。不多解释,上代码
IntentFilter filter2 = new IntentFilter(); filter2.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); registerReceiver(listenBroadcastReceiver, filter2);
public class ListenStateBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { ((MainActivity) context).getAdapter().notifyDataSetChanged(); } } }
第四步:连接设备
这里用到了一个UUID码,用来辨识设备提供的UUID服务的。当然了,我们这边只针对蓝牙串口这一种情况讨论,别的UUID码可以上网找哦~
String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";这个呢,就是串口蓝牙服务的UUID
public void run() { super.run(); final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB"; UUID uuid = UUID.fromString(SPP_UUID); try { BluetoothSocket socket = deviceItems.get(position) .createRfcommSocketToServiceRecord(uuid); socket.connect(); mmOutputStream = socket.getOutputStream(); Message message = new Message(); message.what = START_CONNECT_DEVICE; mHandler.sendMessage(message); } catch (Exception e) { } }这边说明一下,socket是一个像插座一样的东西,蓝牙之间连接传数据就要靠它。通过指定的UUID来创建Rfcomm协议,这样得到了一个socket实例。接着,就可以连接了呢!!用connect方法连接。还有一个注意点就是,因为这是会阻塞的,所以也要用子线程来写!
提示:因为这里我使用的是蓝牙串口,所有只用到了客户端。如果是两个手机蓝牙之间之类的话,还要有服务端哦~~
第五步:发送数据
这边一开始不是太理解,下面就是我自己的理解。非术语,可能还是有点形象的。首先,你要有个输出流。这个输出流给传给小车,就像履带一样,然后你往输出流里写东西,就像往履带上放东西。这样就可以把东西传过去了(原谅我以前没接触过流之类的,一开始搞得真的晕晕的)
mmOutputStream = socket.getOutputStream();这样我们可以得到一个输出流履带~~~
private void writeByteMessage(String msg) { byte[] buffer = new byte[1]; try { if (mmOutputStream == null) { Toast.makeText(MainActivity.this, "输出流为空", Toast.LENGTH_SHORT) .show(); return; } buffer = msg.getBytes(); mmOutputStream.write(buffer); } catch (Exception e) { // TODO: handle exception } finally { try { } catch (Exception e2) { // TODO: handle exception } } }这个方法就是往履带上放东西的~~~记得调用。比如,我这边是发送"W"让小车向前,我可以这么写
case R.id.btn_car_up: writeByteMessage("W"); break;到这里就结束了,这样就简单地运用了串口蓝牙通信。之后会补上更有趣的UI,做成更好玩的东西。
源代码链接(百度云网盘):http://pan.baidu.com/s/1dD6dPvV
相关文章推荐
- 关于android蓝牙通信
- Android BLE学习(二): Android与51822蓝牙模块通信流程的实现与分析
- 关于利用android-serialport-api实现在安卓设备上进行串口通信,附精简版demo,亲测可用。
- android 关于蓝牙间通信,及RFID设备读写芯片问题的处理方案
- Android BLE学习(二): Android与51822蓝牙模块通信流程的实现与分析
- 关于蓝牙通信文档 Android建立蓝牙RFCOMM通信
- Android Service和Activity基于串口蓝牙模块的双向通信
- Android Service和Activity基于串口蓝牙模块的双向通信
- android蓝牙和串口模块的通信
- 一些关于Android蓝牙的学习链接
- 关于android串口通信
- 关于android蓝牙通信的问题
- 关于android Volley网络通信框架的学习
- 关于蓝牙通信的学习资料整理
- android手机通过串口蓝牙透传模块与AVR单片机通信实例。。。蓝牙服务程序案例
- Android 串口蓝牙通信开发Java版本
- 关于自己的游戏《小鱼吃苹果》 J2ME和android代码提供给大家学习
- [Silverlight学习笔记]关于利用WCF RIA Service进行通信并在客户端获取数据
- Android用户界面设计——关于布局的学习笔记
- 关于Android资源学习