android的蓝牙匹配连接
2022-02-15 本文已影响0人
sunny635533
1、声明权限
<uses-permission android:name="android.permission.INTERNET"/>
<!--蓝牙连接要用到权限-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!--蓝牙搜索要用到权限-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!--蓝牙基本的权限-->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- If your app targets Android 9 or lower, you can declare
ACCESS_COARSE_LOCATION instead. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!--获取本设备的mac地址的权限-->
<uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS"
tools:ignore="ProtectedPermissions" />
<!-- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />-->
<!-- <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />-->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />-->
注意:定位权限是必须的,否则无法使用蓝牙的搜索功能
2、获取蓝牙已匹配过的设备和搜索可匹配的设备列表
需要判断蓝牙是否启用,工具类如下:
package com.org.testbluetooth;
import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
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.util.Log;
import androidx.annotation.RequiresApi;
import java.lang.reflect.Method;
public class BluetoothUtils {
public static final String TAG = BluetoothUtils.class.getSimpleName();
/**
* 获取系统蓝牙适配器管理类
*/
public BluetoothAdapter mBluetoothAdapter;
private BluetoothManager bluetoothManager;
private static class Holder {
public static BluetoothUtils INSTANCE = new BluetoothUtils();
}
public static BluetoothUtils getInstance() {
return Holder.INSTANCE;
}
public void init(Activity context) {
if (bluetoothManager == null) {
bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
}
mBluetoothAdapter = bluetoothManager.getAdapter();
// 询问打开蓝牙
if (!isBlueEnable()) {
openBlueSync(context, 1);
}
}
public BluetoothAdapter getBlueAdapter(){
return mBluetoothAdapter;
}
/**
* 设备是否支持蓝牙 true为支持
*
* @return
*/
public boolean isSupportBlue() {
return mBluetoothAdapter != null;
}
/**
* 蓝牙是否打开 true为打开
*
* @return
*/
public boolean isBlueEnable() {
return isSupportBlue() && mBluetoothAdapter.isEnabled();
}
/**
* 开启广播
*/
public void openBroadCast(Activity activity, BroadcastReceiver scanBlueReceiver) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//获得扫描结果
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//绑定状态变化
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//开始扫描
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//扫描结束
activity.registerReceiver(scanBlueReceiver, intentFilter);
}
/**
* 自动打开蓝牙(同步)
* 这个方法打开蓝牙会弹出提示
* 需要在onActivityResult 方法中判断resultCode == RESULT_OK true为成功
*/
public void openBlueSync(Activity activity, int requestCode) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(intent, requestCode);
}
/**
* 配对(配对成功与失败通过广播返回)
*
* @param device
*/
public void pinBlue(BluetoothDevice device) {
if (device == null) {
Log.e(TAG, "bond device null");
return;
}
if (!isBlueEnable()) {
Log.e(TAG, "Bluetooth not enable!");
return;
}
//配对之前把扫描关闭
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
//判断设备是否配对,没有配对在配,配对了就不需要配了
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
Log.d(TAG, "attemp to bond:" + device.getName());
try {
Method createBondMethod = device.getClass().getMethod("createBond");
createBondMethod.invoke(device);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e(TAG, "attemp to bond fail!");
}
}
}
/**
* 取消配对(取消配对成功与失败通过广播返回 也就是配对失败)
*
* @param device
*/
public void cancelPinBlue(BluetoothDevice device) {
if (device == null) {
Log.d(TAG, "cancel bond device null");
return;
}
if (!isBlueEnable()) {
Log.e(TAG, "Bluetooth not enable!");
return;
}
//判断设备是否配对,没有配对就不用取消了
if (device.getBondState() != BluetoothDevice.BOND_NONE) {
Log.d(TAG, "attemp to cancel bond:" + device.getName());
try {
Method removeBondMethod = device.getClass().getMethod("removeBond");
removeBondMethod.invoke(device);
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "attemp to cancel bond fail!");
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
public Boolean hasPermissions(Activity activity,int requestCode) {
if (!hasLocationPermissions(activity)) {
requestLocationPermission(activity,requestCode);
return false;
}
return true;
}
@RequiresApi(Build.VERSION_CODES.M)
public Boolean hasLocationPermissions(Activity activity) {
return activity.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
@RequiresApi(Build.VERSION_CODES.M)
public void requestLocationPermission(Activity activity,int requestCode) {
activity.requestPermissions(
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
requestCode
);
}
}
3、activity里的实现
布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
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">
<androidx.appcompat.widget.LinearLayoutCompat
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"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/selectBluetoothBtn"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ff0f00"
android:text="获取已配对设备信息"
android:layout_marginTop="50dp"/>
<!-- <Button-->
<!-- android:id="@+id/setBlueVisableBtn"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="50dp"-->
<!-- android:background="#ff0f00"-->
<!-- android:text="设置设备可见"-->
<!-- android:layout_marginTop="20dp"/>-->
<Button
android:id="@+id/startSearchBtn"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ff0f00"
android:text="开始蓝牙搜索"
android:layout_marginTop="20dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="已匹配的设备列表"
android:layout_marginTop="30dp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycleview"
android:layout_width="match_parent"
android:layout_height="150dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/startSearchBtn"
android:layout_marginTop="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="搜索到的设备列表"
android:layout_marginTop="30dp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/searchRecycleview"
android:layout_width="match_parent"
android:layout_height="150dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/startSearchBtn"
android:layout_marginTop="10dp"/>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.core.widget.NestedScrollView>
MainAcivity类
package com.org.testbluetooth
import android.Manifest
import android.bluetooth.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.Toast
import java.io.IOException
import java.util.*
import android.bluetooth.BluetoothAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*
import android.bluetooth.BluetoothDevice
import android.content.pm.PackageManager
import android.os.Build
import android.os.Message
import androidx.annotation.RequiresApi
import java.io.InputStream
import java.lang.Exception
class MainActivity : AppCompatActivity() {
var bluetoothUtils: BluetoothUtils?=null
private val REQUEST_ENABLE_BT = 100
private val REQUEST_FINE_LOCATION = 0x124
var TAG = "MainActivity"
var searchDevList = mutableListOf<BluetoothDevice>()
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
var action: String? = intent?.action
println("============== onReceive ========action=$action")
when (action) {
BluetoothDevice.ACTION_FOUND -> {
val device: BluetoothDevice? =
intent?.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
val deviceName = device?.name
val deviceHardwareAddress = device?.address // MAC address
println("========= 通过receiver发现设备 ======${deviceName} ${deviceHardwareAddress}")
if (device != null && !deviceName.isNullOrEmpty()) {
searchDevList.add(device)
}
}
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
println("============== onReceive ACTION_DISCOVERY_FINISHED========蓝牙设备搜索完成=$action")
initSearchRecycleView(searchDevList)
}
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bluetoothUtils = BluetoothUtils.getInstance()
bluetoothUtils?.init(this)
selectBluetoothBtn.setOnClickListener { getBluetoothAdapter() }
startSearchBtn.setOnClickListener { startScanBluetooth() }
bluetoothUtils?.openBroadCast(this,receiver)
//获取已匹配过的蓝牙设备集合
getBluetoothAdapter()
}
/**
* 获取已匹配的蓝牙设备
*/
@RequiresApi(Build.VERSION_CODES.M)
fun getBluetoothAdapter() {
//以检查当前是否已启用蓝牙。如果此方法返回 false,则表示蓝牙处于停用状态
var hasPer = bluetoothUtils?.hasPermissions(this,REQUEST_FINE_LOCATION)
if(!hasPer!!){
Toast.makeText(this,"没有权限",Toast.LENGTH_SHORT).show()
return;
}
var bluetoothAdapter = bluetoothUtils?.mBluetoothAdapter
//获取本机蓝牙名称
var localBlueName = bluetoothAdapter?.name
//获取本机蓝牙地址
var localAaddress = bluetoothAdapter?.address
println("==== 本机蓝牙信息:本机蓝牙名称 =$localBlueName 本机蓝牙地址 =$localAaddress")
println("========已匹配的设备条数==========" + bluetoothAdapter?.bondedDevices?.size)
val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
pairedDevices?.forEach { device ->
val deviceName = device.name
val deviceHardwareAddress = device.address // MAC address
println("=======name=>$deviceName")
println("=======address=>$deviceHardwareAddress")
}
var list = pairedDevices?.toList();
if (list != null) {
initRecycleView(list)
}
}
/**
* 开启蓝牙搜索
*/
fun startScanBluetooth() {
var bluetoothAdapter = bluetoothUtils?.mBluetoothAdapter
// 判断是否在搜索,如果在搜索,就取消搜索
if (bluetoothAdapter == null) {
println("======= bluetoothAdapter为空 =======")
return;
}
if (bluetoothAdapter?.isDiscovering == true) {
bluetoothAdapter?.cancelDiscovery()
}
searchDevList.clear()
bluetoothAdapter?.startDiscovery()
}
/**
* 设置蓝牙可见性,允许外部发现本设备
* 有时候扫描不到某设备,这是因为该设备对外不可见或者距离远,需要设备该蓝牙可见,这样该才能被搜索到。
* 可见时间默认值为120s,最多可设置300。
*/
// fun setVisableToBluetooth() {
// println("=========scanMode =========" + bluetoothAdapter?.scanMode)
//// if (bluetoothAdapter?.scanMode !=
//// BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
// var discoverableIntent = Intent(
// BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE
// )
// discoverableIntent.putExtra(
// BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120
// )
//// startActivityForResult(discoverableIntent,REQUEST_LOCAL_VISABLE)
// startActivity(discoverableIntent)
//// }
// }
@RequiresApi(Build.VERSION_CODES.M)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
println("========= onActivityResult =======resultCode:$resultCode, requestCode:$requestCode")
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_ENABLE_BT) {
Toast.makeText(this, "蓝牙开启成功", Toast.LENGTH_LONG).show()
}else if (requestCode == REQUEST_FINE_LOCATION) {
Toast.makeText(this, "定位权限开启成功", Toast.LENGTH_LONG).show()
getBluetoothAdapter()
}
}
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(receiver)
}
/**
* 客户端连接
*/
private inner class ConnectThread(device: BluetoothDevice) : Thread() {
lateinit var bluetoothDevice: BluetoothDevice;
val MY_UUID = UUID.fromString("0f14d0ab-9605-4a62-a9e4-5ed26688389b")
private val mmScoket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
bluetoothDevice = device;
device.createRfcommSocketToServiceRecord(MY_UUID)
}
override fun run() {
super.run()
// Cancel discovery because it otherwise slows down the connection.
var bluetoothAdapter = bluetoothUtils?.mBluetoothAdapter
bluetoothAdapter?.cancelDiscovery()
try {
mmScoket?.use { socket ->
//Connect to the remote device through the socket. This call blocks until it succeeds or throws an exception
socket.connect()
println("========= 连接设备 =========" + bluetoothDevice.name);
if(socket.isConnected){//连接成功
// isConnect=true;
// val message = Message()
// message.what =0;
// message.obj =bluetoothDevice.getAddress();
// mHandler.sendMessage(message);
var inputStream: InputStream = socket.inputStream //获取蓝牙设备发送的数据
var buffer = ByteArray(1024)
var len:Int = 0
var content:String
while (len != -1) {
len=inputStream.read(buffer)
content = String(buffer, 0, len)
// val message = Message()
// message.what =0;
// message.obj =bluetoothDevice.getAddress();
// mHandler.sendMessage(message);
}
}
}
} catch (e: Exception) {
try {//关闭这个socket
mmScoket?.close()
} catch (e2: IOException) {
e2.printStackTrace();
}
return
}finally {
mmScoket?.close()
}
}
fun cancel() {
try {
mmScoket?.close()
} catch (e: IOException) {
Log.e(TAG + "_ConnectThread", "Could not close the client socket", e)
}
}
}
/**
* 服务端接收数据
*/
private inner class AcceptThread : Thread() {
/**
*
name – service name for SDP record
uuid – uuid for SDP record
*/
var NAME = "lockName"
val MY_UUID = UUID.fromString("000000")
private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) {
var bluetoothAdapter = bluetoothUtils?.mBluetoothAdapter
bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID)
}
override fun run() {
// Keep listening until exception occurs or a socket is returned.
var shouldLoop = true
while (shouldLoop) {
val socket: BluetoothSocket? = try {
mmServerSocket?.accept()
} catch (e: IOException) {
Log.e(TAG + "_AcceptThread", "Socket's accept() method failed", e)
shouldLoop = false
null
}
socket?.also {
// manageMyConnectedSocket(it)
mmServerSocket?.close()
shouldLoop = false
}
}
}
// Closes the connect socket and causes the thread to finish.
fun cancel() {
try {
mmServerSocket?.close()
} catch (e: IOException) {
Log.e(TAG, "Could not close the connect socket", e)
}
}
}
/**
* 已匹配过的列表
*/
fun initRecycleView(list: List<BluetoothDevice>) {
recycleview.layoutManager = LinearLayoutManager(this)
var adapter = DeviceAdapter(list, DeviceAdapter.ItemClickListener {
var device: BluetoothDevice = list.get(it)
// var connectThread = ConnectThread(device);
// connectThread.start()
bluetoothUtils?.cancelPinBlue(device)
})
recycleview.adapter = adapter
}
/**
* 能匹配过的列表
*/
fun initSearchRecycleView(list: List<BluetoothDevice>) {
searchRecycleview.layoutManager = LinearLayoutManager(this)
var adapter = DeviceAdapter(list, DeviceAdapter.ItemClickListener {
var device: BluetoothDevice = list.get(it)
bluetoothUtils?.pinBlue(device)
})
searchRecycleview.adapter = adapter
}
}