Android - 传统蓝牙通信聊天
Android -传统蓝牙通信聊天
概述
详细
原文地址:
Android 蓝牙开发(一)蓝牙通信 CSDN
一、准备工作
开发环境:
jdk1.8
Eclipse Luna Service Release 1 (4.4.1)
运行环境:
华为荣耀6(Android4.4)、华为p9(Android7.0)
实现功能:
Android 蓝牙开发 (开关蓝牙、搜索设备、蓝牙配对、连接、通信、断开连接等)。
二、代码结构
代码包里面,有两个部分,一个是源码,一个是V7支持包。

三、程序实现-蓝牙通信
1 蓝牙基本操作
随着可穿戴设备的流行,研究蓝牙是必不可少的一门技术了。
总结了下蓝牙开发使用的一些东西分享一下。
蓝牙权限
首先需要AndroidManifest.xml文件中添加操作蓝牙的权限。
|
1
2
3
4
|
<uses-permissionandroid:name="Android.permission.BLUETOOTH" />//允许程序连接到已配对的蓝牙设备。<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />//允许程序发现和配对蓝牙设备。 |
BluetoothAdapter
操作蓝牙主要用到的类 BluetoothAdapter类,使用时导包
import android.bluetooth.BluetoothAdapter;
源码具体位置frameworks/base/core/Java/android/bluetooth/BluetoothAdapter.java
BluetoothAdapter 代表本地设备的蓝牙适配器。该BluetoothAdapter可以执行基本的蓝牙任务,例如启
动设备发现,查询配对的设备列表,使用已知的MAC地址实例化一个BluetoothDevice类,并创建一个
BluetoothServerSocket监听来自其他设备的连接请求。
获取蓝牙适配器
|
1
|
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); |
开启蓝牙
|
1
2
3
4
5
6
7
|
if(!mBluetoothAdapter.isEnabled()){ //弹出对话框提示用户是后打开 Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enabler, REQUEST_ENABLE); //不做提示,直接打开,不建议用下面的方法,有的手机会有问题。 // mBluetoothAdapter.enable(); } |
获取本地蓝牙信息
|
1
2
3
4
5
6
7
8
9
10
11
|
//获取本机蓝牙名称 String name = mBluetoothAdapter.getName(); //获取本机蓝牙地址 String address = mBluetoothAdapter.getAddress(); Log.d(TAG,"bluetooth name ="+name+" address ="+address); //获取已配对蓝牙设备 Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices(); Log.d(TAG, "bonded device size ="+devices.size()); for(BluetoothDevice bonddevice:devices){ Log.d(TAG, "bonded device name ="+bonddevice.getName()+" address"+bonddevice.getAddress()); } |
搜索设备
|
1
|
mBluetoothAdapter.startDiscovery(); |
停止搜索
|
1
|
mBluetoothAdapter.cancelDiscovery(); |
搜索蓝牙设备,该过程是异步的,通过下面注册广播接受者,可以监听是否搜到设备。
|
1
2
3
4
5
6
7
8
|
IntentFilter filter = new IntentFilter(); //发现设备 filter.addAction(BluetoothDevice.ACTION_FOUND); //设备连接状态改变 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //蓝牙设备状态改变 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(mBluetoothReceiver, filter); |
监听扫描结果
通过广播接收者查看扫描到的蓝牙设备,每扫描到一个设备,系统都会发送此广播(BluetoothDevice.ACTION_FOUNDE)。其中参数intent可以获取蓝牙设备BluetoothDevice。
该demo中是连接指定名称的蓝牙设备,BLUETOOTH_NAME为"Galaxy Nexus",如果扫描不到,记得改这个蓝牙名称。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.d(TAG,"mBluetoothReceiver action ="+action); if(BluetoothDevice.ACTION_FOUND.equals(action)){//每扫描到一个设备,系统都会发送此广播。 //获取蓝牙设备 BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(scanDevice == null || scanDevice.getName() == null) return; Log.d(TAG, "name="+scanDevice.getName()+"address="+scanDevice.getAddress()); //蓝牙设备名称 String name = scanDevice.getName(); if(name != null && name.equals(BLUETOOTH_NAME)){ mBluetoothAdapter.cancelDiscovery(); //取消扫描 mProgressDialog.setTitle(getResources().getString(R.string.progress_connecting)); //连接到设备。 mBlthChatUtil.connect(scanDevice); } }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){ } } }; |
设置蓝牙可见性
有时候扫描不到某设备,这是因为该设备对外不可见或者距离远,需要设备该蓝牙可见,这样该才能被搜索到。
可见时间默认值为120s,最多可设置300。
|
1
2
3
4
5
6
7
8
9
10
|
if (mBluetoothAdapter.isEnabled()) { if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent discoverableIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra( BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120); startActivity(discoverableIntent); } } |
2 服务端
android 蓝牙之间可以通过SDP协议建立连接进行通信,通信方式类似于平常使用socket。
首先创建BluetoothServerSocket ,BluetoothAdapter中提供了两种创建BluetoothServerSocket 方式,如下图所示为创建安全的RFCOMM Bluetooth socket,该连接是安全的需要进行配对。而通过listenUsingInsecureRfcommWithServiceRecord创建的RFCOMM Bluetooth socket是不安全的,连接时不需要进行配对。
其中的uuid需要服务器端和客户端进行统一。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
private class AcceptThread extends Thread { // 本地服务器套接字 private final BluetoothServerSocket mServerSocket; public AcceptThread() { BluetoothServerSocket tmp = null; // 创建一个新的侦听服务器套接字 try { tmp = mAdapter.listenUsingRfcommWithServiceRecord( SERVICE_NAME, SERVICE_UUID); //tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID); } catch (IOException e) { Log.e(TAG, "listen() failed", e); } mServerSocket = tmp; } public void run() { BluetoothSocket socket = null; // 循环,直到连接成功 while (mState != STATE_CONNECTED) { try { // 这是一个阻塞调用 返回成功的连接 // mServerSocket.close()在另一个线程中调用,可以中止该阻塞 socket = mServerSocket.accept(); } catch (IOException e) { Log.e(TAG, "accept() failed", e); break; } // 如果连接被接受 if (socket != null) { synchronized (BluetoothChatUtil.this) { switch (mState) { case STATE_LISTEN: case STATE_CONNECTING: // 正常情况。启动ConnectedThread。 connected(socket, socket.getRemoteDevice()); break; case STATE_NONE: case STATE_CONNECTED: // 没有准备或已连接。新连接终止。 try { socket.close(); } catch (IOException e) { Log.e(TAG, "Could not close unwanted socket", e); } break; } } } } if (D) Log.i(TAG, "END mAcceptThread"); } public void cancel() { if (D) Log.d(TAG, "cancel " + this); try { mServerSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of server failed", e); } } } |
mServerSocket通过accept()等待客户端的连接(阻塞),直到连接成功或失败。
3 客户端
客户端主要用来创建RFCOMM socket,并连接服务端。
先扫描周围的蓝牙设备,如果扫描到指定设备则进行连接。mBlthChatUtil.connect(scanDevice)连接到设备,
连接过程主要在ConnectThread线程中进行,先创建socket,方式有两种,
如下代码中是安全的(createRfcommSocketToServiceRecord)。另一种不安全连接对应的函数是createInsecureRfcommSocketToServiceRecord。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
private class ConnectThread extends Thread { private BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { mmDevice = device; BluetoothSocket tmp = null; // 得到一个bluetoothsocket try { mmSocket = device.createRfcommSocketToServiceRecord (SERVICE_UUID); } catch (IOException e) { Log.e(TAG, "create() failed", e); mmSocket = null; } } public void run() { Log.i(TAG, "BEGIN mConnectThread"); try { // socket 连接,该调用会阻塞,直到连接成功或失败 mmSocket.connect(); } catch (IOException e) { connectionFailed(); try {//关闭这个socket mmSocket.close(); } catch (IOException e2) { e2.printStackTrace(); } return; } // 启动连接线程 connected(mmSocket, mmDevice); } public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of connect socket failed", e); } } } |
接着客户端socket主动连接服务端。连接过程中会自动进行配对,需要双方同意才可以连接成功。
4 数据传输
客户端与服务端连接成功后都会调用connected(mmSocket, mmDevice),创建一个ConnectedThread线程()。
该线程主要用来接收和发送数据。客户端和服务端处理方式一样。该线程通过socket获得输入输出流。
private InputStream mmInStream = socket.getInputStream();
private OutputStream mmOutStream =socket.getOutputStream();
发送数据
|
1
2
3
4
5
6
7
8
9
10
|
public void write(byte[] buffer) { try { mmOutStream.write(buffer); // 分享发送的信息到Activity mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer) .sendToTarget(); } catch (IOException e) { Log.e(TAG, "Exception during write", e); } } |
接收数据
线程循环进行接收数据。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public void run() { // 监听输入流 while (true) { try { byte[] buffer = new byte[1024]; // 读取输入流 int bytes = mmInStream.read(buffer); // 发送获得的字节的ui activity Message msg = mHandler.obtainMessage(MESSAGE_READ); Bundle bundle = new Bundle(); bundle.putByteArray(READ_MSG, buffer); msg.setData(bundle); mHandler.sendMessage(msg); } catch (IOException e) { Log.e(TAG, "disconnected", e); connectionLost(); break; } } } |
四、运行效果
1、运行,右键项目:Run as -》Android Application (备注:Eclipse需要配置Android开发环境)
2、运行效果如下:
客户端

服务端

出处:http://www.demodashi.com/demo/10676.html
Android - 传统蓝牙通信聊天的更多相关文章
- Android -传统蓝牙通信聊天
概述 Android 传统蓝牙的使用,包括开关蓝牙.搜索设备.蓝牙连接.通信等. 详细 代码下载:http://www.demodashi.com/demo/10676.html 原文地址: Andr ...
- Android 串口蓝牙通信开发Java版本
Android串口BLE蓝牙通信Java版 0. 导语 Qt on Android 蓝牙通信开发 我们都知道,在物联网中,BLE蓝牙是通信设备的关键设备.在传统的物联网应用中,无线WIFI.蓝牙和Zi ...
- Android - 传统蓝牙(蓝牙2.0)
Android Bluetooth 源码基于 Android L [TOC] Reference BluetoothAdapter 首先调用静态方法getDefaultAdapter()获取蓝牙适配器 ...
- Android蓝牙通信
Android为蓝牙设备之间的通信封装好了一些调用接口,使得实现Android的蓝牙通信功能并不困难.可通过UUID使两个设备直接建立连接. 具体步骤: 1. 获取BluetoothAdapter实例 ...
- Android学习笔记之蓝牙通信...
PS:最近同学问我蓝牙的事,因此自己也就脑补了一下蓝牙... 学习内容: 1.如何实现蓝牙通信技术... 蓝牙通信其实是手机里很常用的一种通信方式,现在的手机中是必然存在蓝牙的,蓝牙通信也是有一部 ...
- Android BLE设备蓝牙通信框架BluetoothKit
BluetoothKit是一款功能强大的Android蓝牙通信框架,支持低功耗蓝牙设备的连接通信.蓝牙广播扫描及Beacon解析. 关于该项目的详细文档请关注:https://github.com/d ...
- Android蓝牙通信总结
这篇文章要达到的目标: 1.介绍在Android系统上实现蓝牙通信的过程中涉及到的概念. 2.在android系统上实现蓝牙通信的步骤. 3.在代码实现上的考虑. 4.例子代码实现(手持设备和蓝牙串口 ...
- Qt on Android 蓝牙通信开发
版权声明:本文为MULTIBEANS ORG研发跟随文章,未经MLT ORG允许不得转载. 最近做项目,需要开发安卓应用,实现串口的收发,目测CH340G在安卓手机上非常麻烦,而且驱动都是Java版本 ...
- Android蓝牙通信具体解释
蓝牙通信的大概过程例如以下: 1.首先开启蓝牙 2,搜索可用设备 3,创建蓝牙socket.获取输入输出流 4,读取和写入数据 5.断开连接关闭蓝牙 还要发送配对码发送进行推断! 以下是全部的源码:不 ...
随机推荐
- 470. Implement Rand10() Using Rand7() (拒绝采样Reject Sampling)
1. 问题 已提供一个Rand7()的API可以随机生成1到7的数字,使用Rand7实现Rand10,Rand10可以随机生成1到10的数字. 2. 思路 简单说: (1)通过(Rand N - 1) ...
- flask jinja的宏
form中关于表单的定义 class AreaListForm(Form): area1 = BooleanField(u'1区', default=False) area2 = BooleanFie ...
- 通过自动回复机器人学Mybatis:MySQL脚本 + db >> dao >> service >> servlet
留着参考 makeData.sql delimiter // create procedure make_data() begin declare i int ; do insert into mes ...
- 20145118 《Java程序设计》第1周学习总结
20145118 <Java程序设计>第1周学习总结 教材学习内容总结 由于寒假在家已经安装了java开发工具,所以安装过程在这里不再赘述.这一周我开始了Java初学阶段,从Java的历史 ...
- 20145325张梓靖 《Java程序设计》第4周学习总结
20145325张梓靖 <Java程序设计>第4周学习总结 教材学习内容总结 何谓继承 继承共同行为 继承基本上就是避免多个类间重复定义共同行为:可把相同的程序代码提升为父类:用关键字 e ...
- Java序列化(Serialization)
关于Java的序列化的文章在网上已经够多了,在这里写关于Java序列化的文章是对自己关于这方面的的一种总结,结合以前的开发经验与网上的资料,写了这篇文章,对自己是有着巩固记忆的作用,也希望能够对大家有 ...
- 从0开始学习 GITHUB 系列之「初识 GITHUB」【转】
本文转载自:http://stormzhang.com/github/2016/05/25/learn-github-from-zero1/ 版权声明:本文为 stormzhang 原创文章,可以随意 ...
- 基于spring boot admin 做监控的一些问题记录
问题一 各个健康节点权限问题 解决方式 加入权限模块 <dependency> <groupId>org.springframework.boot</groupId> ...
- BZOJ 2333 【SCOI2011】 棘手的操作
题目链接:棘手的操作 网上的题解大部分都是在线用可并堆艹……但是树高严格\(\log\)的可并堆我不会啊……还是离线大法好…… 我们可以先把所有的合并操作用并查集给处理好,把得到的森林记录下来.然后, ...
- BZOJ 1269 【AHOI2006】 文本编辑器
题目链接:文本编辑器 这道题没啥好说的,直接上\(Splay\)就行了,板子题…… 但是我某个地方忘了下放标记导致调了一晚上 听说有个东西叫\(rope\)可以直接过?然而我并不会 保存一发板子: # ...