android 手机与单片机之间的蓝牙通信
来源:互联网 发布:淘宝hd不能用了吗 编辑:程序博客网 时间:2024/04/30 09:34
刚好碰到这蓝牙通信方面的项目,上网也看过其他人在蓝牙这方面写的博客,但大多都不全,给一些初触蓝牙的开发者造成一定的麻烦,自己当初也费了不少劲。所以把自己参考网上的一些资料用Android studio写的代码完全放出来,分享一下。菜鸟初写博客,若有不恰之处,请指出,必改正。下面我会把自己的思路和代码一一呈现。(PS:由于后期做了些逻辑操作,代码可能有点臃肿,请勿怪。还好完整的代码是会有的,里面有多出来的一两个类没有用的,但懒得重新打包了)
第一篇博客写完,感觉好菜。资源下载:http://download.csdn.net/detail/u013168302/9146907
1.添加权限
<uses-permission android:name="android.permission.BLUETOOTH"/><uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
2.成员变量跟onCreate()方法,为了方便阅读,把这些摆出来
public class PipelineActivity extends Activity { public static BluetoothSocket btSocket; private BluetoothAdapter bluetoothAdapter; private ArrayAdapter<String> deviceAdapter; private List<String> listDevices; private ListView listView; private LinearLayout btContent; private TextView btAllData; private Button openBT; private Button searchBT; final private static int MESSAGE_READ = 100; int i = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pipeline); listView = (ListView) this.findViewById(R.id.list); btContent = (LinearLayout) findViewById(R.id.bt_content_llt); btAllData = (TextView) findViewById(R.id.all_data); btAllData.setText(btAllData.getText(), TextView.BufferType.EDITABLE);//这行可实现TextView尾部追加http://blog.csdn.net/u013168302/article/details/48785927 openBT = (Button) findViewById(R.id.open_btn); searchBT = (Button) findViewById(R.id.search_btn); listDevices = new ArrayList<String>(); bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter.isEnabled()) { openBT.setText("关闭蓝牙"); } deviceAdapter = new ArrayAdapter<String>(getApplicationContext(), R.layout.list_item, listDevices); openBT.setOnClickListener(new BTListener()); searchBT.setOnClickListener(new BTListener()); listView.setAdapter(deviceAdapter); listView.setOnItemClickListener(new ItemClickListener());//添加监听 }
3.注册广播(因为下面搜索时要用到,所以现在前面注册)
private BroadcastReceiver receiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); //下面几行是为了在logcat里面看到搜索到的设备细节,需要的话,可以将注释打开// Bundle b = intent.getExtras();// Object[] lstName = b.keySet().toArray();// // 显示所有收到的消息及其细节// for (int i = 0; i < lstName.length; i++) {// String keyName = lstName[i].toString();// Log.e("-----" + keyName, String.valueOf(b.get(keyName)));// } //搜索设备时,取得设备的MAC地址 if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); String str = device.getName() + "|" + device.getAddress(); if (listDevices.indexOf(str) == -1)// 防止重复添加 listDevices.add(str); // 获取设备名称和mac地址 if (deviceAdapter != null) { deviceAdapter.notifyDataSetChanged(); } } } };
4.点击开启蓝牙,搜索蓝牙设备。 将搜索到设备名称和mac地址通过BroadcastReceiver保存到list集合,再在listview中展示
/** * 蓝牙开启与搜索按钮点击监听 */ class BTListener implements View.OnClickListener { @Override public void onClick(View view) { if (view.getId() == R.id.open_btn) { if (!bluetoothAdapter.isEnabled()) { bluetoothAdapter.enable();//开启蓝牙 Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); enable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); //300秒为蓝牙设备可见时间 startActivity(enable); openBT.setText("关闭蓝牙"); } else { bluetoothAdapter.disable();//关闭蓝牙 openBT.setText("开启蓝牙"); if (btSocket != null) { try { btSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } else if (view.getId() == R.id.search_btn) { if (!bluetoothAdapter.isEnabled()) { Toast.makeText(getApplicationContext(), "请先开启蓝牙", Toast.LENGTH_SHORT).show(); } else { btContent.setVisibility(View.GONE); listView.setVisibility(View.VISIBLE); if (listDevices != null) { listDevices.clear(); if (deviceAdapter != null) { deviceAdapter.notifyDataSetChanged(); } } bluetoothAdapter.startDiscovery(); IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(receiver, filter); } } } }
5.点击listview的item,通过反射连接设备。连接设备之前需要UUID来配对。查看网上的例子和自己的一些实践发现,通过UUID会出现一些问题,当然也有可能只是我自己的代码有问题。所以在此采取反射来获取蓝牙socket对象。
/** * 蓝牙选项,listview列表点击监听 */ class ItemClickListener implements AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { if (!bluetoothAdapter.isEnabled()) { Toast.makeText(getApplicationContext(), "请先开启蓝牙", Toast.LENGTH_SHORT).show(); } else { bluetoothAdapter.cancelDiscovery();//停止搜索 String str = listDevices.get(position); String macAdress = str.split("\\|")[1]; BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAdress); try { Method clientMethod = device.getClass() .getMethod("createRfcommSocket", new Class[]{int.class}); btSocket = (BluetoothSocket) clientMethod.invoke(device, 1); connect(btSocket);//连接设备 } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } }
在这里值得注意while里面的2行代码。这2行代码花了不少功夫才弄到的
(1)if(inputStream.available() >0==false)
(2)Thread.sleep(400);//等待0.4秒,作用:让数据接收完整
这2行用于处理inputStream读取数据不完整或混乱的问题,但感觉还是不够好。若谁有更好的方法,欢迎提出。
另外inputStream在这里是关不掉的,因为这是蓝牙通信,单片机每产生的数据都要传输到手机端。试过多次:一旦断开inputstream,while循环结束,单片机再次产生的数据无法传送。不知道我有没有弄错,欢迎指出。
/** * 连接蓝牙及获取数据 */ public void connect(final BluetoothSocket btSocket) { try { btSocket.connect();//连接 if (btSocket.isConnected()) { Log.e("----connect--- :", "连接成功"); Toast.makeText(getApplicationContext(), "蓝牙连接成功", Toast.LENGTH_SHORT).show(); listView.setVisibility(View.GONE); btContent.setVisibility(View.VISIBLE); new ConnetThread().start();//通信 } else { Toast.makeText(getApplicationContext(), "蓝牙连接失败", Toast.LENGTH_SHORT).show(); btSocket.close(); listView.setVisibility(View.VISIBLE); btContent.setVisibility(View.GONE); Log.e("--------- :", "连接关闭"); } } catch (IOException e) { e.printStackTrace(); } } /** * 蓝牙通信管理 */ private class ConnetThread extends Thread { public void run() { try { InputStream inputStream = btSocket.getInputStream(); byte[] data = new byte[1024]; int len = 0; String result = ""; while (len != -1) { if (inputStream.available() > 0 == false) {//inputStream接收的数据是一段段的,如果不先 continue; } else { try { Thread.sleep(500);//等待0.5秒,让数据接收完整 len = inputStream.read(data); result = URLDecoder.decode(new String(data, "utf-8"));// Log.e("----result:----- :", ">>>" + result); Message msg = new Message(); msg.what = MESSAGE_READ; msg.obj = result; handler.sendMessage(msg); } catch (InterruptedException e) { e.printStackTrace(); } } } inputStream.close();//关不了,也好像不能关 Log.e("--------- :", "关闭inputStream"); if (btSocket != null) { btSocket.close(); } } catch (IOException e) { Log.e("TAG", e.toString()); } } }
7.用Handler处理Message
private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_READ: String result = (String) msg.obj; String data = result.split("\\r\\n")[0]; Log.e("----data:----- :", ">>>" + data); if (i < 6) { Editable text = (Editable) btAllData.getText(); text.append(data); btAllData.setText(text + "\r\n"); i++; } else { btAllData.setText(data + "\r\n"); i = 0; } break; } } }; @Override protected void onDestroy() { unregisterReceiver(receiver); super.onDestroy(); }
8.1布局文件(尚未优化)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#000000" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:background="#000000" android:gravity="center" android:paddingLeft="20dp" android:text="@string/pipeline" android:textColor="#ffffff" android:textSize="20sp" /> <LinearLayout android:visibility="gone" android:id="@+id/bt_content_llt" android:layout_width="match_parent" android:layout_height="1dp" android:layout_weight="1" android:background="#ffffff" android:orientation="vertical"> <LinearLayout style="@style/bt_llt_style" android:layout_weight="3"> <TextView style="@style/btTV_style" android:gravity="left" android:layout_weight="1" android:paddingTop="20dp" android:text="@string/all_data" /> <TextView android:id="@+id/all_data" style="@style/btTV_style" android:gravity="left" android:paddingLeft="2dp" android:paddingTop="20dp" android:textSize="16sp" android:layout_weight="2" /> </LinearLayout> </LinearLayout> <ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="1dp" android:layout_weight="1" android:background="#f0f0f0" android:divider="#c0c0c0" android:dividerHeight="1dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp" android:layout_gravity="center" android:layout_marginTop="10dp" android:orientation="horizontal"> <Button android:id="@+id/open_btn" style="@style/bt_button_style" android:text="@string/open_bluetooth" /> <Button android:id="@+id/search_btn" style="@style/bt_button_style" android:text="@string/search_bluetooth" /> </LinearLayout></LinearLayout>
8.2 item布局
<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/device_name_tv" android:layout_width="match_parent" android:layout_height="50dp" android:paddingLeft="15dp" android:gravity="center|left" android:textColor="#000000" android:background="@drawable/list_item_selector" android:textSize="18sp"></TextView>
8.3 资源文件strings.xml
<resources> <string name="app_name">bluetooth</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string name="pipeline">管道检测仪</string> <string name="open_bluetooth">开启蓝牙</string> <string name="close_bluetooth">关闭蓝牙</string> <string name="search_bluetooth">搜索蓝牙</string> <string name="all_data">全部数据:</string> <color name="item_default">#F0F0F0</color> <color name="item_press">#A5D8F5</color></resources>
8.4 style
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> </style> <style name="btTV_style"> <item name="android:background">#ffffff</item> <item name="android:gravity">left|center</item> <item name="android:paddingLeft">10dp</item> <item name="android:paddingRight">10dp</item> <item name="android:textSize">20sp</item> <item name="android:layout_width">1dp</item> <item name="android:layout_height">match_parent</item> </style> <style name="bt_llt_style"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">1dp</item> <item name="android:layout_weight">1</item> <item name="android:orientation">horizontal</item> </style> <style name="bt_button_style"> <item name="android:layout_width">1dp</item> <item name="android:layout_height">match_parent</item> <item name="android:layout_marginLeft">20dp</item> <item name="android:layout_marginRight">20dp</item> <item name="android:layout_weight">1</item> <item name="android:background">#5CB85C</item> </style></resources>
8.5 listview的item选择器
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 选中 --> <item android:state_pressed="true" android:drawable="@color/item_press" /> <!-- 默认 --> <item android:drawable="@color/item_default"/></selector>
3 0
- android 手机与单片机之间的蓝牙通信
- android开发之手机与单片机蓝牙模块通信
- Android手机蓝牙与单片机通信数据格式问题
- android开发之手机与单片机蓝牙模块通信
- android开发之手机与单片机蓝牙模块通信
- android手机与蓝牙模块的通信
- Android与蓝牙Ble之间的通信
- Android手机之间的蓝牙通信的代码和原理
- android:手机之间蓝牙通信(一)
- android:手机之间蓝牙通信(二)
- android:手机之间蓝牙通信(三)
- android:手机之间蓝牙通信(四)
- 手机与单片机通过蓝牙通信----手机控制灯
- STC89C52单片机通过HC-06蓝牙模块与Android手机通信
- android物联网初步,利用手机蓝牙与单片机通信,实现led灯开关和定时
- 实现蓝牙HC-05、06与单片机的连接及与手机通信(转)
- 安卓手机通过蓝牙与单片机通信
- 安卓手机通过蓝牙与单片机通信
- Mybatis系列(一)入门
- SqlBulkCopy 使用案例
- 使用数据库定义资源、角色和权限
- Windows系统Mongodb安装教程
- Hive日期格式转换用法
- android 手机与单片机之间的蓝牙通信
- NetFlix电影推荐算法(获奖那个)
- (转)那两年炼就的Android内功修养
- Java开发熟手该当心的11个错误
- java中公共类object的方法总结
- 二叉搜索树节点删除 java实现
- APNS开源包的内存泄露问题
- win7的cdc驱动安装问题
- 把软件盘的回车键设置发送按钮