经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
android蓝牙简单开发示例教程
来源:jb51  时间:2021/12/8 14:59:21  对本文有异议

概述

前段时间学习了一些蓝牙开发的知识,记录一下Android中蓝牙的简单开发。下面是最重要的两个类。

BluetoothAdapter : 蓝牙适配器,通过getDefaultAdapter ()去获取一个实例,如果设备不支持蓝牙的话,返回的是一个null对象,通过它,可以打开、关闭蓝牙,扫描设备、向指定设备创建socket通道…

BluetoothDevice : 代表一个设备对象,可以通过它获取设备的名字、地址、类型等,也可以创建匹配,建立socket通道等等。

1、权限申请

  1. <uses-permission android:name="android.permission.BLUETOOTH"/> 使用蓝牙所需要的权限
  2. <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> 使用扫描和设置蓝牙的权限(申明这一个权限必须申明上面一个权限)

Android6以上版本,扫描其他蓝牙还需要位置权限

  1. // Android 9 以下版本
  2. <user-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
  3. // Android 9 以上
  4. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

2、打开蓝牙

  1. mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  2. // 如果设备不支持蓝牙
  3. if (mBluetoothAdapter == null){
  4. return;
  5. }
  6. // 设备支持蓝牙功能,调用startActivityForResult去启动蓝牙
  7. if (!mBluetoothAdapter.isEnabled()){
  8. startBlueTooth.launch(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));
  9. }

打开蓝牙功能是通过startActivity去启动的,但是startActivity这个函数已经过期了,所以我使用官方推荐的Activity Result替代它

  1. ActivityResultLauncher<Intent> startBlueTooth = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
  2. new ActivityResultCallback<ActivityResult>() {
  3. @Override
  4. public void onActivityResult(ActivityResult result) {
  5. if (result==null){
  6. Toast.makeText(BlueToothActivity.this, "open failed", Toast.LENGTH_SHORT).show();
  7. }else {
  8. if (result.getResultCode() == RESULT_CANCELED){
  9. Toast.makeText(BlueToothActivity.this,"用户取消",Toast.LENGTH_SHORT);
  10. }
  11. }
  12. }
  13. });
  14.  

3、接收蓝牙状态的改变

通过广播去接收蓝牙状态的改变

  1. class BluetoothStateChangeReceiver extends BroadcastReceiver{
  2. public int DEFAULT_VALUE_BLUETOOTH = 1000;
  3.  
  4. @Override
  5. public void onReceive(Context context, Intent intent) {
  6. String action = intent.getAction();
  7. if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)){
  8. int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,DEFAULT_VALUE_BLUETOOTH);
  9.  
  10. switch(state){
  11. case BluetoothAdapter.STATE_ON:
  12. Log.d(TAG, "onReceive: open");
  13. break;
  14. case BluetoothAdapter.STATE_OFF:
  15. Log.d(TAG, "onReceive: off");
  16. break;
  17. case BluetoothAdapter.STATE_TURNING_ON :
  18. Log.d(TAG, "onReceive: 正在打开");
  19. break;
  20. case BluetoothAdapter.STATE_TURNING_OFF:
  21. Log.d(TAG, "onReceive: 正在关闭");
  22. break;
  23. }
  24. }
  25. }
  26. }

别忘了广播的注册和解注册

  1. IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
  2. stateReceiver = new BluetoothStateChangeReceiver() ;
  3. registerReceiver(stateReceiver,filter);

4、扫描其他的设备

同样通过广播接收,action是BluetoothDevice.ACTION_FOUND

  1. class MyReceiver extends BroadcastReceiver{
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4.  
  5. String action = intent.getAction();
  6.  
  7. if (BluetoothDevice.ACTION_FOUND.equals(action)) {
  8. // 从intent对象中获取蓝牙设备的信息
  9. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  10. // 当发现新设备不存在于配对列表中时添加
  11. if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
  12. blueNames.add(device.getName()+"\t"+device.getAddress());
  13. }
  14. blueAdpater.notifyDataSetChanged();
  15. Log.d(TAG, "onReceive: " + device.getName());
  16. }
  17. }
  18. }

动态注册广播

  1. IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
  2. registerReceiver(mReceiver,filter);

开启扫描

  1. mBluetoothAdapter.startDiscovery();

5、蓝牙配对

  1. public class BondReceiver extends BroadcastReceiver{
  2.  
  3. @Override
  4. public void onReceive(Context context, Intent intent) {
  5. if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())){
  6. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  7. switch(device.getBondState()){
  8. case BluetoothDevice.BOND_BONDED:
  9. Log.d(TAG, "onReceive: 配对完成");
  10. break;
  11. case BluetoothDevice.BOND_BONDING:
  12. Log.d(TAG, "onReceive: 正在配对");
  13. break;
  14. case BluetoothDevice.BOND_NONE:
  15. Log.d(TAG, "onReceive: 取消配对");
  16. break;
  17. }
  18. }
  19. }
  20. }

6、获取已经配对的设备

已经配对的设备会被存储起来,通过BluetoothAdpater直接获取即可

  1. Set<BluetoothDevice> paireDevices = mBluetoothAdapter.getBondedDevices();
  2. if (paireDevices.size()>0){
  3. for (BluetoothDevice pairedDevice : pairedDevices) {
  4. blueNames.add(pairedDevice.getName()+" "+pairedDevice.getAddress());
  5. Log.d(TAG, "onClick: "+pairedDevice.getName());
  6. }
  7. }

7、连接设备

想要在两台设备之间创建连接,必须实现客户端和服务端机制,他们之间使用套接字机制进行连接,服务端开放服务器套接字,客户端通过MAC地址向服务端发起连接。客户端和服务端以不同的方式获得BluetoothSocket,当客户端和服务端在同一个RFCOMM通道上分别拥有已连接的BluetoothSocket时,将他们视为彼此已经连接,于是每台设备都获得输入和输出流式传输,并开始传输数据。

连接技术

一种实现技术是自动将每台设备准备为一个服务器,从而使每台设备开放一个服务套接字并侦听连接,在此情况下,任何一台设备都可以发起与另一台设备的连接并称为客户端。

服务器

设置服务器套接字并接受连接,步骤依次如下

1、调用listenUsingRfcommWithServiceRecord()获取一个BluetoothServerSocket, 该函数需要两个参数,第一个是服务器的名称,自己取一个即可,第二个是UUID,用来对信息做唯一性标识,我们可以从网上众多UUID生成器中随机的生成一个,然后使用UUID.fromString(String)初始化一个UUID。

2、通过accept()函数开始侦听连接请求

只有远程设备发送的连接请求中UUID与使用此套接字注册的UUID相匹配时服务器才会接受请求,accept函数会返回已连接的BluetoothSocket

3、连接成功后调用close()关闭BluetoothSocket

  1. private class AcceptThread extends Thread{
  2.  
  3. private final BluetoothServerSocket mmServerSocket;
  4. private String mSocketType;
  5.  
  6. public AcceptThread(boolean secure){
  7. BluetoothServerSocket tmp = null;
  8. mSocketType = secure ? "secure" : "Insercure";
  9. try{
  10. if (secure){
  11. tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,MY_UUID_SECURE);
  12. }else{
  13. tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_INSECURE,MY_UUID_INSECURE);
  14. }
  15. } catch (IOException e) {
  16. Log.e(TAG,"socket type"+ mSocketType + "listen() failed",e);
  17. }
  18.  
  19. mmServerSocket = tmp;
  20. }
  21.  
  22. @Override
  23. public void run() {
  24. Log.d(TAG, "Socket Type: " + mSocketType +
  25. "BEGIN mAcceptThread" + this);
  26. setName("AcceptThread"+ mSocketType);
  27.  
  28. BluetoothSocket socket = null;
  29. Log.d(TAG, "run: 开始监听");
  30. while (true){
  31. try{
  32. socket = mmServerSocket.accept();
  33. Log.d("acceptThread", "run: 连接成功");
  34. connected(socket,socket.getRemoteDevice(),mSocketType);
  35. } catch (IOException e) {
  36. Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
  37. break;
  38. }
  39.  
  40. }
  41. Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
  42. }
  43.  
  44. public void cancel() {
  45. Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
  46. try {
  47. mmServerSocket.close();
  48. } catch (IOException e) {
  49. Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
  50. }
  51. }
  52. }

上面的secure和Insecure只是使用了不同的UUID而已。

客户端

远程设备开启监听后,我们就发起向此设备的连接,首先必须先获得远程设备的BluetoothDevice对象,然后获取BluetoothSocket发起连接。

基本步骤如下

1、使用BluetoothDevice通过调用createRfcommSocketToServiceRecord(UUID) 获取 BluetoothSocket

2、通过connect发起连接

  1. private class ConnectThread extends Thread{
  2. private final BluetoothSocket mmSocket;
  3. private final BluetoothDevice mmDevice;
  4. private String mSocketType;
  5.  
  6. public ConnectThread(BluetoothDevice device, boolean secure){
  7. mmDevice = device;
  8. BluetoothSocket tmp = null;
  9. mSocketType = secure ? "Secure" : "Insecure";
  10. try {
  11. if (secure){
  12. tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
  13. }else {
  14. tmp = device.createRfcommSocketToServiceRecord(MY_UUID_INSECURE);
  15. }
  16. } catch (IOException e) {
  17. Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
  18. }
  19. mmSocket = tmp;
  20. }
  21.  
  22. @Override
  23. public void run() {
  24. Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
  25. setName("ConnectThred"+mSocketType);
  26.  
  27. // 总是取消发现,因为它会减慢连接
  28. bluetoothAdapter.cancelDiscovery();
  29. // connect
  30. // Make a connection to the BluetoothSocket
  31. try {
  32. // This is a blocking call and will only return on a
  33. // successful connection or an exception
  34. mmSocket.connect();
  35. Log.d(TAG, "run: socket连接成功");
  36. } catch (IOException e) {
  37. // Close the socket
  38. Log.d(TAG, "run: 关闭socket");
  39. try {
  40. mmSocket.close();
  41. } catch (IOException e2) {
  42. Log.e(TAG, "unable to close() " + mSocketType +
  43. " socket during connection failure", e2);
  44. }
  45.  
  46. return;
  47. }
  48.  
  49. connected(mmSocket,mmDevice,mSocketType);
  50. }
  51.  
  52. public void cancel(){
  53. try{
  54. mmSocket.close();
  55. } catch (IOException e) {
  56. Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
  57. }
  58. }
  59.  
  60. }

发送数据

连接成功后,我们就可以通过socket发送数据了,客户端的Socket对象是BluetoothSocket, 服务端的socket是BluetoothServerSocket,特别注意不要混淆了。使用getInputStreamgetOutputStream分别获取通过套接字处理数据传输的InputStreamOutputStream。写数据比较简单,但是读数据就需要一个单独的线程一直监听才行。

  1. private class ConnectedThread extends Thread{
  2. private final BluetoothSocket mmSocket;
  3. private InputStream mmInStream;
  4. private OutputStream mmOutStream;
  5.  
  6. public ConnectedThread(BluetoothSocket socket, String socketType) throws IOException {
  7. Log.d(TAG, "create ConnectedThread: " + socketType);
  8. mmSocket = socket;
  9.  
  10. InputStream tmpIn = null;
  11. OutputStream tmpOut = null;
  12.  
  13. try{
  14. tmpIn = socket.getInputStream();
  15. tmpOut = socket.getOutputStream();
  16.  
  17. if (socket != null){
  18. tmpOut.write(new String("hello").getBytes());
  19. Log.d(TAG, "ConnectedThread: socket不是null");
  20. }
  21. } catch (IOException e) {
  22. Log.e(TAG,"temp socket not created", e);
  23. }
  24.  
  25. mmInStream = tmpIn;
  26. mmOutStream = tmpOut;
  27.  
  28. // mmOutStream.write(new String("hello").getBytes());
  29. }
  30.  
  31. @RequiresApi(api = Build.VERSION_CODES.KITKAT)
  32. @Override
  33. public void run() {
  34. Log.i(TAG, "BEGIN mConnectedThread");
  35. byte[] buffer = new byte[1024];
  36. int bytes;
  37.  
  38. while (true){
  39. try{
  40. bytes = mmInStream.read(buffer);
  41. // send the bytes to the ui Activity
  42. String text = encodeByteToString(buffer,bytes);
  43. Log.d(TAG, "run: 收到消息:"+ text);
  44. chatItems.add(text);
  45. mHandler.sendMessage(mHandler.obtainMessage());
  46. } catch (IOException e) {
  47. Log.d(TAG, "run: 没有收到消息");
  48. e.printStackTrace();
  49. break;
  50. }
  51. }
  52. }
  53.  
  54. public String encodeByteToString(byte[] data,int length) {
  55. byte[] temp = new byte[length];
  56. for (int i = 0; i < length; i++) {
  57. temp[i] = data[i];
  58. }
  59. try {
  60. return new String(temp,"utf-8");
  61. } catch (UnsupportedEncodingException e) {
  62. return "";
  63. }
  64. }
  65.  
  66.  
  67. public void write(byte[] buffer){
  68. try{
  69. mmOutStream.write(buffer);
  70.  
  71. // mHandler.obtainMessage(Constants.MESSAGE_WRITE,-1,-1,buffer).sendToTarget();
  72. } catch (IOException e) {
  73. e.printStackTrace();
  74. }
  75. }
  76.  
  77. public void cancel(){
  78. try{
  79. mmSocket.close();
  80. Log.d(TAG, "cancel: connectedThread");
  81. } catch (IOException e) {
  82. Log.e(TAG, "close() of connect socket failed", e);
  83. }
  84. }
  85. }

上面的例子我主要是学习官网上的蓝牙聊天项目写的代码,大家也可以直接看官网项目。从上面的例子中可知,接受到的数据流都是一些二进制,要用到实际的项目中还需要进行一定的编码和转换。也就是自己编写一些协议,学过socket编程的同学一定都懂,其实蓝牙已经有很多的好用的协议了,就比如AVRCP(Audio Video Remote Control Profile),定义了蓝牙设备和audio/video控制功能通信的特点和过程, 结合MediaSession 可以很容易的实现设备音视频控制。

到此这篇关于android蓝牙简单开发示例教程的文章就介绍到这了,更多相关android蓝牙开发内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号