1、在配置文件AndroidManifest.xml的<manifest></manifest>中,添加蓝牙权限如下:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2、在入口文件MainActivity中的入口函数onCreate中写一个按钮,并定义onClick函数;点击按钮后调用connectToDevice方法。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDataTextView = findViewById(R.id.data_text_view);
mConnectButton = findViewById(R.id.connect_button);
mConnectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
connectToDevice();
}
});
// 初始化 BluetoothAdapter
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Device does not support Bluetooth");
return;
}
}
2、connectToDevice方法中判断手机是否打开蓝牙,若无则请求用户允许打开,用户处理结果在onActivityResult方法中响应;蓝牙打开后,还需判断是否授予应用蓝牙权限,若无则请求用户允许授予,用户处理结果在onRequestPermissionsResult方法中响应。若蓝牙已打开且应用已被授予权限,则调用connectToDeviceUnderpermission方法。
private void connectToDevice() {
// 扫描并选择蓝牙设备
// 这里省略扫描和选择设备的逻辑,直接连接中回ZC3-U回弹仪,可以通过对话框或列表选择设备
if (!mBluetoothAdapter.isEnabled()) {
// 蓝牙未打开,请求用户打开蓝牙,对应onActivityResult方法
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
// 蓝牙已经打开,继续处理
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED | ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED | ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
//蓝牙权限未被授予,需要向用户申请权限,对应onRequestPermissionsResult方法
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH_CONNECT}, BLUETOOTH_PERMISSION_REQUEST_CODE);
} else {
//蓝牙权限已被授予,进行连接操作
connectToDeviceUnderPermission();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == RESULT_OK) {
// 用户允许打开蓝牙,继续处理
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED | ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED | ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
//蓝牙权限未被授予,需要向用户申请权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH_CONNECT}, BLUETOOTH_PERMISSION_REQUEST_CODE);
} else {
connectToDeviceUnderPermission();
}
} else {
// 用户拒绝打开蓝牙或操作失败
Log.i(TAG, "onActivityResult: 用户拒绝打开蓝牙");
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == BLUETOOTH_PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
grantResults[1] == PackageManager.PERMISSION_GRANTED) {
// 用户授予了蓝牙权限,可以执行蓝牙相关操作
connectToDeviceUnderPermission();
} else {
// 用户拒绝了蓝牙权限,可以给出适当的提示或处理
Toast.makeText(this, "蓝牙权限被拒绝,无法使用蓝牙功能", Toast.LENGTH_SHORT).show();
}
}
}
3、定义设备MAC地址常量和SPP服务特性:
private static final String MAC_ADDRESS = "7A:01:4A:FF:FF:FF";
private static final String SERVICE_CHARACTERISTIC = "00001101-0000-1000-8000-00805f9b34fb";
4、connectToDeviceUnderpermission方法中先通过设备MAC地址获取设备,若未知设备SPP服务特性则可通过设备getUuids方法获取;再根据该服务特性建立socket;定义输入输出流;启动接收数据线程;发送数据。
private void connectToDeviceUnderPermission() {
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(MAC_ADDRESS);
// 只有通过启动该程序打开蓝牙才能获取设备UUID,该UUID与SERVICE_CHARACTERISTIC一致
// 若已知悉设备SPP服务UUID则注释下面两行。
// SERVICE_CHARACTERISTIC = mBluetoothDevice.getUuids()[0].toString();
// Log.i(TAG, "deviceUUID:"+deviceUuid);
// 建立蓝牙连接
UUID uuid = UUID.fromString(SERVICE_CHARACTERISTIC); // SPP通信的UUID
try {
// 蓝牙权限已经被授予,可以连接蓝牙,建立通信套接字
mBluetoothSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(uuid);
mBluetoothSocket.connect();
// 定义输入输出流
mInputStream = mBluetoothSocket.getInputStream();
mOutputStream = mBluetoothSocket.getOutputStream();
Log.d(TAG, "Connected to device: " + mBluetoothDevice.getName());
// 连接成功后,启动接收数据的线程
Thread thread = new Thread(new ReceiveDataThread());
thread.start();
// 连接成功后,进行数据通信
// 将十六进制数组转换为字节数组
String hex = RDI_MSG;
byte[] byteArray = new byte[hex.length()/2];
//49204c6f7665204a617661 split into two characters 49, 20, 4c...
for( int i=0; i<byteArray.length; i++ ) {
//grab the hex in pairs
String output = hex.substring(2*i, (2*i + 2));
//convert hex to decimal
int decimal = Integer.parseInt(output, 16);
byteArray[i] = (byte) decimal;
}
// 发送数据
sendData(byteArray);
} catch (IOException e) {
Log.e(TAG, "Failed to connect: " + e.getMessage());
e.printStackTrace();
// 连接或读取数据失败,关闭连接并进行错误处理
try {
mBluetoothSocket.close();
} catch (IOException e2) {
Log.e(TAG, "Error closing Bluetooth socket: " + e2.getMessage());
}
}
}
5、发送数据sendData:
private void sendData(byte[] data) {
try {
mOutputStream.write(data);
mOutputStream.flush();
Log.d(TAG, "Data sent: " + Arrays.toString(data));
} catch (IOException e) {
Log.e(TAG, "Failed to send data: " + e.getMessage());
}
}
6、接收数据ReceiveDataThread,并将接收的数据内容显示在textView中:
private class ReceiveDataThread implements Runnable {
@Override
public void run() {
byte[] buffer = new byte[1024];
int bytes;
while (true) {
try {
Log.d(TAG, "run: receive start");
bytes = mInputStream.read(buffer);
Log.d(TAG, "run: receive get");
Log.d(TAG, "run: "+bytes);
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
onDestroy();
} catch (IOException e) {
Log.e(TAG, "Error reading data: " + e.getMessage());
break;
}
}
}
}
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == MESSAGE_READ) {
byte[] buffer = (byte[]) msg.obj;
String receivedData = new String(buffer, 0, msg.arg1);
Log.d(TAG, "handleMessage: "+receivedData);
mDataTextView.setText(receivedData);
}
return true;
}
});
@Override
protected void onDestroy() {
super.onDestroy();
// 关闭连接和释放资源
try {
if (mInputStream != null) {
mInputStream.close();
}
if (mOutputStream != null) {
mOutputStream.close();
}
if (mBluetoothSocket != null) {
mBluetoothSocket.close();
}
} catch (IOException e) {
Log.e(TAG, "Error closing Bluetooth socket: " + e.getMessage());
}
}
7、连接真机进行测试,点击按钮可以看到对应文本内容显示为返回数据。