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
   }

}
上一篇下一篇

猜你喜欢

热点阅读