ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Android蓝牙通信

2021-12-13 12:58:35  阅读:101  来源: 互联网

标签:socket 蓝牙 通信 private final Android public 设备


Android 平台包含蓝牙网络堆栈支持,此支持能让设备以无线方式与其他蓝牙设备交换数据。应用框架提供通过 Android Bluetooth API 访问蓝牙功能的权限。这些 API 允许应用以无线方式连接到其他蓝牙设备,从而实现点到点和多点无线功能。
使用蓝牙进行通信的四大必需任务:设置蓝牙、查找局部区域内的配对设备或可用设备、连接设备,以及在设备之间传输数据。
为了让支持蓝牙的设备能够在彼此之间传输数据,它们必须先通过配对过程形成通信通道。其中一台设备(可检测到的设备)需将自身设置为可接收传入的连接请求。另一台设备会使用服务发现过程找到此可检测到的设备。在可检测到的设备接受配对请求后,这两台设备会完成绑定过程,并在此期间交换安全密钥。二者会缓存这些密钥,以供日后使用。完成配对和绑定过程后,两台设备会交换信息。当会话完成时,发起配对请求的设备会发布已将其链接到可检测设备的通道。但是,这两台设备仍保持绑定状态,因此在未来的会话期间,只要二者在彼此的范围内且均未移除绑定,便可自动重新连接。

Android 应用可通过 Bluetooth API 执行以下操作:
1、扫描其他蓝牙设备
2、查询本地蓝牙适配器的配对蓝牙设备
3、建立 RFCOMM 通道
4、通过服务发现连接到其他设备
5、与其他设备进行双向数据传输
6、管理多个连接

关键类和接口:
android.bluetooth 包中提供所有 Bluetooth API。以下概要列出了创建蓝牙连接所需的类和接口:
BluetoothAdapter
表示本地蓝牙适配器(蓝牙无线装置)。BluetoothAdapter 是所有蓝牙交互的入口点。借助该类,您可以发现其他蓝牙设备、查询已(已配对)设备的列表、使用已知的 MAC 地址实例化 BluetoothDevice,以及通过创建 BluetoothServerSocket 侦听来自其他设备的通信。
BluetoothDevice
表示远程蓝牙设备。借助该类,您可以通过 BluetoothSocket 请求与某个远程设备建立连接,或查询有关该设备的信息,例如设备的名称、地址、类和绑定状态等。
BluetoothSocket
表示蓝牙套接字接口(类似于 TCP Socket)。这是允许应用使用 InputStream 和 OutputStream 与其他蓝牙设备交换数据的连接点。
BluetoothServerSocket
表示用于侦听传入请求的开放服务器套接字(类似于 TCP ServerSocket)。如要连接两台 Android 设备,其中一台设备必须使用此类开放一个服务器套接字。当远程蓝牙设备向此设备发出连接请求时,该设备接受连接,然后返回已连接的 BluetoothSocket。
BluetoothClass
描述蓝牙设备的一般特征和功能。这是一组只读属性,用于定义设备的类和服务。虽然这些信息会提供关于设备类型的有用提示,但该类的属性未必描述设备支持的所有蓝牙配置文件和服务。
BluetoothProfile
表示蓝牙配置文件的接口。蓝牙配置文件是适用于设备间蓝牙通信的无线接口规范。举个例子:免提配置文件。如需了解有关配置文件的详细讨论,请参阅使用配置文件。
BluetoothHeadset
提供蓝牙耳机支持,以便与手机配合使用。这包括蓝牙耳机配置文件和免提 (v1.5) 配置文件。
BluetoothA2dp
定义如何使用蓝牙立体声音频传输配置文件 (A2DP),通过蓝牙连接将高质量音频从一个设备流式传输至另一个设备。
BluetoothHealth
表示用于控制蓝牙服务的健康设备配置文件代理。
BluetoothHealthCallback
用于实现 BluetoothHealth 回调的抽象类。您必须扩展此类并实现回调方法,以接收关于应用注册状态和蓝牙通道状态变化的更新内容。
BluetoothHealthAppConfiguration
表示第三方蓝牙健康应用注册的应用配置,该配置旨在实现与远程蓝牙健康设备的通信。
BluetoothProfile.ServiceListener
当 BluetoothProfile 进程间通信 (IPC) 客户端连接到运行特定配置文件的内部服务或断开该服务连接时,向该客户端发送通知的接口。

基本操作代码:

1、设置蓝牙:

获取 BluetoothAdapter:
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
// Device doesn’t support Bluetooth
}
启用蓝牙:
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

2、查询已配对设备:
Set pairedDevices = bluetoothAdapter.getBondedDevices();

if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
for (BluetoothDevice device : pairedDevices) {
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
}
}

3、发现设备:
@Override
protected void onCreate(Bundle savedInstanceState) {

// Register for broadcasts when a device is discovered.
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(receiver, filter);

}

// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
}
}
};

@Override
protected void onDestroy() {
super.onDestroy();

// Don't forget to unregister the ACTION_FOUND receiver.
unregisterReceiver(receiver);

}

4、连接设备:
作为服务器连接:

private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;

public AcceptThread() {
    // Use a temporary object that is later assigned to mmServerSocket
    // because mmServerSocket is final.
    BluetoothServerSocket tmp = null;
    try {
        // MY_UUID is the app's UUID string, also used by the client code.
        tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
    } catch (IOException e) {
        Log.e(TAG, "Socket's listen() method failed", e);
    }
    mmServerSocket = tmp;
}

public void run() {
    BluetoothSocket socket = null;
    // Keep listening until exception occurs or a socket is returned.
    while (true) {
        try {
            socket = mmServerSocket.accept();
        } catch (IOException e) {
            Log.e(TAG, "Socket's accept() method failed", e);
            break;
        }

        if (socket != null) {
            // A connection was accepted. Perform work associated with
            // the connection in a separate thread.
            manageMyConnectedSocket(socket);
            mmServerSocket.close();
            break;
        }
    }
}

// Closes the connect socket and causes the thread to finish.
public void cancel() {
    try {
        mmServerSocket.close();
    } catch (IOException e) {
        Log.e(TAG, "Could not close the connect socket", e);
    }
}

}

作为客户端连接:

private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;

public ConnectThread(BluetoothDevice device) {
    // Use a temporary object that is later assigned to mmSocket
    // because mmSocket is final.
    BluetoothSocket tmp = null;
    mmDevice = device;

    try {
        // Get a BluetoothSocket to connect with the given BluetoothDevice.
        // MY_UUID is the app's UUID string, also used in the server code.
        tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
    } catch (IOException e) {
        Log.e(TAG, "Socket's create() method failed", e);
    }
    mmSocket = tmp;
}

public void run() {
    // Cancel discovery because it otherwise slows down the connection.
    bluetoothAdapter.cancelDiscovery();

    try {
        // Connect to the remote device through the socket. This call blocks
        // until it succeeds or throws an exception.
        mmSocket.connect();
    } catch (IOException connectException) {
        // Unable to connect; close the socket and return.
        try {
            mmSocket.close();
        } catch (IOException closeException) {
            Log.e(TAG, "Could not close the client socket", closeException);
        }
        return;
    }

    // The connection attempt succeeded. Perform work associated with
    // the connection in a separate thread.
    manageMyConnectedSocket(mmSocket);
}

// Closes the client socket and causes the thread to finish.
public void cancel() {
    try {
        mmSocket.close();
    } catch (IOException e) {
        Log.e(TAG, "Could not close the client socket", e);
    }
}

}

5、管理连接:

public class MyBluetoothService {
private static final String TAG = “MY_APP_DEBUG_TAG”;
private Handler handler; // handler that gets info from Bluetooth service

// Defines several constants used when transmitting messages between the
// service and the UI.
private interface MessageConstants {
    public static final int MESSAGE_READ = 0;
    public static final int MESSAGE_WRITE = 1;
    public static final int MESSAGE_TOAST = 2;

    // ... (Add other message types here as needed.)
}

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;
    private byte[] mmBuffer; // mmBuffer store for the stream

    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the input and output streams; using temp objects because
        // member streams are final.
        try {
            tmpIn = socket.getInputStream();
        } catch (IOException e) {
            Log.e(TAG, "Error occurred when creating input stream", e);
        }
        try {
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
            Log.e(TAG, "Error occurred when creating output stream", e);
        }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        mmBuffer = new byte[1024];
        int numBytes; // bytes returned from read()

        // Keep listening to the InputStream until an exception occurs.
        while (true) {
            try {
                // Read from the InputStream.
                numBytes = mmInStream.read(mmBuffer);
                // Send the obtained bytes to the UI activity.
                Message readMsg = handler.obtainMessage(
                        MessageConstants.MESSAGE_READ, numBytes, -1,
                        mmBuffer);
                readMsg.sendToTarget();
            } catch (IOException e) {
                Log.d(TAG, "Input stream was disconnected", e);
                break;
            }
        }
    }

    // Call this from the main activity to send data to the remote device.
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);

            // Share the sent message with the UI activity.
            Message writtenMsg = handler.obtainMessage(
                    MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer);
            writtenMsg.sendToTarget();
        } catch (IOException e) {
            Log.e(TAG, "Error occurred when sending data", e);

            // Send a failure message back to the activity.
            Message writeErrorMsg =
                    handler.obtainMessage(MessageConstants.MESSAGE_TOAST);
            Bundle bundle = new Bundle();
            bundle.putString("toast",
                    "Couldn't send data to the other device");
            writeErrorMsg.setData(bundle);
            handler.sendMessage(writeErrorMsg);
        }
    }

    // Call this method from the main activity to shut down the connection.
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "Could not close the connect socket", e);
        }
    }
}

}

通信原理:
蓝牙通信和socket通信原理基本上是一致的,socket通信原理如下:
在这里插入图片描述
蓝牙客户端Socket流程如下:

1、创建客户端蓝牙Sokcet
2、创建连接
3、读写数据
4、关闭

服务端socket:

1、创建服务端蓝牙Socket
2、绑定端口号(蓝牙忽略)
3、创建监听listen(蓝牙忽略, 蓝牙没有此监听,而是通过whlie(true)死循环来一直监听的)
4、通过accept(),如果有客户端连接,会创建一个新的Socket,体现出并发性,可以同时与多个socket通讯)
5、读写数据
6、关闭

权限设置:

<manifest … >

...

客户端:
public class ConnectThread extends Thread{

	private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
	/** 客户端socket*/    
	private final BluetoothSocket mmSoket;
	 /** 要连接的设备*/    
	private final BluetoothDevice mmDevice;
    private BluetoothAdapter mBluetoothAdapter;
	 /** 主线程通信的Handler*/   
    private final Handler mHandler;
    /** 发送和接收数据的处理类*/   
	private ConnectedThread mConnectedThread;
	
    public ConnectThread(BluetoothDevice device, BluetoothAdapter bluetoothAdapter, Handler mUIhandler) {
    mmDevice = device;
    mBluetoothAdapter = bluetoothAdapter;
    mHandler = mUIhandler;
    BluetoothSocket tmp = null;
    try {
        // 创建客户端Socket
        tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
    } catch (IOException e) {
        e.printStackTrace();
    }
    mmSoket = tmp;
    }
    
   @Override    
   public void run() {
    	super.run();
    // 关闭正在发现设备.(如果此时又在查找设备,又在发送数据,会有冲突,影响传输效率)
    mBluetoothAdapter.cancelDiscovery();
    try {
        // 连接服务器
        mmSoket.connect();
    } catch (IOException e) {
        // 连接异常就关闭
        try {
            mmSoket.close();
        } catch (IOException e1) {
        }
        return;
    }
    manageConnectedSocket(mmSoket);
}

private void manageConnectedSocket(BluetoothSocket mmSoket) {
    // 通知主线程连接上了服务端socket,更新UI
    mHandler.sendEmptyMessage(Constant.MSG_CONNECTED_TO_SERVER);
    // 新建一个线程进行通讯,不然会发现线程堵塞
    mConnectedThread = new ConnectedThread(mmSoket,mHandler);
    mConnectedThread.start();
}

/**
* 关闭当前客户端
*/  
 public void cancle() {
    try {
        mmSoket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
 }
 
/**    * 发送数据    * @paramdata    */
public void sendData(byte[] data) {
    if(mConnectedThread != null) {
        mConnectedThread.write(data);
    }
}

}

服务器端:

public class AccepThread extendsThread{
	/** 连接的名称*/ 
	 private static final String NAME = "BluetoothClass";
	/** UUID*/    
	private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
	 /** 服务端蓝牙Sokcet*/    
	private final BluetoothServerSocket mmServerSocket;
	private final BluetoothAdapter mBluetoothAdapter;
	 /** 线程中通信的更新UI的Handler*/    
	private final Handler mHandler;
	 /** 监听到有客户端连接,新建一个线程单独处理,不然在此线程中会堵塞*/   
	private ConnectedThread mConnectedThread;

	public AccepThread(BluetoothAdapter adapter, Handler handler) throws IOException {
  	 	 mBluetoothAdapter = adapter;
  		  this.mHandler = handler;
    	// 获取服务端蓝牙socket       
		mmServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
	 }
	 
	 @Override
	  public void run() {
    		super.run();
    		// 连接的客户端soacket
    		BluetoothSocket socket = null;
    		// 服务端是不退出的,要一直监听连接进来的客户端,所以是死循环
    		while (true){
     		   // 通知主线程更新UI,客户端开始监听
     		   mHandler.sendEmptyMessage(Constant.MSG_START_LISTENING);
     		   try {
        		    // 获取连接的客户端socket
         		   socket =  mmServerSocket.accept();
      		  } catch (IOException e) {
        		    // 通知主线程更新UI, 获取异常
       		     mHandler.sendEmptyMessage(Constant.MSG_ERROR);
      		      e.printStackTrace();
      		      // 服务端退出一直监听线程
      		      break;
  		      }
        if(socket != null) {
            // 管理连接的客户端socket
            manageConnectSocket(socket);
            // 这里应该是手动断开,案例应该是只保证连接一个客户端,所以连接完以后,关闭了服务端socket
            try {
                mmServerSocket.close();
               mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
            } catch (IOException e) {
				e.printStackTrace();           
		   }           
	   }
     }
   }

/**    * 管理连接的客户端socket    
  • @paramsocket */
    private void manageConnectSocket(BluetoothSocket socket) {
    // 只支持同时处理一个连接
    // mConnectedThread不为空,踢掉之前的客户端
    if(mConnectedThread != null) {
    mConnectedThread.cancle();
    }
    // 主线程更新UI,连接到了一个客户端
    mHandler.sendEmptyMessage(Constant.MSG_GOT_A_CLINET);
    // 新建一个线程,处理客户端发来的数据
    mConnectedThread = new ConnectedThread(socket, mHandler);
    mConnectedThread.start();
    }

    /*断开服务端,结束监听 /

    public void cancle() {
    try {
    mmServerSocket.close();
    mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    /** * 发送数据

    • @paramdata */
      public void sendData(byte[] data){
      if(mConnectedThread != null) {
      mConnectedThread.write(data);
      }
      }
      }

共同通讯处理类ConnectedThread:

public class ConnectedThread extends Thread{
	/** 当前连接的客户端BluetoothSocket*/
	private final BluetoothSocket mmSokcet;
	/** 读取数据流*/
	private final InputStream mmInputStream;
	/** 发送数据流*/
	private final OutputStream mmOutputStream;
	/** 与主线程通信Handler*/
	private Handler mHandler;
	private String TAG ="ConnectedThread";
	public ConnectedThread(BluetoothSocket socket,Handler handler) {
		 mmSokcet = socket; mHandler = handler;
		 InputStream tmpIn =null;
		 OutputStream tmpOut =null;
		try{ 
  			   tmpIn = socket.getInputStream();
 			    tmpOut = socket.getOutputStream(); 
		 }catch(IOException e) {
		 e.printStackTrace();
		 }
		 mmInputStream = tmpIn; 
		 mmOutputStream = tmpOut;
	 }
	@Override
	public void run() {
		super.run();
      byte[] buffer =new byte[1024];
  	  while(true) {
 		   try{
			// 读取数据
            int bytes = mmInputStream.read(buffer);
    	    if(bytes >0) {
    	   String data =new String(buffer,0,bytes,"utf-8");
    		// 把数据发送到主线程, 此处还可以用广播
    		Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA,data); 
     		mHandler.sendMessage(message); 
			}
    		 Log.d(TAG,"messge size :"+ bytes);
            }catch(IOException e){
             e.printStackTrace();
      	   } 
     } 
}
// 踢掉当前客户端
public void cancle() {
            try{ 
                mmSokcet.close();
          }catch(IOException e) { 
         e.printStackTrace(); 
     } 
}
/** * 服务端发送数据 * 
	@paramdata */
public void write(byte[] data) {
             try{ 
                     mmOutputStream.write(data);
               }catch(IOException e) { 
                 e.printStackTrace(); 
             } 
 }

}

链接:
https://developer.android.google.cn/guide/topics/connectivity/bluetooth
http://www.android-doc.com/guide/topics/connectivity/bluetooth.html
https://www.jianshu.com/p/8fbbc6723a7c
https://blog.csdn.net/weixin_39079048/article/details/78923669

标签:socket,蓝牙,通信,private,final,Android,public,设备
来源: https://blog.csdn.net/qq_51771271/article/details/121902530

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有