前言:蓝牙聊天App设计全部有三篇文章(一、UI界面设计,二、蓝牙搜索配对连接实现,三、蓝牙连接聊天),这篇文章是:三、蓝牙连接聊天。

课程1:Android Studio小白安装教程,以及第一个Android项目案例“Hello World”的调试运行
课程2:蓝牙聊天App设计1:Android Studio制作蓝牙聊天通讯软件(UI界面设计)
课程3:蓝牙聊天App设计2:Android Studio制作蓝牙聊天通讯软件(蓝牙搜索配对连接)
课程4:蓝牙聊天App设计3:Android Studio制作蓝牙聊天通讯软件(完结,蓝牙连接聊天,结合生活情景进行蓝牙通信的通俗讲解,以及代码功能实现,内容详细,讲解通俗易懂)

本次任务:
本次项目的任务是制作一个基于蓝牙通信协议的蓝牙聊天软件。具体内容就是用户双方在手机上安装本软件后,通过蓝牙连接后进行“微信聊天”。文章末尾附项目下载链接共享。

效果图与视频:

用户1效果图

用户2效果图

效果视频

基于Android的蓝牙通信聊天软件(效果视频)

文章说明:
本文将结合生活情景,讲解蓝牙通信原理。具体讲解代码实现过程并详细解析代码含义。本文的前提是你已经能够掌握蓝牙的搜索展示,连接配对功能,如果还未掌握,建议先观看前面文章,一步一个脚印来。

在明确任务和目标效果后,下面开始本项目的内容讲解:


上图是蓝牙通信的原理图,建议看完,清楚整个功能流程后,再针对每个环节进行开发。为了进一步加深理解,下面举一个情景例子。

周末,你在家里一个人呆着无聊,就通过发朋友圈(开启BluetoothServerSocket)的方式,邀请一位朋友来家里做客(由于你家太小,只能待两个人)。你的朋友老王看到你的朋友圈(BluetoothServerSocket)后,立刻就私信你,询问自己能否去你家里做客聊天(发起访问通道请求)。你答应了,给了老王你家(BluetoothSocket)的详细地址,并立刻删除刚才的朋友圈(关闭BluetoothServerSocket),防止其他朋友看到后也要来做客。接下来,你就一直待在家里,等待老王上门(Accept)。老王到了你家门口后,就一直按门铃(connect),直到你开门邀请进去。到了这个时候,其实你们就已经处在同个聊天场所(BluetoothSocket)了。坐下聊天过程中,你和老王轮流说话(write),但是无论你们是否在说话,耳朵却一直保持倾听(read)的状态。到了中午时,由于老王下午还有事要处理,便与你告别,离开你家(断开BluetoothSocket)。老王离开后,你就重新发了朋友圈(开启BluetoothServerSocket),等待下一位朋友的私信做客。

概念的提前解释

下面的内容是编程过程中会出现的常见词,在这里先提前通俗解析一下,起码得在心中有个概念,这是干什么用的,有什么区别?

1.Socket(套接字)
用于进程间的通信,创建一个Socket,其实就是相当于创建一个聊天通道(BluetoothServerSocket和BluetoothSocket),也就是聊天场所的建立。

2.Handler
用于线程间的通信。与前面的Binder/Socket用于进程间通信不同,Handler用于同进程的线程间通信。用最简单的话描述: handler其实就是子线程运行并生成Message后,Looper获取message并传递给Handler。本质是“消息池”、“快递中转站”,实现不同线程间的信息交互。
例如:在聊天场景中,将聊天线程中需更新UI的操作信息传递到UI主线程,从而实现聊天线程对UI的更新处理,最终实现异步消息的处理

3.Handler中obtainMessage与new Message的区别:
(1)obtainmessage() 相当于向快递中转站(Handler)拿一个空的旧盒子(Message),不需要另开辟空间,obtianmessage指循环利用旧盒子
(2)new Message() 相当于向快递中转站(Handler)拿一个空的新盒子(Message),需要重新申请,效率低

4.Message
message其实相当于一个快递盒子,是线程间的传递媒介。聊天子线程把需要更新的文字信息包装在message这个盒子里,然后把message盒子送到Handler这个“物流中心”,Handler又把需要更新的内容准确传递到主线程手里,然后更新UI。message的结构主要包括what字段和obj字段,what用来标明信息类型(int型),obj用来寄放信息内容(String型),例如:MSG_GOT_DATA代表告诉Handler,我已经正常收到了信息,信息的内容是“我想问一下你现在吃饭没?”,Message的结构如下所示:

what obj
MSG_GOT_DATA 我想问一下你现在吃饭没?

5.sendMessage和sendEmptyMessage的区别
二者的作用都是线程向Handler发送信息message(也就是寄快递)
(1)sendEmptyMessage(int what) 与sendMessage(Message msg) 相比,只有盒子的what空间能使用,性能上比sendMessage快,但局限性很明显
(2)sendMessage(Message msg) 相当于向快递中转站(Handler)寄一整个盒子,其中盒子的what空间和obj空间都能使用

what内容的获取 obj内容的获取
int what = message.what String obj = message.obj
what = MSG_GOT_DATA obj = “我想问一下你现在吃饭没?”

Constant.java是用来预先定义一些下面可能需要用到的常量(其实就是对sendEmptyMessage(int what)中“what”内容的规定)

package com.example.wyb.btw3.connect;
/*** Created by WYB on 2023/4/24.*/
public class Constant {public static final String CONNECTTION_UUID = "00001101-0000-1000-8000-00805F9B34FB";/*** 开始监听*/public static final int MSG_START_LISTENING = 1;/*** 结束监听*/public static final int MSG_FINISH_LISTENING = 2;/*** 有客户端连接*/public static final int MSG_GOT_A_CLINET = 3;/*** 连接到服务器*/public static final int MSG_CONNECTED_TO_SERVER = 4;/*** 获取到数据*/public static final int MSG_GOT_DATA = 5;/*** 出错*/public static final int MSG_ERROR = -1;
}
//明确快递中转站(Handler)收到what类型后,向UI界面传递什么更新内容
private class MyHandler extends Handler {public void handleMessage(Message message) {super.handleMessage(message);switch (message.what) {case Constant.MSG_GOT_DATA:     //这些代表什么,文章前面的定义有说明showToast("data:" + String.valueOf(message.obj));break;case Constant.MSG_ERROR:showToast("error:" + String.valueOf(message.obj));Log.e("提示","error:" + String.valueOf(message.obj));break;case Constant.MSG_CONNECTED_TO_SERVER:showToast("连接到服务端");break;case Constant.MSG_GOT_A_CLINET:showToast("找到客户端");break;}}
}

功能实现

一、创建请求访问通道并开启端口监听(服务端):

请求访问通道的建立:需要通过创建一个AcceptThread线程来实现。之所以需要创建线程,是因为Accept()会处于阻塞状态,一直监听,等待连接请求。所以需要创建一个线程来专门干这事。

mAcceptThread = new AcceptThread(mblueToothController.getAdapter(),mUIHandler);
mAcceptThread.start();

AcceptThread.java的完整代码如下(每段代码都有详细功能解析)

package com.example.wyb.btw3.connect;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import java.io.IOException;
import java.util.UUID;
/*** Created by WYB on 2023/4/24.*/
public class AcceptThread extends Thread {private static final String NAME = "BlueToothClass";private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);private final BluetoothServerSocket mmServerSocket;private final BluetoothAdapter mBluetoothAdapter;private final Handler mHandler;private ConnectedThread mConnectedThread;public AcceptThread(BluetoothAdapter adapter, Handler handler) {mBluetoothAdapter = adapter;mHandler = handler;BluetoothServerSocket tmp = null;try {// MY_UUID是应用程序的UUID,客户端代码使用相同的UUID,服务端开启端口并返回一个BluetoothServerSocket,等待客户端请求tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);} catch (IOException e) { }mmServerSocket = tmp;}//run函数是线程执行start()后执行的函数public void run() {BluetoothSocket socket;//持续监听,直到出现异常或返回socketwhile (true) {try {mHandler.sendEmptyMessage(Constant.MSG_START_LISTENING);socket = mmServerSocket.accept();//accept处于阻塞状态,直到收到客户端请求,并返回给客户端一个BluetoothSocket对象,也就是聊天通道的建立} catch (IOException e) {mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, e));break;}// 如果一个连接被接受if (socket != null) {// 在单独的线程中完成管理连接的工作manageConnectedSocket(socket);try {mmServerSocket.close();mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);} catch (IOException e) {e.printStackTrace();}break;}}}private void manageConnectedSocket(BluetoothSocket socket) {//只支持同时处理一个连接,因为多个连接的话涉及内容复杂,所以本项目只接受一个设备连接。如果已经存在一个在运行的聊天线程,则关闭掉if( mConnectedThread != null) {mConnectedThread.cancel();}mHandler.sendEmptyMessage(Constant.MSG_GOT_A_CLINET);//创建一个聊天线程(ConnectedThread,具体实现文章后面会详细解读)mConnectedThread = new ConnectedThread(socket, mHandler);mConnectedThread.start();}/*** 取消监听socket,使此线程关闭*/public void cancel() {try {mmServerSocket.close();mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);} catch (IOException e) { }}//发送信息的实现public void sendData(byte[] data) {if( mConnectedThread!=null){mConnectedThread.write(data);}}
}

二、发起聊天请求并建立通信通道(客户端):

device指服务端的蓝牙设备,mblueToothController.getAdapter()指本客户端的蓝牙适配器。创建线程是因为connect()这个函数会发生阻塞。

mConnectThread = new ConnectThread(device,mblueToothController.getAdapter(),mUIHandler);
mConnectThread.start();

ConnectThread.java的完整代码如下(每段代码都有详细功能解析)

package com.example.wyb.btw3.connect;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
/*** Created by WYB on 2023/4/24.*/
public class ConnectThread extends Thread {private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;private BluetoothAdapter mBluetoothAdapter;private final Handler mHandler;private ConnectedThread mConnectedThread;public ConnectThread(BluetoothDevice device, BluetoothAdapter adapter, Handler handler) {BluetoothSocket tmp = null;mmDevice = device;mBluetoothAdapter = adapter;mHandler = handler;// 用BluetoothSocket连接到给定的蓝牙设备try {//通过同一个UUID向服务端device发起连接请求,服务端接收请求后会建立并返回一个BluetoothSocket聊天通道tmp = device.createRfcommSocketToServiceRecord(MY_UUID);} catch (IOException e) { }mmSocket = tmp;}//由于请求过程处于阻塞状态,所以整个请求过程得用线程public void run() {// 关闭蓝牙的搜索功能,避免请求过程出现数据错误mBluetoothAdapter.cancelDiscovery();try {// 通过BluetoothSocket通道发起设备连接请求,阻塞运行直到成功或抛出异常mmSocket.connect();} catch (Exception connectException) {mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, connectException));// 如果无法连接则关闭socket并退出try {mmSocket.close();} catch (IOException closeException) { }return;}// 在单独的线程中完成管理连接的工作manageConnectedSocket(mmSocket);}//开门成功后就开始下一个任务private void manageConnectedSocket(BluetoothSocket mmSocket) {//发送连接成功提示文字mHandler.sendEmptyMessage(Constant.MSG_CONNECTED_TO_SERVER);//创建连接后的处理线程,即通信线程mConnectedThread = new ConnectedThread(mmSocket, mHandler);mConnectedThread.start();}/*** 取消正在进行的连接并关闭socket*/public void cancel() {try {mmSocket.close();} catch (IOException e) { }}/*** 发送数据*/public void sendData(byte[] data) {if( mConnectedThread!=null){mConnectedThread.write(data);}}
}

三、数据交互与断开(服务端与客户端的聊天实现)

数据交互阶段的原理其实特别简单,就是服务端和客户端都分别开启一个聊天线程,聊天线程的主要功能其实就一个——一直执行read()。也就是处于监听状态,等待另一方write()。

ConnectedThread.java的完整代码如下(每段代码都有详细功能解析)

package com.example.wyb.btw3.connect;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/*** Created by WYB on 2023/4/24.*/
public class ConnectedThread extends Thread {private final BluetoothSocket mmSocket;private final InputStream mmInStream;private final OutputStream mmOutStream;private final Handler mHandler;public ConnectedThread(BluetoothSocket socket, Handler handler) {mmSocket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;mHandler = handler;try {tmpIn = socket.getInputStream(); //输入流tmpOut = socket.getOutputStream();  //输出流} catch (IOException e) { }mmInStream = tmpIn;mmOutStream = tmpOut;}public void run() {byte[] buffer = new byte[1024];  // 用于流的缓冲存储int bytes; // 从read()返回bytes// 持续监听InputStream(也就是对方发来的信息),直到出现异常while (true) {try {// 从InputStream读取数据,也就是我前面提到的一直保持read()bytes = mmInStream.read(buffer);// 将获得的bytes发送到UI层activityif( bytes >0) {//obtainMessage可以看前面的解析Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA, new String(buffer, 0, bytes, "utf-8"));mHandler.sendMessage(message);}Log.d("GOTMSG", "message size" + bytes);} catch (IOException e) {mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, e));break;}}}/*** 在main中调用此函数,将数据发送到远端设备中*/public void write(byte[] bytes) {try {mmOutStream.write(bytes);} catch (IOException e) { }}/*** 在main中调用此函数,断开连接*/public void cancel() {try {mmSocket.close();} catch (IOException e) { }}
}

四、bluetoothChat项目功能demo分享:
链接:https://pan.baidu.com/s/1z8tW3aA7a5knKxiwlE3BFw 提取码:3d53

五、bluetoothChat最终App分享:
链接:https://pan.baidu.com/s/1XOpO62n8e2SfdyJzWf5Mwg 提取码:12pf

蓝牙聊天App设计3:Android Studio制作蓝牙聊天通讯软件(完结,蓝牙连接聊天,结合生活情景进行蓝牙通信的通俗讲解,以及代码功能实现,内容详细,讲解通俗易懂)相关推荐

  1. 蓝牙聊天App设计1:Android Studio制作蓝牙聊天通讯软件(UI界面设计)

    前言:蓝牙聊天App设计全部有三篇文章(一.UI界面设计,二.蓝牙搜索配对连接实现,三.蓝牙连接聊天),这篇文章是一.UI界面设计 课程1:Android Studio小白安装教程,以及第一个Andr ...

  2. 蓝牙App设计2:使用Android Studio制作一个蓝牙软件(包含:代码实现等)

    前言:蓝牙聊天App设计全部有三篇文章(一.UI界面设计,二.蓝牙搜索配对连接实现,三.蓝牙连接聊天),这篇文章是:二.蓝牙搜索配对连接实现. 课程1:Android Studio小白安装教程,以及第 ...

  3. Android Studio制作手机App:通过手机蓝牙(Bluetooth)与STM32上的低功耗蓝牙(HC-42)连接通信,实现手机端对单片机的控制。

    背景: 本文的内容是针对单片机蓝牙模块(HC-42)开发的手机App.在这之前,我想先声明一点,手机与手机间的蓝牙连接方式"与"手机与HC间的蓝牙连接方式"是不一样的.原 ...

  4. android页面布局计算机,Android Studio制作简单计算器App

    Android Studio制作简单计算器App 计算机界面如图: 程序设计步骤: (1)在布局文件中声明编辑文件框EditText,按钮Button等组件. (2)在MainActivity中获取组 ...

  5. 物联网控制APP入门专题(四)---使用android studio制作一个控制页面的APP框架

    摘要:上篇文章讲了如何用阿里云IoT Studio快速制作一个网页版的手机端,以及通过第三方平台将这个网页打包成一个APK文件,使它可以安装到手机实现APP的功能.但是使用第三方平台做的APP是需要收 ...

  6. Android Studio制作.9图片,看这一篇就够了

    一..9.png图片概念 这是安卓开发里面的一种特殊的图片 这种格式的图片在android 环境下具有自适应调节大小的能力,不会失真 (1)允许开发人员定义可扩展区域,当需要延伸图片以填充比图片本身更 ...

  7. Android Studio 制作微信界面 上

    工程功能介绍 打开app,首先是个闪屏界面(常见于一般打开app时的小广告),设置时间为2s后进入登录界面.在登录界面中,中间可以输入密码,点击登录按钮进入微信的界面.   微信的界面由4个fragm ...

  8. Android Studio 制作微信界面 下

    主界面 上一篇文章的链接: Android Studio 制作微信界面 上_nazonomaster的博客-CSDN博客https://blog.csdn.net/nazonomaster/artic ...

  9. Android studio制作计算器源代码

    版权声明:本文为博主原创文章,未经博主允许不得转载.https://mp.csdn.net/postedit/82623704 一.Android studio制作计算器源代码 这是我学Android ...

最新文章

  1. php列表显示教程,Dedecms后台管理文档列表显示自定义字段方法教程
  2. C语言 —— 贪吃蛇
  3. jquery uploadify 多文件上传插件 使用经验
  4. 详解JMeter函数和变量
  5. checkInterruptWhileWaiting
  6. java dfs算法蓝桥杯题_【蓝桥杯省赛JavaB组真题详解】四平方和(2016)_疼疼蛇的博客-CSDN博客...
  7. CCF 201509-1 数列分段
  8. EM算法最完整易懂讲解
  9. MySQL学习笔记15:触发器
  10. HDU2072 单词数(解法二)【废除!!!】
  11. 蓝桥杯2018年第九届C/C++省赛B组第六题-递增三元组
  12. ASP连接sql server实例解析
  13. eXtremeComponents简单应用
  14. 电脑提示文件或目录损坏且无法读取
  15. 单片机C语言59秒计时器,0到59秒单片机秒表课程设计报告.doc
  16. CM添加kafka服务
  17. [进程通信] 进程间通信 之 管道
  18. 【Java 类和对象】
  19. linux中白屏变黑屏咋变,解决 APP启动白屏黑屏问题
  20. matlab调用海康威视摄像头_招聘|海康威视招聘一批算法、图像等AI工程师

热门文章

  1. jacobian 矩阵意义_对雅可比矩阵的理解
  2. boss直聘个人使用体验
  3. 启动apache,却访问不了页面
  4. 归因分析笔记4:PCA逆变换
  5. 笔记:表单验证以及sweetalert中swal的使用
  6. Python----virtualenv虚拟沙盘
  7. 如何将AI模型集成到android应用(app)中
  8. 安卓开发 切换应用语言和获取系统语言
  9. 架构设计说明书究竟应该包含什么
  10. 8.14.4. jsonb Indexing