手把手教你做蓝牙聊天应用(二)-设计方案

第2节 设计方案

功能确定后,就要开始围绕功能进行功能的验证、界面设计的规划、以及程序结构的规划了。

2.1 技术验证

选定了现阶段要完成的核心功能后,我们首先需要对它们做技术上的验证,看看用什么样的方法能实现它们。在进行技术验证的同时,也能让我们发现很多我们在头脑风暴阶段没有意识到的现实问题。

现在的手机和移动设备已经把蓝牙作为了标准配置,它常常用到与周边小设备的数据连接上,例如蓝牙自拍杆,蓝牙音箱,蓝牙键盘,运动手环等等,可以看出这都是一些不需要太大数据量传输而需要保持长时间数据通信的设备。

蓝牙技术从出现到现在经过了多个版本,现在手机上最普遍的就是蓝牙4.0了。

作为应用程序的开发者,其实并不需要知道很多与蓝牙技术相关的硬件知识。Google将软件开发者会用到的所有蓝牙功能,都封装了起来,通过Android SDK提供给开发者使用。所以,只要我们知道如何使用Android SDK中提供的蓝牙接口函数就可以了。至于硬件是如何实现的无线电波互相通信,开发者完全不需要去关心。

要操作蓝牙设备,我们首先要获取操作蓝牙设备的对象BluetoothAdapter

BluetoothAdapter BTAdapter = BluetoothAdapter.getDefaultAdapter();

同时,还需要使用到三项系统权限,

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.anddle.anddlechat">

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    ......
</manifest>

之后我们就可以通过BluetoothAdapter对真实的蓝牙设备以及软件功能进行操作使用了。

2.1.1 设备发现

一句简单的“让设备互相连通”,实际上却包含了很多隐藏的任务。

2.1.1.1 开启蓝牙

用户并不一定总是把蓝牙设备开启的,因为我们的应用要使用蓝牙,假如用户没有打开蓝牙设备,我们的程序得打开它或者提示让用户主动打开它。

有三种方式开启蓝牙功能,

  1. 使用安卓系统提供的开关打开。例如,在设置->蓝牙界面中打开,

  2. 在应用中使用Intent启动确认窗口,让用户选择是否允许打开,
    BluetoothAdapter BTAdapter = BluetoothAdapter.getDefaultAdapter();
    //判断蓝牙功能是否打开
    if (!BTAdapter.isEnabled()) {
        //没有打开,就启动确认窗口询问用户是否打开
        Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivity(i);
    }

  3. 在应用中直接打开蓝牙功能,不需要询问用户,
    BluetoothAdapter BTAdapter = BluetoothAdapter.getDefaultAdapter();
    //判断蓝牙功能是否打开
    if (!BTAdapter.isEnabled()) {
        ////没有打开,就直接打开
        BTAdapter.enable();
    }

2.1.1.2 允许被发现

蓝牙打开后,设备并不一定能够别的蓝牙设备找到,出于安全角度考虑,很多设备的蓝牙开关即使是打开的,也有可能禁止让别的设备发现自己。

为了保险起见,我们需要设置设备可以被别的蓝牙设备发现,

  1. 使用BluetoothAdaptergetScanMode()方法,获取当前的蓝牙是否能被发现;
  2. 通过BluetoothAdapter.ACTION_REQUEST_DISCOVERABLEIntent启动询问窗口;
  3. BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION用来指定允许被发现的时间,例如,120就表示未来120秒内该蓝牙设备允许被别人发现;如果将该值设置成0,表示可以一直被发现;
BluetoothAdapter BTAdapter = BluetoothAdapter.getDefaultAdapter();
//判断是非可以被发现
if(BTAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
    //启动窗口,询问用户是否可以设置成允许被发现
    Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    //可以一直被别的蓝牙设备发现
    i.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0);
    startActivity(i);
}

2.1.1.3 搜索可连接设备

搜索可连接的蓝牙设备,

  1. 使用BluetoothAdapterstartDiscovery()方法开始搜素设备

    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    
    //如果正在搜索蓝牙设备,则停止搜索
    if (mBluetoothAdapter.isDiscovering()) {
        mBluetoothAdapter.cancelDiscovery();
    }
    
    //开始搜索蓝牙设备
    mBluetoothAdapter.startDiscovery();
  2. 当开始搜索的时候,系统会发出一个BluetoothAdapter.ACTION_DISCOVERY_STARTED广播;

    当结束搜索的时候,系统会发出一个BluetoothAdapter.ACTION_DISCOVERY_FINISHED广播;

    当找到某个可连接设备的时候,系统会发出一个BluetoothAdapter.ACTION_FOUND广播;并把可连设备的信息放在广播对应的Intent当中;

    因此,需要自定义一个BroadcastReceiver来接收这些广播,

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
    
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                //从Intent中获取可连接的设备
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    
            } else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
            //开始搜索了    
    
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            //结束搜索了
            }
    
        }
    };
  3. 接收机采用动态的方式,在应用启动的时候被注册,不使用的时候要注销,
    //注册接收机
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothDevice.ACTION_FOUND);
    filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
    filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    registerReceiver(mReceiver, filter);
    
    ------------------------------------------
    
    //注销接收机
    unregisterReceiver(mReceiver);

2.1.1.4 搜索结果展示

应用需要一个搜索附近可连设备的功能,它能把附近的设备搜索出来,形成列表供用户选择,如果用户选择了某个设备,那么它就要去主动连接这个设备。

设置->蓝牙界面就有这样的展示,

但是,我们自身的聊天应用也要有一个类似的界面,方便用户选择设备,在同一个应用内就完成展示和连接的功能。

2.1.2 设备连接

蓝牙设备的连接(wifi设备也是如此)可以分成主动连接和被动连接。

设备的连接是通过Socket-套结字实现的。所谓Socket就像它的英文意思“插座”一样,将两个不同的实体通过插口插座连接起来。

两个实体连接起来后,就可以通过套结字来传送数据了。



/*******************************************************************/

* 版权声明

* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。

/*******************************************************************/


2.1.2.1 被动连接

应用如果不去主动发现周围设备,不主动去连接其它设备,那它可以作为一个被动的接受者,等待别人的连接。

  1. 创建一个BluetoothServerSocket,需要给他指定一个名字和UUID。名字可以任意指定,UUID就有一定的学问了。使用相同UUID的蓝牙应用,可以被彼此发现。

    蓝牙设备的UUID可以分成两个大类,

    1. 由蓝牙标准统一规定的UUID。例如00001101-0000-1000-8000-00805F9B34FB这个UUID代表的就是蓝牙串口服务
    2. 自定义UUID,蓝牙应用的开发者可以自己“设计”一个UUID。这个UUID不能与已有规定的UUID相同,只是为了将自己的蓝牙应用与别的蓝牙应用区分开。

    蓝牙的UUID概念有点像网络编程中的端口号。

    例如,按照规定,端口号23是分配给FTP服务使用的,其它FTP应用想要彼此通信,不用去猜对方的端口号是什么,只要知道协议中已经规定了23,大家都会遵守就行,设计出来的应用绝不会有问题。

    开发者也可以为自己的应用指定一个不知名的端口号,不需要和别的应用互通。

    这里我们就使用蓝牙串口服务的UUID

    BluetoothAdapter BluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    
    final String BT_NAME = "AnddleChat";
    final UUID BT_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    BluetoothServerSocket mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(
                BT_NAME, BT_UUID);
  2. 启动一个线程-叫它监听线程,在它当中,使用BluetoothServerSocketaccept()方法,等待其它设备的连接;
    BluetoothSocket socket = mServerSocket.accept();

    这是一个阻塞的调用。就是说一旦执行accept()函数,它就一直等在那里,不会返回了。这也是要将等待连接单独放到监听线程执行的原因。

    如果accept()函数返回了,有两种可能,

    1. 有别的设备向自己发出了连接的请求。accept()返回后,socket就是被连接设备与连接设备建立的Socket,这也正是程序所一直等待的;
    2. mServerSocketclose()函数被调用,导致accept()抛出了异常,
      //别的地方关闭了BluetoothServerSocket
      mServerSocket.close()
      
      ----------------------------------
      
      //监听线程
      try {
          socket = mServerSocket.accept();
      } catch (IOException e) {
          //正在监听会抛出异常
      }

经过上面的流程,监听线程在收到其它蓝牙设备的连接后,就得到了Socket

2.1.2.2 主动连接

蓝牙设备搜索到周围的可连接设备后,可以主动向它们发起连接请求。

  1. 通过对方的地址,获取蓝牙设备,

    BluetoothAdapter BluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceAddr);
    
  2. 创建可发起连接请求的Socket,创建时要使用到UUID,发起连接的设备的UUID要与被动接受连接的设备的UUID要相同,就像是要把发送和接受调整到同样的频道上一样,
    final UUID BT_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    BluetoothSocket mSocket = device.createRfcommSocketToServiceRecord(BT_UUID);
  3. connect()函数主动发起连接请求,该函数是一个阻塞调用,不确定返回的时间,所以需要为它开启一个单独的线程执行,
    //工作线程中执行
    mSocket.connect();

    一旦执行成功后,我们就得到了可以与其它蓝牙设备通信的Socket

2.1.3 数据交换

连接建立以后,双方开始交换数据。无论是主动连接还是被动连接,数据的交换都是通过之前得到的Socket进行的。

  1. 获取读数据和发送数据的能力。Socket交换的数据都是以二进制的形式进行的。因此可以使用流的方式对数据进行操作,

    InputStream mInStream = mSocket.getInputStream();
    OutputStream mOutStream = mSocket.getOutputStream();

    InputStream负责读取从Socket传入的数据;

    OutputStream负责将数据从Socket发送出去;

  2. 采用InputStreamread()方法读取数据。这是一个阻塞的方法,只有连接的对方有数据传送过来的时候才会返回,并把传来的数据放在buffer参数当中,返回值代表读取的数据字节长度。

    读完一次以后,需要重复调用read()继续读取接下来的数据,这是一个循环的过程,

    //存放传入数据的数据区
    byte[] buffer = new byte[MAX_BUFFER_SIZE];
    //存放读取数据的字节长度
    int bytes;
    while (true) {
        try {
            //等待读取数据
            bytes = mInStream.read(buffer);
        } catch (IOException e) {
            //异常情况,例如Socket被关闭,对方断开连接
            break;
        }
    }

    当别当地方关闭了Socket后,mInStream.read()会抛出异常,

    “`java

    //别的地方关闭了Socket

    mSocket.close()



//监听线程

try {

//等待读取数据

bytes = mInStream.read(buffer);

} catch (IOException e) {

//异常情况,例如Socket被关闭

break;

}

“`

因为mInStream是通过mSocket创建的,两者之间已经建立了联系。所以当关闭mSocket的时候mInStream也会收到影响,从而抛出异常。

  1. 采用OutputStreamwrite()方法发送数据

    mOutStream.write(data);

在使用Socket的过程中,很多地方都可能抛出异常,所以在后面设计程序结构的时候,我们要好好考虑如何对应这些异常情况。

2.2 界面设计

确定了功能,进行了技术调查之后,我们就可以开始根据目前掌握的信息,设计应用的使用流程了。

应用流程分为三个部分,

2.2.1 应用启动

  1. 如果蓝牙功能没有打开,启动应用后提示打开蓝牙功能,

  2. 如果不能被其它蓝牙设备发现,启动应用后提示允许被其它蓝牙设备发现,

  3. 如果蓝牙功能已经打开,并且可以被其它设备发现,进入主界面,

2.2.2 设备主动连接及信息发送流程

2.2.3 设备被动连接及信息发送流程,

2.2.4 关于界面

2.3 程序结构

根据应用的功能和界面逻辑,程序结构将分成三个模块,

  1. 聊天记录的展示和发送。使用一个ActivityChatActivity来完成这部分工作;
  2. 发现可连接的蓝牙设备,让用户选择要连接的设备。使用一个ActivityDeviceListActivity来实现。当用户选中了某个设备后,将结果它告诉ChatActivity,统一由ChatActivity来进行连接的逻辑控制;
  3. 连接管理模块。连接蓝牙设备、设备之间发送消息、断开连接等等和通信密切相关的工作都交给ConnectionManager模块。该模块提供简单易用的接口給ChatActivity调用。

时间: 2024-10-05 03:58:15

手把手教你做蓝牙聊天应用(二)-设计方案的相关文章

手把手教你做蓝牙聊天应用(一)-设计方案

前言 通过"计算器"和"视频播放器"我们已经能够开始开发一些比较像样的应用了. 今天,我们将开始制作一个"蓝牙聊天"应用.这个应用其实很简单,没有炫酷的界面,就是一对一.通过蓝牙连接两台设备,让两个人互相发送信息. 可别觉得它太无聊.没有什么实用性,其实我们正是想通过它让你开始接触网络编程(蓝牙和wifi都是无线连接技术,它们的程序设计方法和思路非常的相似). 另外,学会了使用蓝牙,就为大家打开了技术开发的另一扇大门-物联网,现在很多物联网硬件都

手把手教你做蓝牙聊天应用(五)-界面使用ConnectionManager

第5节 界面使用ConnectionManager ConnectionManager已经设计完成了,它的价值需要在ChatActivity中体现出来. 5.1 监听ConnectionManager 实现对ConnectionManager各个状态的监听,当ConnectionManager的状态有变化.收到发送的数据时,需要让ChatActivity知道,它才能将各种变化反应到用户界面上. 5.1.1 创建监听器 ConnectionManager定义了ConnectionListener接

手把手教你做蓝牙聊天应用(六)-界面优化

第6节 应用的美化与完善 现在,我们还可以为聊天应用加上多国语言的支持和关于界面,把使用到的颜色和尺寸定义到资源文件当中,这样一来,安豆的蓝牙聊天应用就算是比较完整的完成了. 这两部分在以前"计算器"章节中,已经介绍过了,大家就自己动手吧. 这一节,我们将重点介绍聊天文字的背景图片是如何制作的. 6.1 9Patch图片的原理 观察一下安卓系统中需要经常用到的图片,可以发现: 很多要使用透明效果的地方在转角处: 很多图片不同的地方只在靠近边缘的地方,内部区域几乎都是一样的: 为此安卓系

手把手教你做蓝牙聊天应用(三)-获取要连接的设备

第3节 获取要连接的设备 这一节我们开始设计蓝牙聊天应用的界面.根据之前的规划,连接管理将放在单独的ConnectionManager模块当中,所以每当要使用连接功能的时候,我们就暂时把它空着,等到ConnectionManager开发完成之后再加进来. 这里我们将完成下面的界面设计, 3.1 主界面 主界面是一个独立的Activity-ChatActivity,它要实现三个主要功能, 当蓝牙没有开启或者设备不能被发现的时候,请求用户打开对应的功能: 下方有输入框输入要发送的文字内容,点击按钮后

手把手教你做蓝牙小车(二)

第5节 BTChat 本节开始介绍Arduino蓝牙模块,配合Android应用,实现一个蓝牙聊天应用. 5.1 什么是蓝牙 简单说就是一种不同设备之间点对点通讯的技术. 有大篇大篇的蓝牙各种协议,各种规范... 本课程只讲用到的内容,不展开更多内容了. 5.2 SDP Service Discovery Protocol,简称SDP,是允许设备发现其他设备所支持服务的协议. 蓝牙协议给每个服务分配一个UUID,用来区分各种服务. SDP的UUID是00001101-0000-1000-8000

手把手教你做蓝牙聊天应用(四)-蓝牙连接模块

第4节 蓝牙连接模块 蓝牙连接的管理模块需要为ChatActivity提供于连接相关的所有功能,要设计的方便使用,并尽量隐藏连接的细节. 4.1 对外接口 我们首先来看看ConnectionManager需要向Chat Activity提供哪些接口. 监听.当应用运行起来后,聊天应用需要启动对其它蓝牙设备的监听,迎接随时可能到来的连接请求.所以ConnectionManager需要提供启动监听-startListen()和停止监听-stopListen()的两个接口: 主动连接.应用搜索到可连接

手把手教你做蓝牙小车(一)

第1节 选择Arduino开发板 1.1 Arduino是什么 对Arduino,官方有一堆解释. 作为一个软件程序猿,在我眼里,Arduino是学习"可怕硬件"的一个便捷通道.它把复杂的硬件名称,属性给我们隐藏起来,只需要一些简单的软件知识,就可以学习硬件开发. 1.2 怎么选择Arduino开发板 1.2.1 官方版本还是兼容版本 Arduino是开源项目,硬件结构,软件设计都开源. 所以不存在盗版的问题. 官方版质量肯定是杠杠的,但价格也贵. 两者价格数字差不多,一个卖人民币,一

手把手教你做蓝牙小车(三)

第6节 马达 要说蓝牙小车哪个模块最重要,多数人一定会以为是马达. 之前说过,为了防止开发板被电流击穿,控制马达时要增加一块扩展板. 所以,控制马达,只对扩展板编程,而不需要对马达编程. 此外,扩展板厂家会提供通过扩展板控制马达的代码. 综上,对开发人员来说,马达,只要确认存在就可以了. 6.1 扩展板 Arduino开发板只提供了一些基础.通用的接口,针对一些常见的特殊功能,Arduino专门为其推出了扩展板. 6.1.1 官方扩展板 Arduino官方目前总共推出了5款扩展板. 分别是 Ar

手把手教你做Android聊天机器人

我相信大家应该知道有款应用叫小黄鸡吧! 如果不知道,那你见过那种可以秒回复的聊天应用么? 如果仍然没看到!那你总见过可以迅速回复你的微信公共账吧! 如果仍然....亲出门左拐 好,不多说. 首先大家都应该了解程序,程序就是由人为的设定搭建起来的一套系统, 这里的机器人也是,简单的原理就是当你输入关键字后,通过一套算法, 在数据库中找到与之最为匹配的内容在返回给你.这个已经有人实现,我们这节教程就用别人 已经实现好的东西去做一??个应用来玩耍~~ (当然如果你非要想知道如何去做,给我留言.看情况我