Android蓝牙通信
需求:手机扫描Android设备二维码,获取蓝牙MAC地址,建立Scoket通信,发送数据。
手机客户端:
申请蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.bluetooth" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />
public void requestPermission() {
if (Build.VERSION.SDK_INT >= 23 ) {
ArrayList<String> permissionsList = new ArrayList<>();
String[] permissions = {
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
};
for (String perm : permissions) {
if (PackageManager.PERMISSION_GRANTED != checkSelfPermission(perm)) {
permissionsList.add(perm);
}
}
if (!permissionsList.isEmpty()) {
String[] strings = new String[permissionsList.size()];
requestPermissions(permissionsList.toArray(strings), 0);
}
}
}
打开蓝牙,扫描附近蓝牙设备,扫描到目标蓝牙设备 根据蓝牙MAC地址建立连接,需要配对,发送数据
private final static String TAG = "Scan";
private Button scanButton;
private BluetoothSocket bluetoothSocket;
BluetoothAdapter bluetoothAdapter;
boolean stopDiscover;
private String CONNECT_BLUETOOTH_MAC = "22:22:56:3A:00:00";
BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(receiver, filter);
if (bluetoothAdapter != null) {
if (!bluetoothAdapter.isEnabled()) {
bluetoothAdapter.enable();
}
bluetoothAdapter.startDiscovery();
}
Thread mThread = new Thread(() -> {
try {
if (bluetoothAdapter != null) {
//真我GT Neo6 SE [54:9A:8F:00:6E:18]
String deviceAddress = CONNECT_BLUETOOTH_MAC; // 目标蓝牙设备的 MAC 地址
// 使用设备的 MAC 地址获取设备对象
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(deviceAddress);
if(bluetoothDevice == null){
Log.d(TAG, "bluetoothDevice null");
}
// if (bluetoothDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
// bluetoothDevice.createBond();
// } else {
// Log.d("Bluetooth", "设备已配对");
// }
// 创建一个 RFCOMM 传输通道
bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
bluetoothAdapter.cancelDiscovery(); // 在连接之前,停止搜索设备
bluetoothSocket.connect(); // 连接到蓝牙设备
InputStream inputStream = bluetoothSocket.getInputStream();
OutputStream outputStream = bluetoothSocket.getOutputStream();
//Receive
byte[] buffer = new byte[1024];
int bytesRead;
// while (true) {
// try {
// bytesRead = inputStream.read(buffer);
// } catch (IOException e) {
// break;
// }
// }
//Send
String message = "Hello, Bluetooth Device!";
outputStream.write(message.getBytes());
}
} catch (SecurityException e) {
Log.d(TAG, e.toString());
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, e.toString());
}
});
private final BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, action);
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
String deviceName = device.getName();
String deviceAddress = device.getAddress(); // 设备的 MAC 地址
Log.d(TAG, "找到设备:" + deviceName + " [" + deviceAddress + "]");
if(CONNECT_BLUETOOTH_MAC.equals(deviceAddress)){
stopDiscover = true;
mThread.start();
}
}else if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
Log.d(TAG, "ACTION_DISCOVERY_STARTED");
}else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
Log.d(TAG, "ACTION_DISCOVERY_FINISHED");
if(!stopDiscover){
bluetoothAdapter.startDiscovery();
}
}
}
};
protected void onDestroy() {
super.onDestroy();
try {
if (bluetoothSocket != null) {
bluetoothSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (receiver != null){
unregisterReceiver(receiver);
}
stopDiscover = false;
}
Android服务端:
申请蓝牙权限,打开蓝牙,设置蓝牙可见性,根据UUID等待接受客户端scoket连接,读取数据
package com.itemp.ms66btreceiver;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
private String TAG = "BTReceiver";
private StringBuilder incoming = new StringBuilder();
private boolean listening;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestPermission();
}
@Override
protected void onStart() {
super.onStart();
startServerSocket();
}
private void startServerSocket() {
BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); // 300秒可见性
startActivity(discoverableIntent);
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
String name = "bluetoothserver";
try {
if (bluetoothAdapter != null) {
if (!bluetoothAdapter.isEnabled()) {
bluetoothAdapter.enable();
}
// bluetoothAdapter.startDiscovery();
}
final BluetoothServerSocket btserver = bluetoothAdapter.listenUsingRfcommWithServiceRecord(name, uuid);
Thread acceptThread = new Thread(() -> {
final BluetoothSocket serverSocket;
try {
serverSocket = btserver.accept();
runOnUiThread(() -> {
Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
});
listenForMessages(serverSocket, incoming);
} catch (IOException e) {
e.printStackTrace();
}
});
acceptThread.start();
} catch (SecurityException e) {
} catch (IOException e) {
Log.e(TAG, "Socket listener IO Exception", e);
}
}
private void sendMessage(BluetoothSocket socket, String message) {
OutputStream outStream;
try {
outStream = socket.getOutputStream();
byte[] byteArray = (message + " ").getBytes();
byteArray[byteArray.length - 1] = 0;
outStream.write(byteArray);
} catch (IOException e) {
}
}
private void listenForMessages(BluetoothSocket socket, final StringBuilder incoming) {
listening = true;
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
try {
InputStream instream = socket.getInputStream();
int bytesRead = -1;
while (listening) {
bytesRead = instream.read(buffer);
if (bytesRead != -1) {
String result = "";
while ((bytesRead == bufferSize) && (buffer[bufferSize - 1] != 0)) {
result = result + new String(buffer, 0, bytesRead - 1);
bytesRead = instream.read(buffer);
}
result = result + new String(buffer, 0, bytesRead - 1);
incoming.append(result);
runOnUiThread(() -> {
Log.d(TAG, "BT Receiver:" + incoming.toString());
});
}
}
socket.close();
} catch (IOException e) {
}
}
//22 22 d0 1f 05 00
public void requestPermission() {
if (Build.VERSION.SDK_INT >= 23) {
ArrayList<String> permissionsList = new ArrayList<>();
String[] permissions = {Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.BLUETOOTH_ADVERTISE,
Manifest.permission.ACCESS_FINE_LOCATION};
for (String perm : permissions) {
if (PackageManager.PERMISSION_GRANTED != checkSelfPermission(perm)) {
permissionsList.add(perm);
}
}
if (!permissionsList.isEmpty()) {
String[] strings = new String[permissionsList.size()];
requestPermissions(permissionsList.toArray(strings), 0);
}
}
}
}
查看服务端Logcat 收到客户端发送的字符串:Hello, Bluetooth Device
服务端创建BluetoothServerSocket ,BluetoothAdapter中提供了两种创建BluetoothServerSocket 方式,bluetoothDevice.createRfcommSocketToServiceRecord为创建安全的RFCOMM Bluetooth socket,该连接是安全的需要进行配对。而通过bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord创建的RFCOMM Bluetooth socket是不安全的,连接时不需要进行配对。其中的uuid需要服务器端和客户端进行统一。
客户端
客户端主要用来创建RFCOMM socket,并连接服务端。先扫描周围的蓝牙设备,如果扫描到指定设备则进行连接。mBlthChatUtil.connect(scanDevice)连接到设备,连接过程主要在ConnectThread线程中进行,先创建socket,方式有两种,如下代码中是安全的(createRfcommSocketToServiceRecord)。另一种不安全连接对应的函数是createInsecureRfcommSocketToServiceRecord。