Android -传统蓝牙通信聊天

概述

Android 传统蓝牙的使用,包括开关蓝牙、搜索设备、蓝牙连接、通信等。

详细

原文地址:

Android 蓝牙开发(一)蓝牙通信 CSDN

Android 蓝牙开发(一)蓝牙通信 简书

一、准备工作

 

开发环境:

jdk1.8

Eclipse Luna Service Release 1 (4.4.1)

运行环境:

华为荣耀6(Android4.4)、华为p9(Android7.0)

实现功能:

  • Android 蓝牙开发 (开关蓝牙、搜索设备、蓝牙配对、连接、通信、断开连接等)。

二、代码结构

代码包里面,有两个部分,一个是源码,一个是V7支持包。

屏幕快照 2017-07-20 下午5.54.54.png

三、程序实现-蓝牙通信

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、运行效果如下:

 

客户端

1500550538898087682.jpeg

 

服务端

Screenshot_2017-07-20-19-26-57.png

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

posted on   demo例子集  阅读(5215)  评论(0编辑  收藏  举报

(评论功能已被禁用)
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5
点击右上角即可分享
微信分享提示