操作蓝牙需要蓝牙权限 及 定位权限
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
蓝牙控制类
@SuppressLint("MissingPermission")
class BluetoothManager private constructor(){
enum class Type {
Connect, ConnectError, Read, IoError
}
companion object{
const val TAG = "bl_blue"
val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
BluetoothManager()
}
}
lateinit var context: Context
private var bluetoothAdapter: BluetoothAdapter? = null
private var mBluetoothA2dp: BluetoothA2dp? = null //通过反射链接蓝牙时使用
private var socket:SerialSocket? = null //需要蓝牙通信时使用
private var currentConnectDevice:BluetoothDevice? = null //当前连接的设备
var connectState = false
/**
* 初始化
*/
open fun init(context:Context):BluetoothManager{
this.context = context
bluetoothAdapter = if(Build.VERSION.SDK_INT > Build.VERSION_CODES.Q){
val blueManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager
blueManager.adapter
}else{
BluetoothAdapter.getDefaultAdapter()
}
if(bluetoothAdapter?.isEnabled?:false){
getBluetoothA2DP()
}
init?.let { it(bluetoothAdapter?.isEnabled?:false) }
return instance
}
/**
* 初始化广播
*/
open fun registerReceiver():BluetoothManager{
val filter = IntentFilter()
filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED) //连接状态变化广播
filter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)
filter.addAction(BluetoothDevice.ACTION_FOUND)
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED) //蓝牙设备配对和解除配对时发送
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED) //开闭状态变化
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED) //扫描蓝牙
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED) //扫描结束
context.registerReceiver(mBroadcastReceiver, filter)
return instance
}
/**
* 取消注册广播
*/
open fun unRegisterReceiver(){
context.unregisterReceiver(mBroadcastReceiver)
}
/**
* 搜索附近蓝牙
*/
open fun searchBluetooth(){
if(!checkInit()){
return
}
bluetoothAdapter?.let {
if(it.isDiscovering){ //正在扫描则取消扫描后重新扫描
it.cancelDiscovery()
}
val startDiscovery = it.startDiscovery()
LogUtils.i(TAG," startDiscovery>>${ startDiscovery}")
}
}
/**
* 连接蓝牙设备 获取收发数据时使用此处
*/
open fun connect(device:BluetoothDevice){
currentConnectDevice = device
socket = SerialSocket(context.getApplicationContext(), device)
socket?.connect(object : SerialListener{
override fun onSerialConnect() {
LogUtils.i(TAG,"onSerialConnect")
setConnectChanged(true)
}
override fun onSerialConnectError(e: Exception?) {
LogUtils.i(TAG,"onSerialConnectError")
setConnectChanged(false)
}
override fun onSerialRead(data: ByteArray?) {
data?.let {
LogUtils.i(TAG,"onSerialRead>>${String(it)}")
if (String(it) == "+PTT=P") {
EventBusUtils.sendEvent(Event(Constants.BLE_DOWN, ""))
}
if (String(it) == "+PTT=R") {
EventBusUtils.sendEvent(Event(Constants.BLE_UP, ""))
}
}
}
override fun onSerialRead(datas: ArrayDeque?) {
LogUtils.i(TAG,"onSerialRead")
datas?.let {
datas.forEach {
LogUtils.i(TAG,"onSerialRead>>${String(it)}")
}
}
}
override fun onSerialIoError(e: Exception?) {
LogUtils.i(TAG,"onSerialIoError")
setConnectChanged(false)
}
})
}
fun setConnectChanged(state:Boolean){
connectState = state
connectChange?.let {
it.invoke(connectState)
}
}
/**
* 断开连接与上边的一块用
*/
open fun disConnect(){
socket?.let {
it.disconnect()
currentConnectDevice = null
}
}
fun checkInit():Boolean{
if(bluetoothAdapter == null || !bluetoothAdapter!!.isEnabled){
return false
}
return true
}
/**
* 反射连接蓝牙
*/
open fun connectBluetooth(device:BluetoothDevice){
currentConnectDevice = device
try {
val connect: Method? = mBluetoothA2dp?.javaClass?.getDeclaredMethod("connect", BluetoothDevice::class.java)
connect?.let {
it.setAccessible(true)
it.invoke(mBluetoothA2dp, device)
}
} catch (e: NoSuchMethodException) {
LogUtils.e(TAG, "connect exception:$e")
e.printStackTrace()
} catch (e: InvocationTargetException) {
LogUtils.e(TAG, "connect exception:$e")
e.printStackTrace()
} catch (e: IllegalAccessException) {
LogUtils.e(TAG, "connect exception:$e")
e.printStackTrace()
}
}
/**
* 反射断开蓝牙
*/
private fun disconnectBluetooth() {
LogUtils.i(TAG, "disconnect")
if (mBluetoothA2dp == null) {
return
}
if (currentConnectDevice == null) {
return
}
try {
val disconnect = mBluetoothA2dp!!.javaClass.getDeclaredMethod(
"disconnect",
BluetoothDevice::class.java
)
disconnect.isAccessible = true
disconnect.invoke(mBluetoothA2dp, currentConnectDevice)
currentConnectDevice = null
} catch (e: NoSuchMethodException) {
LogUtils.e(TAG, "connect exception:$e")
e.printStackTrace()
} catch (e: InvocationTargetException) {
LogUtils.e(TAG, "connect exception:$e")
e.printStackTrace()
} catch (e: IllegalAccessException) {
LogUtils.e(TAG, "connect exception:$e")
e.printStackTrace()
}
}
private fun getBluetoothA2DP() {
if (bluetoothAdapter == null) {
return
}
if (mBluetoothA2dp != null) {
return
}
bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
if (profile == BluetoothProfile.A2DP) {
//Service连接成功,获得BluetoothA2DP
mBluetoothA2dp = proxy as BluetoothA2dp
}
}
override fun onServiceDisconnected(profile: Int) {}
}, BluetoothProfile.A2DP)
}
/**
* 关闭蓝牙 此处时关闭所有
*/
open fun close(){
LogUtils.i("bl_blue","close")
bluetoothAdapter?.let {
if(it.isDiscovering){
it.cancelDiscovery()
}
disConnect()
disconnectBluetooth()
it.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp)
if(it.isEnabled){
it.disable()
}
}
unRegisterReceiver()
}
/**
* 连接保存的设备
*/
open fun connectSaveDevice(){
val address = MmkvUtils.getInstance().decode(Constants.SP_SAVE_BLUETOOTH,"") as String
LogUtils.i("bl_blue","address>>${address}")
bluetoothAdapter?.let {
if(!StringUtils.isEmpty(address)){
val device = it.getRemoteDevice(address)
if(device != null){
connect(device)
}else{
disConnect()
}
}
}
}
val mBroadcastReceiver:BroadcastReceiver
get() {
return object :BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
intent?.apply {
when(action){
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED ->{ //连接状态
val connectState = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1)
when(connectState){
BluetoothA2dp.STATE_CONNECTING -> { //正在连接
LogUtils.i(TAG,"正在连接")
}
BluetoothA2dp.STATE_CONNECTED -> { //已连接
LogUtils.i(TAG,"已连接")
val connectDevice: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) //保存已连接的蓝牙
connectDevice?.let {
MmkvUtils.getInstance().encode(Constants.SP_SAVE_BLUETOOTH,it.address)
connect(it)
}
}
BluetoothA2dp.STATE_DISCONNECTING -> { //正在断开连接
LogUtils.i(TAG,"正在断开连接")
}
BluetoothA2dp.STATE_DISCONNECTED -> { //断开连接
LogUtils.i(TAG,"断开连接")
}
}
}
BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED -> { //播放状态改变
val playingState = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1)
when(playingState){
BluetoothA2dp.STATE_PLAYING -> {
LogUtils.i(TAG,"正在播放")
}
BluetoothA2dp.STATE_NOT_PLAYING -> {
LogUtils.i(TAG,"未播放")
}
}
}
BluetoothDevice.ACTION_FOUND -> { //发现新设备
val foundDevice: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
LogUtils.i(TAG,"foundDevice>>${foundDevice}")
}
BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
val deviceBound: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
deviceBound?.let {
LogUtils.i(TAG, "device name: $it.name")
}
val stateBondChanged =
intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1)
when (stateBondChanged) {
BluetoothDevice.BOND_NONE -> {
LogUtils.i(TAG, "BOND_NONE 删除配对")
}
BluetoothDevice.BOND_BONDING -> {
LogUtils.i(TAG, "BOND_BONDING 正在配对")
}
BluetoothDevice.BOND_BONDED -> {
LogUtils.i(TAG, "BOND_BONDED 配对成功")
}
}
}
BluetoothAdapter.ACTION_STATE_CHANGED -> {
val stateChanged = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
when (stateChanged) {
BluetoothAdapter.STATE_OFF -> LogUtils.i(TAG, "STATE_OFF 手机蓝牙关闭")
BluetoothAdapter.STATE_TURNING_OFF -> LogUtils.i(TAG, "STATE_TURNING_OFF 手机蓝牙正在关闭")
BluetoothAdapter.STATE_ON -> LogUtils.i(TAG, "STATE_ON 手机蓝牙开启")
BluetoothAdapter.STATE_TURNING_ON -> LogUtils.i(TAG, "STATE_TURNING_ON 手机蓝牙正在开启")
}
}
BluetoothAdapter.ACTION_DISCOVERY_STARTED -> { //扫描
LogUtils.i(TAG, "开始扫描蓝牙 state: ACTION_DISCOVERY_STARTED");
}
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
LogUtils.i(TAG, "蓝牙扫描结束 state: ACTION_DISCOVERY_FINISHED")
}
}
}
}
}
}
private var init: ((Boolean) -> Unit)? = null //初始化监听
private var changed: ((Type) -> Unit)? = null //变化监听
private var connectChange: ((Boolean) -> Unit)? = null //连接变化
open fun setOnInitListener(init:(Boolean) -> Unit){
this.init = init
}
open fun setOnChangeListener(changed:(Type) -> Unit){
this.changed = changed
}
open fun setOnConnectChangeListener(connectChange:(Boolean) -> Unit){
this.connectChange = connectChange
}
}
这是别人写的一个用来进行蓝牙通信的socket的连接线程,就直接拿来用了
@SuppressLint("MissingPermission")
public class SerialSocket implements Runnable {
private static final UUID BLUETOOTH_SPP = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private final Context context;
private SerialListener listener;
private final BluetoothDevice device;
private BluetoothSocket socket;
private boolean connected;
public SerialSocket(Context context, BluetoothDevice device) {
if(context instanceof Activity)
throw new InvalidParameterException("expected non UI context");
this.context = context;
this.device = device;
}
public String getName() {
return device.getName() != null ? device.getName() : device.getAddress();
}
/**
* connect-success and most connect-errors are returned asynchronously to listener
*/
public void connect(SerialListener listener) throws IOException {
this.listener = listener;
Executors.newSingleThreadExecutor().submit(this);
}
public void disconnect() {
listener = null; // ignore remaining data and errors
// connected = false; // run loop will reset connected
if(socket != null) {
try {
socket.close();
} catch (Exception ignored) {
}
socket = null;
}
}
public void write(byte[] data) throws IOException {
if (!connected)
throw new IOException("not connected");
socket.getOutputStream().write(data);
}
@Override
public void run() { // connect & read
try {
socket = device.createRfcommSocketToServiceRecord(BLUETOOTH_SPP);
socket.connect();
if(listener != null)
listener.onSerialConnect();
} catch (Exception e) {
if(listener != null)
listener.onSerialConnectError(e);
try {
socket.close();
} catch (Exception ignored) {
}
socket = null;
return;
}
connected = true;
try {
byte[] buffer = new byte[1024];
int len;
//noinspection InfiniteLoopStatement
while (true) {
len = socket.getInputStream().read(buffer);
byte[] data = Arrays.copyOf(buffer, len);
if(listener != null) {
listener.onSerialRead(data);
}
}
} catch (Exception e) {
connected = false;
if (listener != null)
listener.onSerialIoError(e);
try {
socket.close();
} catch (Exception ignored) {
}
socket = null;
}
}
}
public interface SerialListener {
void onSerialConnect ();
void onSerialConnectError (Exception e);
void onSerialRead (byte[] data); // socket -> service
void onSerialRead (ArrayDeque datas); // service -> UI thread
void onSerialIoError (Exception e);
}
使用
BluetoothManager.Companion.getInstance().init(this) //初始化
.registerReceiver() //注册广播
.connectSaveDevice(); //自动连接已经保存的蓝牙 (这一步其实不需要蓝牙权限,系统连接成功过,这一步就可直接调用)
其他开关蓝牙/获取已配对设备/搜索附近设备的结果请自行处理