热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

AndroidBLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信

AndroidBLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信前面的终究只是小知识点,上不了台面,也只能算是

Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信




前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主从关系的客户端和服务端了,本文相关链接需要去google的API上查看,需要翻墙的


Bluetooth Low Energy:http://developer.android.com/guide/topics/connectivity/bluetooth-le.html


但是我们依然没有讲到BLE(低功耗蓝牙),放心,下一篇就回讲到,跟前面的基本上很大的不同,我们今天来看下客户端和服务端的实现

我们以上篇为栗子:
Android BLE与终端通信(二)——Android Bluetooth基础搜索蓝牙设备显示列表



一.蓝牙数据传输


蓝牙数据传输其实跟我们的 Socket(套接字)有点类似,如果有不懂的,可以百度一下概念,我们只要知道是这么回事就可以了,在网络中使用Socket和ServerSocket控制客户端和服务端来数据读写。而蓝牙通讯也是由客户端和服务端来完成的,蓝牙客户端Socket是BluetoothSocket,蓝牙服务端Socket是BluetoothServerSocket,这两个类都在android.bluetooth包下,而且无论是BluetoothSocket还是BluetoothServerSocket,我们都需要一个UUID(标识符),这个UUID在上篇也是有提到,而且他的格式也是固定的:



UUID:XXXXXXXX(8)-XXXX(4)-XXXX(4)-XXXX(4)-XXXXXXXXXXXX(12)


第一段是8位,中间三段式4位,最后一段是12位,UUID相当于Socket的端口,而蓝牙地址则相当于Socket的IP



1.activity_main.xml

<LinearLayout xmlns:android&#61;"http://schemas.android.com/apk/res/android"xmlns:tools&#61;"http://schemas.android.com/tools"android:layout_width&#61;"match_parent"android:layout_height&#61;"match_parent"android:orientation&#61;"vertical" ><Button
android:layout_width&#61;"match_parent"android:layout_height&#61;"wrap_content"android:onClick&#61;"btnSearch"android:text&#61;"搜索蓝牙设备" />
<ListView
android:id&#61;"&#64;&#43;id/lvDevices"android:layout_width&#61;"match_parent"android:layout_height&#61;"0dp"android:layout_weight&#61;"1" />
LinearLayout>

2.实现步骤


1.声明

我们需要的东西

// 本地蓝牙适配器private BluetoothAdapter mBluetoothAdapter;// 列表private ListView lvDevices;// 存储搜索到的蓝牙private List bluetoothDevices &#61; new ArrayList();// listview的adapterprivate ArrayAdapter arrayAdapter;// UUID.randomUUID()随机获取UUIDprivate final UUID MY_UUID &#61; UUID.fromString("db764ac8-4b08-7f25-aafe-59d03c27bae3");// 连接对象的名称private final String NAME &#61; "LGL";// 这里本身即是服务端也是客户端&#xff0c;需要如下类private BluetoothSocket clientSocket;private BluetoothDevice device;// 输出流_客户端需要往服务端输出private OutputStream os;

2.初始化

// 获取本地蓝牙适配器mBluetoothAdapter &#61; BluetoothAdapter.getDefaultAdapter();// 判断手机是否支持蓝牙if (mBluetoothAdapter &#61;&#61; null) {Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();finish();}// 判断是否打开蓝牙if (!mBluetoothAdapter.isEnabled()) {// 弹出对话框提示用户是后打开Intent intent &#61; new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(intent, 1);// 不做提示&#xff0c;强行打开// mBluetoothAdapter.enable();}// 初始化listviewlvDevices &#61; (ListView) findViewById(R.id.lvDevices);lvDevices.setOnItemClickListener(this);// 获取已经配对的设备Set pairedDevices &#61; mBluetoothAdapter.getBondedDevices();// 判断是否有配对过的设备if (pairedDevices.size() > 0) {for (BluetoothDevice device : pairedDevices) {// 遍历到列表中bluetoothDevices.add(device.getName() &#43; ":"&#43; device.getAddress() &#43; "\n");}}// adapterarrayAdapter &#61; new ArrayAdapter(this,android.R.layout.simple_list_item_1, android.R.id.text1,bluetoothDevices);lvDevices.setAdapter(arrayAdapter);/*** 异步搜索蓝牙设备——广播接收*/// 找到设备的广播IntentFilter filter &#61; new IntentFilter(BluetoothDevice.ACTION_FOUND);// 注册广播registerReceiver(receiver, filter);// 搜索完成的广播filter &#61; new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);// 注册广播registerReceiver(receiver, filter);}

3.点击搜索

public void btnSearch(View v) {// 设置进度条setProgressBarIndeterminateVisibility(true);setTitle("正在搜索...");// 判断是否在搜索,如果在搜索&#xff0c;就取消搜索if (mBluetoothAdapter.isDiscovering()) {mBluetoothAdapter.cancelDiscovery();}// 开始搜索mBluetoothAdapter.startDiscovery();}

4.搜索设备

private final BroadcastReceiver receiver &#61; new BroadcastReceiver() {&#64;Overridepublic void onReceive(Context context, Intent intent) {// 收到的广播类型String action &#61; intent.getAction();// 发现设备的广播if (BluetoothDevice.ACTION_FOUND.equals(action)) {// 从intent中获取设备BluetoothDevice device &#61; intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);// 判断是否配对过if (device.getBondState() !&#61; BluetoothDevice.BOND_BONDED) {// 添加到列表bluetoothDevices.add(device.getName() &#43; ":"&#43; device.getAddress() &#43; "\n");arrayAdapter.notifyDataSetChanged();}// 搜索完成} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {// 关闭进度条setProgressBarIndeterminateVisibility(true);setTitle("搜索完成&#xff01;");}}};

5.客户端实现已经发送数据流

// 客户端&#64;Overridepublic void onItemClick(AdapterView parent, View view, int position,long id) {// 先获得蓝牙的地址和设备名String s &#61; arrayAdapter.getItem(position);// 单独解析地址String address &#61; s.substring(s.indexOf(":") &#43; 1).trim();// 主动连接蓝牙try {// 判断是否在搜索,如果在搜索&#xff0c;就取消搜索if (mBluetoothAdapter.isDiscovering()) {mBluetoothAdapter.cancelDiscovery();}try {// 判断是否可以获得if (device &#61;&#61; null) {// 获得远程设备device &#61; mBluetoothAdapter.getRemoteDevice(address);}// 开始连接if (clientSocket &#61;&#61; null) {clientSocket &#61; device.createRfcommSocketToServiceRecord(MY_UUID);// 连接clientSocket.connect();// 获得输出流os &#61; clientSocket.getOutputStream();}} catch (Exception e) {// TODO: handle exception}// 如果成功获得输出流if (os !&#61; null) {os.write("Hello Bluetooth!".getBytes("utf-8"));}} catch (Exception e) {// TODO: handle exception}}

6.Handler服务

// 服务端&#xff0c;需要监听客户端的线程类private Handler handler &#61; new Handler() {public void handleMessage(android.os.Message msg) {Toast.makeText(MainActivity.this, String.valueOf(msg.obj),Toast.LENGTH_SHORT).show();super.handleMessage(msg);}};

7.服务端读取数据流

// 线程服务类private class AcceptThread extends Thread {private BluetoothServerSocket serverSocket;private BluetoothSocket socket;// 输入 输出流private OutputStream os;private InputStream is;public AcceptThread() {try {serverSocket &#61; mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}&#64;Overridepublic void run() {// 截获客户端的蓝牙消息try {socket &#61; serverSocket.accept(); // 如果阻塞了&#xff0c;就会一直停留在这里is &#61; socket.getInputStream();os &#61; socket.getOutputStream();// 不断接收请求,如果客户端没有发送的话还是会阻塞while (true) {// 每次只发送128个字节byte[] buffer &#61; new byte[128];// 读取int count &#61; is.read();// 如果读取到了&#xff0c;我们就发送刚才的那个ToastMessage msg &#61; new Message();msg.obj &#61; new String(buffer, 0, count, "utf-8");handler.sendMessage(msg);}} catch (Exception e) {// TODO: handle exception}}}

8.开启服务

首先要声明

//启动服务ac &#61; new AcceptThread();ac.start();

MainActivity完整代码

package com.lgl.bluetoothget;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;public class MainActivity extends Activity implements OnItemClickListener {// 本地蓝牙适配器private BluetoothAdapter mBluetoothAdapter;// 列表private ListView lvDevices;// 存储搜索到的蓝牙private List bluetoothDevices &#61; new ArrayList();// listview的adapterprivate ArrayAdapter arrayAdapter;// UUID.randomUUID()随机获取UUIDprivate final UUID MY_UUID &#61; UUID.fromString("db764ac8-4b08-7f25-aafe-59d03c27bae3");// 连接对象的名称private final String NAME &#61; "LGL";// 这里本身即是服务端也是客户端&#xff0c;需要如下类private BluetoothSocket clientSocket;private BluetoothDevice device;// 输出流_客户端需要往服务端输出private OutputStream os;//线程类的实例private AcceptThread ac;&#64;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}private void initView() {// 获取本地蓝牙适配器mBluetoothAdapter &#61; BluetoothAdapter.getDefaultAdapter();// 判断手机是否支持蓝牙if (mBluetoothAdapter &#61;&#61; null) {Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();finish();}// 判断是否打开蓝牙if (!mBluetoothAdapter.isEnabled()) {// 弹出对话框提示用户是后打开Intent intent &#61; new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(intent, 1);// 不做提示&#xff0c;强行打开// mBluetoothAdapter.enable();}// 初始化listviewlvDevices &#61; (ListView) findViewById(R.id.lvDevices);lvDevices.setOnItemClickListener(this);// 获取已经配对的设备Set pairedDevices &#61; mBluetoothAdapter.getBondedDevices();// 判断是否有配对过的设备if (pairedDevices.size() > 0) {for (BluetoothDevice device : pairedDevices) {// 遍历到列表中bluetoothDevices.add(device.getName() &#43; ":"&#43; device.getAddress() &#43; "\n");}}// adapterarrayAdapter &#61; new ArrayAdapter(this,android.R.layout.simple_list_item_1, android.R.id.text1,bluetoothDevices);lvDevices.setAdapter(arrayAdapter);//启动服务ac &#61; new AcceptThread();ac.start();/*** 异步搜索蓝牙设备——广播接收*/// 找到设备的广播IntentFilter filter &#61; new IntentFilter(BluetoothDevice.ACTION_FOUND);// 注册广播registerReceiver(receiver, filter);// 搜索完成的广播filter &#61; new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);// 注册广播registerReceiver(receiver, filter);}public void btnSearch(View v) {// 设置进度条setProgressBarIndeterminateVisibility(true);setTitle("正在搜索...");// 判断是否在搜索,如果在搜索&#xff0c;就取消搜索if (mBluetoothAdapter.isDiscovering()) {mBluetoothAdapter.cancelDiscovery();}// 开始搜索mBluetoothAdapter.startDiscovery();}// 广播接收器private final BroadcastReceiver receiver &#61; new BroadcastReceiver() {&#64;Overridepublic void onReceive(Context context, Intent intent) {// 收到的广播类型String action &#61; intent.getAction();// 发现设备的广播if (BluetoothDevice.ACTION_FOUND.equals(action)) {// 从intent中获取设备BluetoothDevice device &#61; intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);// 判断是否配对过if (device.getBondState() !&#61; BluetoothDevice.BOND_BONDED) {// 添加到列表bluetoothDevices.add(device.getName() &#43; ":"&#43; device.getAddress() &#43; "\n");arrayAdapter.notifyDataSetChanged();}// 搜索完成} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {// 关闭进度条setProgressBarIndeterminateVisibility(true);setTitle("搜索完成&#xff01;");}}};// 客户端&#64;Overridepublic void onItemClick(AdapterView parent, View view, int position,long id) {// 先获得蓝牙的地址和设备名String s &#61; arrayAdapter.getItem(position);// 单独解析地址String address &#61; s.substring(s.indexOf(":") &#43; 1).trim();// 主动连接蓝牙try {// 判断是否在搜索,如果在搜索&#xff0c;就取消搜索if (mBluetoothAdapter.isDiscovering()) {mBluetoothAdapter.cancelDiscovery();}try {// 判断是否可以获得if (device &#61;&#61; null) {// 获得远程设备device &#61; mBluetoothAdapter.getRemoteDevice(address);}// 开始连接if (clientSocket &#61;&#61; null) {clientSocket &#61; device.createRfcommSocketToServiceRecord(MY_UUID);// 连接clientSocket.connect();// 获得输出流os &#61; clientSocket.getOutputStream();}} catch (Exception e) {// TODO: handle exception}// 如果成功获得输出流if (os !&#61; null) {os.write("Hello Bluetooth!".getBytes("utf-8"));}} catch (Exception e) {// TODO: handle exception}}// 服务端&#xff0c;需要监听客户端的线程类private Handler handler &#61; new Handler() {public void handleMessage(android.os.Message msg) {Toast.makeText(MainActivity.this, String.valueOf(msg.obj),Toast.LENGTH_SHORT).show();super.handleMessage(msg);}};// 线程服务类private class AcceptThread extends Thread {private BluetoothServerSocket serverSocket;private BluetoothSocket socket;// 输入 输出流private OutputStream os;private InputStream is;public AcceptThread() {try {serverSocket &#61; mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}&#64;Overridepublic void run() {// 截获客户端的蓝牙消息try {socket &#61; serverSocket.accept(); // 如果阻塞了&#xff0c;就会一直停留在这里is &#61; socket.getInputStream();os &#61; socket.getOutputStream();// 不断接收请求,如果客户端没有发送的话还是会阻塞while (true) {// 每次只发送128个字节byte[] buffer &#61; new byte[128];// 读取int count &#61; is.read();// 如果读取到了&#xff0c;我们就发送刚才的那个ToastMessage msg &#61; new Message();msg.obj &#61; new String(buffer, 0, count, "utf-8");handler.sendMessage(msg);}} catch (Exception e) {// TODO: handle exception}}}
}

Google的API上其实已经说的很详细了的&#xff0c;这里我再提供一份PDF学习文档&#xff0c;可以更加直观的了解


PDF文档下载地址&#xff1a;http://download.csdn.net/detail/qq_26787115/9416162


Demo下载地址&#xff1a;http://download.csdn.net/detail/qq_26787115/9416158


推荐阅读
  • 我在尝试将组合框转换为具有自动完成功能时遇到了一个问题,即页面上的列表框也被转换成了自动完成下拉框,而不是保持原有的多选列表框形式。 ... [详细]
  • Lua字符串1.字符串常见形式字符串或串(String)是由数字、字母、下划线组成的一串字符。Lua语言中字符串可以使用以下三种方式来表示:•单引号间的一串字符。 ... [详细]
  • 浅谈Android五大布局——LinearLayout、FrameLayout和AbsoulteLa
    为什么80%的码农都做不了架构师?Android的界面是有布局和组件协同完成的,布局好比是建筑里的框架,而组件则相当于建筑里的砖瓦。 ... [详细]
  • 本文详细介绍了PHP中的几种超全局变量,包括$GLOBAL、$_SERVER、$_POST、$_GET等,并探讨了AJAX的工作原理及其优缺点。通过具体示例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • IntelliJ IDEA配置微服务启动显示
    通过编辑IntelliJ IDEA的workspace.xml文件,可以实现微服务启动对象的显示。具体步骤包括定位并修改workspace.xml中的RunDashboard部分。 ... [详细]
  • 本文总结了在多人协作开发环境中使用 Git 时常见的问题及其解决方案,包括错误合并分支的处理、使用 SourceTree 查找问题提交、Git 自动生成的提交信息解释、删除远程仓库文件夹而不删除本地文件的方法、合并冲突时的注意事项以及如何将多个提交合并为一个。 ... [详细]
  • 来自FallDream的博客,未经允许,请勿转载,谢谢。一天一套noi简直了.昨天勉强做完了noi2011今天教练又丢出来一套noi ... [详细]
  • 题面:P3178[HAOI2015]树上操作好像其他人都嫌这道题太容易了懒得讲,好吧那我讲。题解:第一个操作和第二个操作本质上是一样的&# ... [详细]
  • HDU 2537 键盘输入处理
    题目描述了一个名叫Pirates的男孩想要开发一款键盘输入软件,遇到了大小写字母判断的问题。本文提供了该问题的解决方案及实现方法。 ... [详细]
  • UVa 11683: 激光雕刻技术解析
    自1958年发明以来,激光技术已在众多领域得到广泛应用,包括电子设备、医疗手术工具、武器等。本文将探讨如何使用激光技术进行材料雕刻,并通过编程解决一个具体的激光雕刻问题。 ... [详细]
  • Docker基础入门与环境配置指南
    本文介绍了Docker——一款用Go语言编写的开源应用程序容器引擎。通过Docker,用户能够将应用及其依赖打包进容器内,实现高效、轻量级的虚拟化。容器之间采用沙箱机制,确保彼此隔离且资源消耗低。 ... [详细]
  • 本文详细介绍了Apache Spark 2.2.0版本中集群模式的基本概念和工作流程,包括如何通过集群管理器分配资源,以及Spark应用程序在集群中的运行机制。链接:http://spark.apache.org/docs/2.2.0/cluster-overview.html ... [详细]
  • 本文探讨了如何选择一个合适的序列化版本ID(serialVersionUID),包括使用生成器还是简单的整数,以及在不同情况下应如何处理序列化版本ID。 ... [详细]
  • 本文由公众号【数智物语】(ID: decision_engine)发布,关注获取更多干货。文章探讨了从数据收集到清洗、建模及可视化的全过程,介绍了41款实用工具,旨在帮助数据科学家和分析师提升工作效率。 ... [详细]
  • 本文介绍了如何通过安装和配置php_uploadprogress扩展来实现文件上传时的进度条显示功能。通过一个简单的示例,详细解释了从安装扩展到编写具体代码的全过程。 ... [详细]
author-avatar
Hoorxx鹿_416
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有