Android -传统蓝牙通信聊天

矫情吗;* 2022-05-31 09:53 381阅读 0赞

概述

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

详细

代码下载:http://www.demodashi.com/demo/10676.html

原文地址:

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. <uses-permissionandroid:name="Android.permission.BLUETOOTH" />
  2. //允许程序连接到已配对的蓝牙设备。
  3. <uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
  4. //允许程序发现和配对蓝牙设备。

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. if(!mBluetoothAdapter.isEnabled()){
  2. //弹出对话框提示用户是后打开
  3. Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  4. startActivityForResult(enabler, REQUEST_ENABLE);
  5. //不做提示,直接打开,不建议用下面的方法,有的手机会有问题。
  6. // mBluetoothAdapter.enable();
  7. }

获取本地蓝牙信息

  1. //获取本机蓝牙名称
  2. String name = mBluetoothAdapter.getName();
  3. //获取本机蓝牙地址
  4. String address = mBluetoothAdapter.getAddress();
  5. Log.d(TAG,"bluetooth name ="+name+" address ="+address);
  6. //获取已配对蓝牙设备
  7. Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
  8. Log.d(TAG, "bonded device size ="+devices.size());
  9. for(BluetoothDevice bonddevice:devices){
  10. Log.d(TAG, "bonded device name ="+bonddevice.getName()+" address"+bonddevice.getAddress());
  11. }

搜索设备

  1. mBluetoothAdapter.startDiscovery();

停止搜索

  1. mBluetoothAdapter.cancelDiscovery();

搜索蓝牙设备,该过程是异步的,通过下面注册广播接受者,可以监听是否搜到设备。

  1. IntentFilter filter = new IntentFilter();
  2. //发现设备
  3. filter.addAction(BluetoothDevice.ACTION_FOUND);
  4. //设备连接状态改变
  5. filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
  6. //蓝牙设备状态改变
  7. filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
  8. registerReceiver(mBluetoothReceiver, filter);

监听扫描结果

通过广播接收者查看扫描到的蓝牙设备,每扫描到一个设备,系统都会发送此广播(BluetoothDevice.ACTION_FOUNDE)。其中参数intent可以获取蓝牙设备BluetoothDevice。

该demo中是连接指定名称的蓝牙设备,BLUETOOTH_NAME为”Galaxy Nexus”,如果扫描不到,记得改这个蓝牙名称。

  1. private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4. String action = intent.getAction();
  5. Log.d(TAG,"mBluetoothReceiver action ="+action);
  6. if(BluetoothDevice.ACTION_FOUND.equals(action)){//每扫描到一个设备,系统都会发送此广播。
  7. //获取蓝牙设备
  8. BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  9. if(scanDevice == null || scanDevice.getName() == null) return;
  10. Log.d(TAG, "name="+scanDevice.getName()+"address="+scanDevice.getAddress());
  11. //蓝牙设备名称
  12. String name = scanDevice.getName();
  13. if(name != null && name.equals(BLUETOOTH_NAME)){
  14. mBluetoothAdapter.cancelDiscovery();
  15. //取消扫描
  16. mProgressDialog.setTitle(getResources().getString(R.string.progress_connecting)); //连接到设备。
  17. mBlthChatUtil.connect(scanDevice);
  18. }
  19. }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
  20. }
  21. }
  22. };

设置蓝牙可见性

有时候扫描不到某设备,这是因为该设备对外不可见或者距离远,需要设备该蓝牙可见,这样该才能被搜索到。

可见时间默认值为120s,最多可设置300。

  1. if (mBluetoothAdapter.isEnabled()) {
  2. if (mBluetoothAdapter.getScanMode() !=
  3. BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
  4. Intent discoverableIntent = new Intent(
  5. BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
  6. discoverableIntent.putExtra(
  7. BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
  8. startActivity(discoverableIntent);
  9. }
  10. }

2 服务端

android 蓝牙之间可以通过SDP协议建立连接进行通信,通信方式类似于平常使用socket。

首先创建BluetoothServerSocket ,BluetoothAdapter中提供了两种创建BluetoothServerSocket 方式,如下图所示为创建安全的RFCOMM Bluetooth socket,该连接是安全的需要进行配对。而通过listenUsingInsecureRfcommWithServiceRecord创建的RFCOMM Bluetooth socket是不安全的,连接时不需要进行配对。

其中的uuid需要服务器端和客户端进行统一。

  1. private class AcceptThread extends Thread {
  2. // 本地服务器套接字
  3. private final BluetoothServerSocket mServerSocket;
  4. public AcceptThread() {
  5. BluetoothServerSocket tmp = null;
  6. // 创建一个新的侦听服务器套接字
  7. try {
  8. tmp = mAdapter.listenUsingRfcommWithServiceRecord(
  9. SERVICE_NAME, SERVICE_UUID);
  10. //tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID);
  11. } catch (IOException e) {
  12. Log.e(TAG, "listen() failed", e);
  13. }
  14. mServerSocket = tmp;
  15. }
  16. public void run() {
  17. BluetoothSocket socket = null;
  18. // 循环,直到连接成功
  19. while (mState != STATE_CONNECTED) {
  20. try {
  21. // 这是一个阻塞调用 返回成功的连接
  22. // mServerSocket.close()在另一个线程中调用,可以中止该阻塞
  23. socket = mServerSocket.accept();
  24. } catch (IOException e) {
  25. Log.e(TAG, "accept() failed", e);
  26. break;
  27. }
  28. // 如果连接被接受
  29. if (socket != null) {
  30. synchronized (BluetoothChatUtil.this) {
  31. switch (mState) {
  32. case STATE_LISTEN:
  33. case STATE_CONNECTING:
  34. // 正常情况。启动ConnectedThread。
  35. connected(socket, socket.getRemoteDevice());
  36. break;
  37. case STATE_NONE:
  38. case STATE_CONNECTED:
  39. // 没有准备或已连接。新连接终止。
  40. try {
  41. socket.close();
  42. } catch (IOException e) {
  43. Log.e(TAG, "Could not close unwanted socket", e);
  44. }
  45. break;
  46. }
  47. }
  48. }
  49. }
  50. if (D) Log.i(TAG, "END mAcceptThread");
  51. }
  52. public void cancel() {
  53. if (D) Log.d(TAG, "cancel " + this);
  54. try {
  55. mServerSocket.close();
  56. } catch (IOException e) {
  57. Log.e(TAG, "close() of server failed", e);
  58. }
  59. }
  60. }

mServerSocket通过accept()等待客户端的连接(阻塞),直到连接成功或失败。

3 客户端

客户端主要用来创建RFCOMM socket,并连接服务端。

先扫描周围的蓝牙设备,如果扫描到指定设备则进行连接。mBlthChatUtil.connect(scanDevice)连接到设备,

连接过程主要在ConnectThread线程中进行,先创建socket,方式有两种,

如下代码中是安全的(createRfcommSocketToServiceRecord)。另一种不安全连接对应的函数是createInsecureRfcommSocketToServiceRecord。

  1. private class ConnectThread extends Thread {
  2. private BluetoothSocket mmSocket;
  3. private final BluetoothDevice mmDevice;
  4. public ConnectThread(BluetoothDevice device) {
  5. mmDevice = device;
  6. BluetoothSocket tmp = null;
  7. // 得到一个bluetoothsocket
  8. try {
  9. mmSocket = device.createRfcommSocketToServiceRecord
  10. (SERVICE_UUID);
  11. } catch (IOException e) {
  12. Log.e(TAG, "create() failed", e);
  13. mmSocket = null;
  14. }
  15. }
  16. public void run() {
  17. Log.i(TAG, "BEGIN mConnectThread");
  18. try {
  19. // socket 连接,该调用会阻塞,直到连接成功或失败
  20. mmSocket.connect();
  21. } catch (IOException e) {
  22. connectionFailed();
  23. try {//关闭这个socket
  24. mmSocket.close();
  25. } catch (IOException e2) {
  26. e2.printStackTrace();
  27. }
  28. return;
  29. }
  30. // 启动连接线程
  31. connected(mmSocket, mmDevice);
  32. }
  33. public void cancel() {
  34. try {
  35. mmSocket.close();
  36. } catch (IOException e) {
  37. Log.e(TAG, "close() of connect socket failed", e);
  38. }
  39. }
  40. }

接着客户端socket主动连接服务端。连接过程中会自动进行配对,需要双方同意才可以连接成功。

4 数据传输

客户端与服务端连接成功后都会调用connected(mmSocket, mmDevice),创建一个ConnectedThread线程()。

该线程主要用来接收和发送数据。客户端和服务端处理方式一样。该线程通过socket获得输入输出流。

private InputStream mmInStream = socket.getInputStream();

private OutputStream mmOutStream =socket.getOutputStream();

发送数据

  1. public void write(byte[] buffer) {
  2. try {
  3. mmOutStream.write(buffer);
  4. // 分享发送的信息到Activity
  5. mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer)
  6. .sendToTarget();
  7. } catch (IOException e) {
  8. Log.e(TAG, "Exception during write", e);
  9. }
  10. }

接收数据

线程循环进行接收数据。

  1. public void run() {
  2. // 监听输入流
  3. while (true) {
  4. try {
  5. byte[] buffer = new byte[1024];
  6. // 读取输入流
  7. int bytes = mmInStream.read(buffer);
  8. // 发送获得的字节的ui activity
  9. Message msg = mHandler.obtainMessage(MESSAGE_READ);
  10. Bundle bundle = new Bundle();
  11. bundle.putByteArray(READ_MSG, buffer);
  12. msg.setData(bundle);
  13. mHandler.sendMessage(msg);
  14. } catch (IOException e) {
  15. Log.e(TAG, "disconnected", e);
  16. connectionLost();
  17. break;
  18. }
  19. }
  20. }

四、运行效果

1、运行,右键项目:Run as -》Android Application (备注:Eclipse需要配置Android开发环境)

2、运行效果如下:

客户端

1500550538898087682.jpeg

服务端

Screenshot\_2017-07-20-19-26-57.png

注:本文著作权归作者,由demo大师(http://www.demodashi.com)宣传,拒绝转载,转载需要作者授权

发表评论

表情:
评论列表 (有 0 条评论,381人围观)

还没有评论,来说两句吧...

相关阅读