参考文章:http://t.csdnimg.cn/hVDdT
Android-蓝牙开发:解决搜索不到蓝牙设备_android项目中无法搜索到蓝牙-CSDN博客
第一篇是参考代码来源,第二篇是解决第一篇出现的搜索不到设备的问题。
演示视频:【第三步:成功啦!】 https://www.bilibili.com/video/BV1KJ4m1A7LQ/?share_source=copy_web&vd_source=feda4a3e701cae91ddc70f622b5b48d6
纠正视频里面的话,原帖主肯定是明白原理,但是我本人是照抄代码,没有搞清楚原理,因此是我自己的问题,如果有明白为什么会出现我视频里面问题的朋友可以留言告知我,我一定改正!!
根据第一篇文章提供的源码链接下载后复制到自己的项目中,解决各种报错,并在主代码中添加第二篇文章提供的代码并调用getPermission()后,即可运行。
activity
bluet.java
package com.example.yueduqi;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.yueduqi.util.ClsUtils;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
public class bluet extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "BtMain";
private static final int CONNECT_SUCCESS = 0x01;
private static final int CONNECT_FAILURE = 0x02;
private static final int DISCONNECT_SUCCESS = 0x03;
private static final int SEND_SUCCESS = 0x04;
private static final int SEND_FAILURE = 0x05;
private static final int RECEIVE_SUCCESS = 0x06;
private static final int RECEIVE_FAILURE = 0x07;
private static final int START_DISCOVERY = 0x08;
private static final int STOP_DISCOVERY = 0x09;
private static final int DISCOVERY_DEVICE = 0x0A;
private static final int DEVICE_BOND_NONE = 0x0B;
private static final int DEVICE_BONDING = 0x0C;
private static final int DEVICE_BONDED = 0x0D;
private Button btSearch;
private TextView tvCurConState;
private TextView tvCurBondState;
private TextView tvName;
private TextView tvAddress;
private Button btConnect;
private Button btDisconnect;
private Button btBound;
private Button btDisBound;
private EditText etSendMsg;
private Button btSend;
private TextView tvSendResult;
private TextView tvReceive;
private LinearLayout llDeviceList;
private LinearLayout llDataSendReceive;
private ListView lvDevices;
private LVDevicesAdapter lvDevicesAdapter;
//蓝牙
private BluetoothAdapter bluetoothAdapter;
private BtBroadcastReceiver btBroadcastReceiver;
//连接设备的UUID
public static final String MY_BLUETOOTH_UUID = "00001101-0000-1000-8000-00805F9B34FB"; //蓝牙通讯
//当前要连接的设备
private BluetoothDevice curBluetoothDevice;
//发起连接的线程
private ConnectThread connectThread;
//管理连接的线程
private ConnectedThread connectedThread;
//当前设备连接状态
private boolean curConnState = false;
//当前设备与系统配对状态
private boolean curBondState = false;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case START_DISCOVERY:
Log.d(TAG, "开始搜索设备...");
break;
case STOP_DISCOVERY:
Log.d(TAG, "停止搜索设备...");
break;
case DISCOVERY_DEVICE: //扫描到设备
BluetoothDevice bluetoothDevice = (BluetoothDevice) msg.obj;
lvDevicesAdapter.addDevice(bluetoothDevice);
break;
case CONNECT_FAILURE: //连接失败
Log.d(TAG, "连接失败");
tvCurConState.setText("连接失败");
curConnState = false;
break;
case CONNECT_SUCCESS: //连接成功
Log.d(TAG, "连接成功");
tvCurConState.setText("连接成功");
curConnState = true;
llDataSendReceive.setVisibility(View.VISIBLE);
llDeviceList.setVisibility(View.GONE);
break;
case DISCONNECT_SUCCESS:
tvCurConState.setText("断开成功");
curConnState = false;
break;
case SEND_FAILURE: //发送失败
Toast.makeText(bluet.this, "发送失败", Toast.LENGTH_SHORT).show();
break;
case SEND_SUCCESS: //发送成功
String sendResult = (String) msg.obj;
tvSendResult.setText(sendResult);
break;
case RECEIVE_FAILURE: //接收失败
String receiveError = (String) msg.obj;
tvReceive.setText(receiveError);
break;
case RECEIVE_SUCCESS: //接收成功
String receiveResult = (String) msg.obj;
tvReceive.setText(receiveResult);
break;
case DEVICE_BOND_NONE: //已解除配对
tvCurBondState.setText("解除配对成功");
curBondState = false;
break;
case DEVICE_BONDING: //正在配对
tvCurBondState.setText("正在配对...");
break;
case DEVICE_BONDED: //已配对
tvCurBondState.setText("配对成功");
curBondState = true;
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bluetooth);
//初始化视图
initView();
//初始化监听
iniListener();
//初始化数据
initData();
//初始化蓝牙
initBluetooth();
//初始化蓝牙广播
initBtBroadcast();
//安卓版本升级蓝牙连接需要获取位置
getPermission();
}
private void initData() {
lvDevicesAdapter = new LVDevicesAdapter(bluet.this);
lvDevices.setAdapter(lvDevicesAdapter);
}
private void initView() {
btSearch = findViewById(R.id.bt_search);
tvCurConState = findViewById(R.id.tv_cur_con_state);
tvCurBondState = findViewById(R.id.tv_cur_bond_state);
btConnect = findViewById(R.id.bt_connect);
btDisconnect = findViewById(R.id.bt_disconnect);
btBound = findViewById(R.id.bt_bound);
btDisBound = findViewById(R.id.bt_disBound);
tvName = findViewById(R.id.tv_name);
tvAddress = findViewById(R.id.tv_address);
etSendMsg = findViewById(R.id.et_send_msg);
btSend = findViewById(R.id.bt_to_send);
tvSendResult = findViewById(R.id.tv_send_result);
tvReceive = findViewById(R.id.tv_receive_result);
llDeviceList = findViewById(R.id.ll_device_list);
llDataSendReceive = findViewById(R.id.ll_data_send_receive);
lvDevices = findViewById(R.id.lv_devices);
}
private void iniListener() {
btSearch.setOnClickListener(this);
btConnect.setOnClickListener(this);
btDisconnect.setOnClickListener(this);
btBound.setOnClickListener(this);
btDisBound.setOnClickListener(this);
btSend.setOnClickListener(this);
lvDevices.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
BluetoothDevice bluetoothDevice = (BluetoothDevice) lvDevicesAdapter.getItem(i);
if (ActivityCompat.checkSelfPermission(bluet.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
tvName.setText(bluetoothDevice.getName());
tvAddress.setText(bluetoothDevice.getAddress());
curBluetoothDevice = bluetoothDevice;
}
});
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.bt_search) { // 搜索蓝牙
llDataSendReceive.setVisibility(View.GONE);
llDeviceList.setVisibility(View.VISIBLE);
searchBtDevice();
} else if (view.getId() == R.id.bt_connect) { // 连接蓝牙
if (!curConnState) {
startConnectDevice(curBluetoothDevice, MY_BLUETOOTH_UUID, 10000);
} else {
Toast.makeText(this, "当前设备已连接", Toast.LENGTH_SHORT).show();
}
} else if (view.getId() == R.id.bt_disconnect) { // 断开连接
if (curConnState) {
clearConnectedThread();
} else {
Toast.makeText(this, "当前设备未连接", Toast.LENGTH_SHORT).show();
}
} else if (view.getId() == R.id.bt_bound) { // 配对设备
if (!curBondState) {
boundDevice(curBluetoothDevice);
} else {
Toast.makeText(this, "当前设备已经与系统蓝牙建立配对", Toast.LENGTH_SHORT).show();
}
} else if (view.getId() == R.id.bt_disBound) { // 解除配对
if (curBondState) {
disBoundDevice(curBluetoothDevice);
} else {
Toast.makeText(this, "当前设备尚未与系统蓝牙建立配对", Toast.LENGTH_SHORT).show();
}
} else if (view.getId() == R.id.bt_to_send) { // 发送数据
if (curConnState) {
String sendMsg = etSendMsg.getText().toString();
if (sendMsg.isEmpty()) {
Toast.makeText(this, "发送数据为空!", Toast.LENGTH_SHORT).show();
return;
}
sendData(sendMsg, true); // 以16进制字符串形式发送数据
} else {
Toast.makeText(this, "请先连接当前设备", Toast.LENGTH_SHORT).show();
}
}
// 注意:这里没有else块,因为如果没有匹配到任何ID,则不执行任何操作
}
/**
* 解决:无法发现蓝牙设备的问题
*
* 对于发现新设备这个功能, 还需另外两个权限(Android M 以上版本需要显式获取授权,附授权代码):
*/
private final int ACCESS_LOCATION=1;
@SuppressLint("WrongConstant")
private void getPermission() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
int permissionCheck = 0;
permissionCheck = this.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION);
permissionCheck += this.checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION);
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
//未获得权限
this.requestPermissions( // 请求授权
new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION},
ACCESS_LOCATION);// 自定义常量,任意整型
}
}
}
/**
* 请求权限的结果回调。每次调用 requestpermissions(string[],int)时都会调用此方法。
* @param requestCode 传入的请求代码
* @param permissions 传入permissions的要求
* @param grantResults 相应权限的授予结果:PERMISSION_GRANTED 或 PERMISSION_DENIED
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case ACCESS_LOCATION:
if (hasAllPermissionGranted(grantResults)) {
Log.i(TAG, "onRequestPermissionsResult: 用户允许权限");
} else {
Log.i(TAG, "onRequestPermissionsResult: 拒绝搜索设备权限");
}
break;
}
}
private boolean hasAllPermissionGranted(int[] grantResults) {
for (int grantResult : grantResults) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
return false;
}
}
return true;
}
// 搜索设备 /
private void searchBtDevice() {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
if (bluetoothAdapter.isDiscovering()) { //当前正在搜索设备...
return;
}
//开始搜索
bluetoothAdapter.startDiscovery();
}
// 配对/接触配对设备
/**
* 执行绑定 反射
* @param bluetoothDevice 蓝牙设备
* @return true 执行绑定 false 未执行绑定
*/
public boolean boundDevice(BluetoothDevice bluetoothDevice) {
if (bluetoothDevice == null) {
Log.e(TAG, "boundDevice-->bluetoothDevice == null");
return false;
}
try {
return ClsUtils.createBond(BluetoothDevice.class, bluetoothDevice);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
* 执行解绑 反射
* @param bluetoothDevice 蓝牙设备
* @return true 执行解绑 false未执行解绑
*/
public boolean disBoundDevice(BluetoothDevice bluetoothDevice) {
if (bluetoothDevice == null) {
Log.e(TAG, "disBoundDevice-->bluetoothDevice == null");
return false;
}
try {
return ClsUtils.removeBond(BluetoothDevice.class, bluetoothDevice);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
// 连接设备 ///
/**
* 开始连接设备
* @param bluetoothDevice 蓝牙设备
* @param uuid 发起连接的UUID
* @param conOutTime 连接超时时间
*/
public void startConnectDevice(final BluetoothDevice bluetoothDevice, String uuid, long conOutTime) {
if (bluetoothDevice == null) {
Log.e(TAG, "startConnectDevice-->bluetoothDevice == null");
return;
}
if (bluetoothAdapter == null) {
Log.e(TAG, "startConnectDevice-->bluetooth3Adapter == null");
return;
}
//发起连接
connectThread = new ConnectThread(bluetoothAdapter, curBluetoothDevice, uuid);
connectThread.setOnBluetoothConnectListener(new ConnectThread.OnBluetoothConnectListener() {
@Override
public void onStartConn() {
if (ActivityCompat.checkSelfPermission(bluet.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
Log.d(TAG, "startConnectDevice-->开始连接..." + bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress());
}
@Override
public void onConnSuccess(BluetoothSocket bluetoothSocket) {
//移除连接超时
mHandler.removeCallbacks(connectOuttimeRunnable);
Log.d(TAG, "startConnectDevice-->移除连接超时");
Log.w(TAG, "startConnectDevice-->连接成功");
Message message = new Message();
message.what = CONNECT_SUCCESS;
mHandler.sendMessage(message);
//标记当前连接状态为true
curConnState = true;
//管理连接,收发数据
managerConnectSendReceiveData(bluetoothSocket);
}
@Override
public void onConnFailure(String errorMsg) {
Log.e(TAG, "startConnectDevice-->" + errorMsg);
Message message = new Message();
message.what = CONNECT_FAILURE;
mHandler.sendMessage(message);
//标记当前连接状态为false
curConnState = false;
//断开管理连接
clearConnectedThread();
}
});
connectThread.start();
//设置连接超时时间
mHandler.postDelayed(connectOuttimeRunnable, conOutTime);
}
//连接超时
private Runnable connectOuttimeRunnable = new Runnable() {
@Override
public void run() {
Log.e(TAG, "startConnectDevice-->连接超时");
Message message = new Message();
message.what = CONNECT_FAILURE;
mHandler.sendMessage(message);
//标记当前连接状态为false
curConnState = false;
//断开管理连接
clearConnectedThread();
}
};
// 断开连接 //
/**
* 断开已有的连接
*/
public void clearConnectedThread() {
Log.d(TAG, "clearConnectedThread-->即将断开");
//connectedThread断开已有连接
if (connectedThread == null) {
Log.e(TAG, "clearConnectedThread-->connectedThread == null");
return;
}
connectedThread.terminalClose(connectThread);
//等待线程运行完后再断开
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
connectedThread.cancel(); //释放连接
connectedThread = null;
}
}, 10);
Log.w(TAG, "clearConnectedThread-->成功断开连接");
Message message = new Message();
message.what = DISCONNECT_SUCCESS;
mHandler.sendMessage(message);
}
///
/// 管理已有连接、收发数据 //
/**
* 管理已建立的连接,收发数据
* @param bluetoothSocket 已建立的连接
*/
public void managerConnectSendReceiveData(BluetoothSocket bluetoothSocket) {
//管理已有连接
connectedThread = new ConnectedThread(bluetoothSocket);
connectedThread.start();
connectedThread.setOnSendReceiveDataListener(new ConnectedThread.OnSendReceiveDataListener() {
@Override
public void onSendDataSuccess(byte[] data) {
Log.w(TAG, "发送数据成功,长度" + data.length + "->" + bytes2HexString(data, data.length));
Message message = new Message();
message.what = SEND_SUCCESS;
message.obj = "发送数据成功,长度" + data.length + "->" + bytes2HexString(data, data.length);
mHandler.sendMessage(message);
}
@Override
public void onSendDataError(byte[] data, String errorMsg) {
Log.e(TAG, "发送数据出错,长度" + data.length + "->" + bytes2HexString(data, data.length));
Message message = new Message();
message.what = SEND_FAILURE;
message.obj = "发送数据出错,长度" + data.length + "->" + bytes2HexString(data, data.length);
mHandler.sendMessage(message);
}
@Override
public void onReceiveDataSuccess(byte[] buffer) {
Log.w(TAG, "成功接收数据,长度" + buffer.length + "->" + bytes2HexString(buffer, buffer.length));
Message message = new Message();
message.what = RECEIVE_SUCCESS;
message.obj = "成功接收数据,长度" + buffer.length + "->" + bytes2HexString(buffer, buffer.length);
mHandler.sendMessage(message);
}
@Override
public void onReceiveDataError(String errorMsg) {
Log.e(TAG, "接收数据出错:" + errorMsg);
Message message = new Message();
message.what = RECEIVE_FAILURE;
message.obj = "接收数据出错:" + errorMsg;
mHandler.sendMessage(message);
}
});
}
/ 发送数据 /
/**
* 发送数据
* @param data 要发送的数据 字符串
* @param isHex 是否是16进制字符串
* @return true 发送成功 false 发送失败
*/
public boolean sendData(String data, boolean isHex) {
if (connectedThread == null) {
Log.e(TAG, "sendData:string -->connectedThread == null");
return false;
}
if (data == null || data.length() == 0) {
Log.e(TAG, "sendData:string-->要发送的数据为空");
return false;
}
if (isHex) { //是16进制字符串
data.replace(" ", ""); //取消空格
//检查16进制数据是否合法
if (data.length() % 2 != 0) {
//不合法,最后一位自动填充0
String lasts = "0" + data.charAt(data.length() - 1);
data = data.substring(0, data.length() - 2) + lasts;
}
Log.d(TAG, "sendData:string -->准备写入:" + data); //加空格显示
return connectedThread.write(hexString2Bytes(data));
}
//普通字符串
Log.d(TAG, "sendData:string -->准备写入:" + data);
return connectedThread.write(data.getBytes());
}
//
/// 数据类型转换 //
/**
* 字节数组-->16进制字符串
* @param b 字节数组
* @param length 字节数组长度
* @return 16进制字符串 有空格类似“0A D5 CD 8F BD E5 F8”
*/
public static String bytes2HexString(byte[] b, int length) {
StringBuffer result = new StringBuffer();
String hex;
for (int i = 0; i < length; i++) {
hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
result.append(hex.toUpperCase()).append(" ");
}
return result.toString();
}
/**
* hexString2Bytes
* 16进制字符串-->字节数组
* @param src 16进制字符串
* @return 字节数组
*/
public static byte[] hexString2Bytes(String src) {
int l = src.length() / 2;
byte[] ret = new byte[l];
for (int i = 0; i < l; i++) {
ret[i] = (byte) Integer
.valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue();
}
return ret;
}
///
/**
* 初始化蓝牙
*/
private void initBluetooth() {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
Toast.makeText(this, "当前手机设备不支持蓝牙", Toast.LENGTH_SHORT).show();
} else {
//手机设备支持蓝牙,判断蓝牙是否已开启
if (bluetoothAdapter.isEnabled()) {
Toast.makeText(this, "手机蓝牙已开启", Toast.LENGTH_SHORT).show();
} else {
//蓝牙没有打开,去打开蓝牙。推荐使用第二种打开蓝牙方式
//第一种方式:直接打开手机蓝牙,没有任何提示
// bluetoothAdapter.enable(); //BLUETOOTH_ADMIN权限
//第二种方式:友好提示用户打开蓝牙
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
startActivity(enableBtIntent);
}
}
}
/**
* 初始化蓝牙广播
*/
private void initBtBroadcast() {
//注册广播接收
btBroadcastReceiver = new BtBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //开始扫描
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//扫描结束
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//搜索到设备
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //配对状态监听
registerReceiver(btBroadcastReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销广播接收
unregisterReceiver(btBroadcastReceiver);
}
/**
* 蓝牙广播接收器
*/
private class BtBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.equals(action, BluetoothAdapter.ACTION_DISCOVERY_STARTED)) { //开启搜索
Log.d(TAG, "开启搜索...");
Message message = new Message();
message.what = START_DISCOVERY;
mHandler.sendMessage(message);
} else if (TextUtils.equals(action, BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {//完成搜素
Log.d(TAG, "停止搜索...");
Message message = new Message();
message.what = STOP_DISCOVERY;
mHandler.sendMessage(message);
} else if (TextUtils.equals(action, BluetoothDevice.ACTION_FOUND)) { //3.0搜索到设备
//蓝牙设备
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//信号强度
int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
if (ActivityCompat.checkSelfPermission(bluet.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
Log.w(TAG, "扫描到设备:" + bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress());
Message message = new Message();
message.what = DISCOVERY_DEVICE;
message.obj = bluetoothDevice;
mHandler.sendMessage(message);
} else if (TextUtils.equals(action, BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (ActivityCompat.checkSelfPermission(bluet.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
int bondSate = bluetoothDevice.getBondState();
switch(bondSate) {
case BluetoothDevice.BOND_NONE:
Log.d(TAG, "已解除配对");
Message message1 = new Message();
message1.what = DEVICE_BOND_NONE;
mHandler.sendMessage(message1);
break;
case BluetoothDevice.BOND_BONDING:
Log.d(TAG, "正在配对...");
Message message2 = new Message();
message2.what = DEVICE_BONDING;
mHandler.sendMessage(message2);
break;
case BluetoothDevice.BOND_BONDED:
Log.d(TAG, "已配对");
Message message3 = new Message();
message3.what = DEVICE_BONDED;
mHandler.sendMessage(message3);
break;
}
}
}
}
}
类
ConnectedThread.java
ConnectThread.java
ClsUtils.java
太长了,自己去原文下载项目复制粘贴吧,这几个类没什么要修改的,把标红的地方按系统提示给改了就好了。注意复制项目的时候把values文件夹下面的东西也复制一下
并且这个绿色的东西AndroidManifest.xml里面也要声明activity,不然运行不了。
界面
bluetooth.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"
android:padding="10dp"
android:focusableInTouchMode="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/ll_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/bt_search"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:background="@drawable/bt_bg"
android:text="搜索"
android:textColor="#FFFFFF"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_cur_con_state"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@color/colorPrimary"/>
<TextView
android:id="@+id/tv_cur_bond_state"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@color/colorPrimary"/>
</LinearLayout>
<LinearLayout
android:layout_toRightOf="@+id/ll_search"
android:layout_marginLeft="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:hint="设备名"
android:textColor="@color/colorPrimary"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingLeft="10dp"
android:background="@drawable/tv_bg"/>
<TextView
android:id="@+id/tv_address"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:hint="设备地址"
android:textColor="@color/colorPrimary"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingLeft="10dp"
android:background="@drawable/tv_bg"/>
<LinearLayout
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/bt_connect"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:background="@drawable/bt_bg"
android:text="连接"
android:textColor="#FFFFFF"
android:textSize="14sp"
tools:ignore="TouchTargetSizeCheck" />
<Button
android:id="@+id/bt_disconnect"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:background="@drawable/bt_bg"
android:text="断开"
android:textColor="#FFFFFF"
android:textSize="14sp"
tools:ignore="TouchTargetSizeCheck" />
</LinearLayout>
<LinearLayout
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/bt_bound"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:background="@drawable/bt_bg"
android:text="配对"
android:textColor="#FFFFFF"
android:textSize="14sp"
tools:ignore="TouchTargetSizeCheck" />
<Button
android:id="@+id/bt_disBound"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:background="@drawable/bt_bg"
android:text="解除配对"
android:textColor="#FFFFFF"
android:textSize="14sp"
tools:ignore="TouchTargetSizeCheck" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
<!--搜索设备列表-->
<LinearLayout
android:id="@+id/ll_device_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"
android:visibility="visible">
<ListView
android:id="@+id/lv_devices"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
<!--数据收发-->
<LinearLayout
android:id="@+id/ll_data_send_receive"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="设置要发送的数据(十六进制字符串形式)"
android:textColor="@color/colorPrimary"/>
<EditText
android:id="@+id/et_send_msg"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#333333"
android:padding="10dp"
android:hint="在此输入要发送的数据"
android:background="@drawable/tv_bg"/>
<Button
android:id="@+id/bt_to_send"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="发送"
android:textColor="#FFFFFF"
android:textSize="14sp"
android:background="@drawable/bt_bg"/>
<TextView
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="显示接收的数据操作结果(发送成功/发送失败)"
android:textColor="@color/colorPrimary"/>
<ScrollView
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@drawable/tv_bg">
<TextView
android:id="@+id/tv_send_result"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:textSize="14sp"
android:hint="此处显示发送数据操作结果"
android:textColor="@color/colorPrimary"/>
</ScrollView>
<TextView
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="显示接收的数据(以十六进制字符串显示)"
android:textColor="@color/colorPrimary"/>
<ScrollView
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@drawable/tv_bg">
<TextView
android:id="@+id/tv_receive_result"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:textSize="14sp"
android:hint="此处显示发送数据操作结果"
android:textColor="@color/colorPrimary"/>
</ScrollView>
</LinearLayout>
</LinearLayout>
device_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_device_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:textColor="@color/colorPrimary"
android:textSize="16sp"/>
<TextView
android:id="@+id/tv_device_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:textColor="@color/colorPrimary"
android:textSize="16sp"/>
</LinearLayout>
权限声明
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- ACCESS_FINE_LOCATION:允许一个程序访问精确位置(如GPS) -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><!-- ACCESS_COARSE_LOCATION:允许一个程序访问CellID或WiFi热点来获取大致的位置 -->
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Bluetooth"
tools:targetApi="31">
<activity
android:name=".bluet"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
新的安卓开发软件在用蓝牙时不仅要声明蓝牙权限,还要声明位置权限。
运行效果
搜索后点击配对
配对成功
解除配对成功
连接成功
发送数据成功
说明:先点击配对,首次配对是会弹窗的;再点击连接出现发送数据页面,输入一串数字,显示发送成功,但是接收数据失败,这是出现的问题,我无法解决。并且我尝试了多台设备,只有这个手机设备能够连接成功,还有一个设备是我的耳机,不知道怎么解释这个问题,可能协议不一样?不明白,我还有很多需要学习的地方,这次只是学习别人的代码,演示视频可以看b站,我还有一个专栏介绍了一下如何在拿到别人的项目文件时把项目文件的代码复制到自己的项目中,期间出现了很多问题,最后解决了。记得给手机开权限!还有声明权限!