Android蓝牙扫描和连接!

本文介绍了一种基于蓝牙的签到功能实现方案,包括如何在指定时间内开启蓝牙、扫描并连接指定蓝牙设备的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做蓝牙签到功能时,了解了蓝牙的一部分知识,虽然不够深入,但是感觉良好,他的整体思路如下:
在指定时间内,打开蓝牙,扫描周边蓝牙信号,如果有指定的蓝牙,则由用户确认连接蓝牙,最终签到成功.
首先,我们先写一个工具类,来判断当前时间是否在指定时间之内:

/**
     * 判断时间是否在时间段内
     *
     * @param date
     *            当前时间 yyyy-MM-dd HH:mm:ss
     * @param strDateBegin
     *            开始时间 00:00:00
     * @param strDateEnd
     *            结束时间 00:05:00
     * @return
     */
    public static boolean isInDate(Date date, String strDateBegin,
                                   String strDateEnd) {
         //首先,我们使用SimpleDateFormat来设置时间格式                          
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String strDate = sdf.format(date);
        // 截取当前时间时分秒,这里所填写的数字,是将当前时间转换成指定格式后,来截取字符串,数字为截取的范围
        int strDateH = Integer.parseInt(strDate.substring(11, 13));
        int strDateM = Integer.parseInt(strDate.substring(14, 16));
        int strDateS = Integer.parseInt(strDate.substring(17, 19));
        // 截取开始时间时分秒,这里截取传入的开始时间
        int strDateBeginH = Integer.parseInt(strDateBegin.substring(0, 2));
        int strDateBeginM = Integer.parseInt(strDateBegin.substring(3, 5));
        int strDateBeginS = Integer.parseInt(strDateBegin.substring(6, 8));
        // 截取结束时间时分秒,这里截取传入的结束时间
        int strDateEndH = Integer.parseInt(strDateEnd.substring(0, 2));
        int strDateEndM = Integer.parseInt(strDateEnd.substring(3, 5));
        int strDateEndS = Integer.parseInt(strDateEnd.substring(6, 8));
        if ((strDateH >= strDateBeginH && strDateH <= strDateEndH)) {
            // 当前时间小时数在开始时间和结束时间小时数之间
            if (strDateH > strDateBeginH && strDateH < strDateEndH) {
                return true;
                // 当前时间小时数等于开始时间小时数,分钟数在开始和结束之间
            } else if (strDateH == strDateBeginH && strDateM >= strDateBeginM
                    && strDateM <= strDateEndM) {
                return true;
                // 当前时间小时数等于开始时间小时数,分钟数等于开始时间分钟数,秒数在开始和结束之间
            } else if (strDateH == strDateBeginH && strDateM == strDateBeginM
                    && strDateS >= strDateBeginS && strDateS <= strDateEndS) {
                return true;
            }
            // 当前时间小时数大等于开始时间小时数,等于结束时间小时数,分钟数小等于结束时间分钟数
            else if (strDateH >= strDateBeginH && strDateH == strDateEndH
                    && strDateM <= strDateEndM) {
                return true;
                // 当前时间小时数大等于开始时间小时数,等于结束时间小时数,分钟数等于结束时间分钟数,秒数小等于结束时间秒数
            } else if (strDateH >= strDateBeginH && strDateH == strDateEndH
                    && strDateM == strDateEndM && strDateS <= strDateEndS) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

判断时间结束后,我们来看看怎么连接和扫描蓝牙的,至于布局文件,这里就不说了,只有两个控件而已.
第一步:我们获取蓝牙适配器,来检测当前手机是否支持蓝牙:

 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if(mBluetoothAdapter ==null){
            //不支持蓝牙
            Toast.makeText(this, "该设备不支持蓝牙", Toast.LENGTH_SHORT).show();
        }
  如果蓝牙适配器为空的话,说明自手机不支持蓝牙,如果不为空的话,我们获取他曾经连接过得蓝牙设备信息.
        else {
            // 获取所有已经绑定的蓝牙设备
            Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
            //遍历已绑定设备
            if (devices.size() > 0) {
                for (BluetoothDevice bluetoothDevice : devices) {
                    tvDevices.append(bluetoothDevice.getName() + ":"
                            + bluetoothDevice.getAddress() + "\n\n");
                }
            }
      这里的广播是查找蓝牙,查找完毕,和蓝牙连接状态发生改变时用于接受状态.
      //蓝牙查询,可以在reciever中接受查询到的蓝牙设备
            IntentFilter mFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
            registerReceiver(mReceiver, mFilter);
            // 注册搜索完时的receiver
            mFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
            registerReceiver(mReceiver, mFilter);
    //蓝牙连接状态发生改变时,接收状态
            mFilter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
            registerReceiver(mReceiver,mFilter);
        }

广播注册完成了,那么接收呢?需要创建广播接受者:

private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {

            String action = intent.getAction();
            // 获得已经搜索到的蓝牙设备
            if (action.equals(BluetoothDevice.ACTION_FOUND)) {
            //通过此方法获取搜索到的蓝牙设备
                BluetoothDevice device = intent
                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
         // 获取搜索到的蓝牙绑定状态,看看是否是已经绑定过的蓝牙
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
        // 如果没有绑定过则将蓝牙名称和地址显示在TextView上
                    tvDevices.append(device.getName() + ":"
                            + device.getAddress() + "\n");
                    //78:02:F8:D0:C0:D1
    //如果指定地址的蓝牙和搜索到的蓝牙相同,则我们停止扫描
                    if ("38:A4:ED:77:A2:8C".equals(device.getAddress())) {
                        mBluetoothAdapter.cancelDiscovery();//这句话是停止扫描蓝牙
                        //根据蓝牙地址创建蓝牙对象
                        BluetoothDevice btDev = mBluetoothAdapter.getRemoteDevice(device.getAddress());
                        //通过反射来配对对应的蓝牙
                        try {
                //这里是调用的方法,此方法使用反射,后面解释
                            createBond(btDev.getClass(), btDev);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                // 搜索完成
            } else if (action
                    .equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                setTitle("搜索蓝牙设备");
            }else if(action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)){
            //获取发生改变的蓝牙对象
                BluetoothDevice device = intent
                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                        //根据不同的状态显示提示
                    switch (device.getBondState()) {
                        case BluetoothDevice.BOND_BONDING://正在配对
                            Log.d("yxs", "正在配对......");
                            break;
                        case BluetoothDevice.BOND_BONDED://配对结束
                            Log.d("yxs", "完成配对");
                            Toast.makeText(context, "签到成功", Toast.LENGTH_SHORT).show();
                            handler.sendEmptyMessageDelayed(1,2000);
                            break;
                        case BluetoothDevice.BOND_NONE://取消配对/未配对
                            Log.d("yxs", "取消配对");
                        default:
                            break;
                    }
            }
        }
    };

上面有提到是用反射来连接蓝牙:这里贴一下代码,此代码为百度所得,懂得他是什么意思,但是反射我不太懂,就不说什么了.

  /**
     * 蓝牙配对
     *
     * @param btClass
     * @param btDevice
     * @return
     * @throws Exception
     */
    public boolean createBond(Class btClass, BluetoothDevice btDevice) throws Exception {
        Method createBondMethod = btClass.getMethod("createBond");//获取蓝牙的连接方法
        Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
        return returnValue.booleanValue();//返回连接状态
    }

咱们获取了扫描的数据,也连接了蓝牙,但是,怎么开启扫描的呢?在扫描之前我们是否还要判断此时蓝牙是否已经打开了呢?
在一开始,我们已经知道了当前手机是否支持蓝牙.那么,在开启扫描时,我们来判断蓝牙是否开起来了:

  /**
     * 判断是否支持蓝牙,并打开蓝牙
     * 获取到BluetoothAdapter之后,还需要判断是否支持蓝牙,以及蓝牙是否打开。
     * 如果没打开,需要让用户打开蓝牙:
     */
    public void checkBleDevice(Context context) {
    //通过适配器对象调用isEnabled()方法,判断蓝牙是否打开了
        if (mBluetoothAdapter.isEnabled()) {
        //如果蓝牙已经打开,判断此时蓝牙是否正在扫描,如果正在扫描,则先停止当前扫描,然后在重新扫描
             if (mBluetoothAdapter.isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
        }
        // 开始搜索蓝牙设备,搜索到的蓝牙设备通过广播返回
        mBluetoothAdapter.startDiscovery();

        }else{
        //如果没有开启蓝牙,调用系统方法,让用户确认开启蓝牙
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            enableBtIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(enableBtIntent);
        }
    }

这是用户知晓的情况下,当用户没有手动去扫描蓝牙,但是此时已经到了签到时间,我们就会用到之前判断当前时间是否在指定时间之内的方法,

 /**
     * 判断是否到了指定时间,打开蓝牙,扫描指定设备
     */
    public void currentTime(){
        if(TimeUtils.isCurrentInTimeScope(13,33,13,50)){
//如果时间已经到了,则默认自动打开蓝牙,且使用handler发出一条通知,
            mBluetoothAdapter.enable();
            handler.sendEmptyMessageDelayed(0,2000);
        }else{
        //如果没有到指定时间,我们也发出一条通知,
            handler.sendEmptyMessageDelayed(2,1000);
        }
    }

上面通过时间判断开启蓝牙发出消息,那么,我们要做什么呢,这要看你自己了,我是这么干的:,1.当时间到了之后,我接受到消息,此时自动调用checkBleDevice方法扫描蓝牙,2.如果么有到指定时间,我会依旧发出一条消息,在对应消息处理中,调用currentTime方法,判断当前时间,在currentTime方法中,无论到没到指定时间,都会发出一条消息,会继续在1和2中继续判断,循环往复,直到时间到了。

 private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(msg.what == 0){
                checkBleDevice(MainActivity.this);
            }else if(msg.what ==1){
                BluetoothDevice btDev = mBluetoothAdapter.getRemoteDevice("38:A4:ED:77:A2:8C");
                try {
                    removeBond(btDev.getClass(),btDev);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }else if(msg.what == 2){
                Toast.makeText(MainActivity.this, "检查状态", Toast.LENGTH_SHORT).show();
                handler.sendEmptyMessageDelayed(3,60000);
            }else if(msg.what == 3){
                currentTime();
            }
        }
    };

因为,蓝牙连接成功后会记住,所以签到成功后,我会移除指定的已配对的蓝牙设备,通过调用另一个使用反射完成方法”:

 /**
     * 删除指定的已配对设备
     * @param btClass
     * @param btDevice
     * @return
     * @throws Exception
     */
    public boolean removeBond(Class btClass,BluetoothDevice btDevice) throws Exception {
        Method removeBondMethod = btClass.getMethod("removeBond");//获取移除蓝牙设备的方法
        Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);//得到操作结果
        return returnValue.booleanValue();
    }

最后由于我们注册了广播,所以,在页面销毁时,我们要解除注册:

 @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除注册
        unregisterReceiver(mReceiver);
    }
<think>我们正在创建一个用于蓝牙扫描连接的工具类。根据引用内容,我们可以总结出以下关键点: 1. 扫描蓝牙设备需要获取蓝牙适配器(BluetoothAdapter),并注册一个LeScanCallback回调(引用[2])。 2. 连接蓝牙设备可能需要使用反射(引用[1][4]),特别是对于A2DP连接,以及配对(createBond)操作。 3. 我们需要一个数据结构来保存扫描到的蓝牙设备(引用[3])。 4. 需要处理权限(引用[5])。 工具类设计思路: - 初始化蓝牙适配器 - 扫描蓝牙设备(包括开始扫描停止扫描) - 连接设备(包括配对A2DP连接) - 回调接口用于返回扫描结果连接状态 注意:由于Android版本差异,部分API可能需要反射,特别是A2DP连接(如引用[1]所示)配对(如引用[4]所示)。 我们将创建一个名为BluetoothUtils的工具类,包含以下功能: 1. 初始化蓝牙适配器 2. 扫描蓝牙设备(使用经典蓝牙扫描低功耗蓝牙扫描?根据引用[2]使用的是经典蓝牙的LeScan,但注意LeScan是用于BLE设备的,经典设备使用startDiscovery) 3. 连接设备(包括配对连接A2DP) 但是,用户需求是“执行蓝牙扫描连接操作”,并没有明确是经典蓝牙还是低功耗蓝牙(BLE)。根据引用内容,引用了A2DP(经典蓝牙配置文件)LeScan(用于BLE),所以我们需要考虑两者吗? 然而,在同一个工具类中同时处理经典蓝牙低功耗蓝牙可能会使代码复杂。通常,这两者有不同的API。因此,我们需要明确用户的需求。 但是,用户问题中并没有明确,所以我们可以先提供一种(比如经典蓝牙)或者分别提供。但根据引用[3]中使用了BluetoothGatt,这是BLE的连接方式。所以用户可能是混合需求? 考虑到引用内容混合了经典蓝牙(A2DP)低功耗蓝牙(LeScan, BluetoothGatt),我们可能需要分别处理。 然而,由于工具类的通用性,我们可能希望它同时支持经典蓝牙低功耗蓝牙的基本操作。但这样会使工具类变得庞大且复杂。 因此,我们这里先按照经典蓝牙扫描(发现设备)A2DP连接来实现,同时提供BLE扫描(LeScan)连接(Gatt)的接口可能超出范围。但是,引用[2][3]都是关于BLE的,所以用户可能更关注BLE? 重新阅读用户问题:“我想获取用于执行蓝牙扫描连接操作的Android工具类”,并且引用中同时出现了经典蓝牙(A2DP)BLE(LeScan, Gatt),所以用户可能希望同时支持? 但通常,经典蓝牙BLE的API使用方式不同,建议分开实现。不过,我们可以设计一个工具类,提供两种蓝牙技术的支持,但需要明确区分。 由于时间有限,我们这里先实现BLE的扫描连接(因为引用[2][3]提供了更多BLE的信息),同时保留经典蓝牙的接口(如A2DP连接)作为扩展。 具体步骤: 1. 初始化蓝牙适配器(BluetoothAdapter) 2. BLE扫描: - 开始扫描:使用BluetoothAdapter.startLeScan(LeScanCallback) - 停止扫描:BluetoothAdapter.stopLeScan(LeScanCallback) 3. BLE连接: - 使用BluetoothDevice.connectGatt()建立Gatt连接 4. 经典蓝牙扫描(发现设备): - 使用BluetoothAdapter.startDiscovery(),并通过广播接收器接收结果(但引用中没有提供,所以可能需要另外实现) 5. 经典蓝牙连接(如A2DP): - 使用反射调用BluetoothA2dp的connect方法(如引用[1]) 由于用户需求是工具类,我们可以先实现BLE部分,因为引用中BLE的信息更多。经典蓝牙部分可以后续扩展。 因此,我们设计一个工具类,主要针对BLE操作,同时包含经典蓝牙A2DP连接的辅助方法(因为引用[1][4]提到了)。 注意:在Android中,BLE扫描连接需要不同的权限特性,例如: - 需要BLUETOOTHBLUETOOTH_ADMIN权限 - 对于Android 6.0以上,还需要位置权限(因为蓝牙扫描可以用于获取位置信息) 工具类结构: 类名:BluetoothUtils 内部接口: - OnScanListener:扫描监听器,用于返回扫描到的设备 - OnConnectionListener:连接监听器,用于返回连接状态 属性: - BluetoothAdapter mBluetoothAdapter - Context mContext - List<BleDevice> mBleDevices // 保存扫描到的BLE设备 - boolean isScanning // 是否正在扫描 方法: - 初始化:getInitialization(Context context) // 初始化蓝牙适配器 - 开始扫描(BLE):startScan(OnScanListener listener) - 停止扫描(BLE):stopScan() - 连接设备(BLE):connect(BluetoothDevice device, OnConnectionListener listener) // 通过BluetoothGatt连接 - 配对设备(经典蓝牙):createBond(BluetoothDevice device) // 使用反射配对 - 连接A2DP(经典蓝牙):connectA2DP(BluetoothDevice device) // 使用反射连接A2DP 注意:BLE设备连接后,我们会得到一个BluetoothGatt对象,然后可以通过它进行GATT操作,但这里我们只实现连接。 由于连接后的GATT操作(服务发现、读写等)较为复杂,不在工具类中展开,我们只提供连接断开等基本操作。 另外,我们使用引用[3]中的BleDevice类来封装扫描到的BLE设备信息。 代码实现: 1. 初始化蓝牙适配器 2. BLE扫描回调:在回调中将扫描到的设备加入列表,并通过监听器返回 3. 连接BLE设备:调用device.connectGatt(),并设置BluetoothGattCallback 由于工具类代码较长,我们将分步骤编写。 注意:在AndroidManifest.xml中需要添加蓝牙权限。 由于引用[5]提到权限申请,我们在工具类中不处理权限,由调用者确保权限已获取。 开始编写代码:</think>以下是一个用于执行蓝牙扫描连接操作的Android工具类实现,结合了蓝牙扫描、设备连接、配对等核心功能: ```java import android.bluetooth.*; import android.content.Context; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; public class BluetoothUtils { private Context context; private BluetoothAdapter bluetoothAdapter; private List<BluetoothDevice> deviceList = new ArrayList<>(); private BluetoothAdapter.LeScanCallback leScanCallback; // 初始化蓝牙适配器 public void initBluetooth(Context context) { this.context = context; BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); bluetoothAdapter = bluetoothManager.getAdapter(); } // 开始扫描BLE设备 public void startBleScan(ScanCallback callback) { if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) { return; } leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { if (!deviceList.contains(device)) { deviceList.add(device); callback.onDeviceFound(device, rssi); } } }; bluetoothAdapter.startLeScan(leScanCallback); } // 停止扫描 public void stopBleScan() { if (bluetoothAdapter != null && leScanCallback != null) { bluetoothAdapter.stopLeScan(leScanCallback); } } // 蓝牙配对(反射实现) public boolean createBond(BluetoothDevice device) { try { Method method = BluetoothDevice.class.getMethod("createBond"); return (boolean) method.invoke(device); } catch (Exception e) { e.printStackTrace(); return false; } } // 连接A2DP设备(反射实现) public void connectA2dp(BluetoothDevice device) { try { BluetoothProfile.ServiceListener listener = new BluetoothProfile.ServiceListener() { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { if (profile == BluetoothProfile.A2DP) { BluetoothA2dp a2dpProxy = (BluetoothA2dp) proxy; try { Method connectMethod = BluetoothA2dp.class.getMethod( "connect", BluetoothDevice.class); connectMethod.invoke(a2dpProxy, device); } catch (Exception e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(int profile) {} }; bluetoothAdapter.getProfileProxy(context, listener, BluetoothProfile.A2DP); } catch (Exception e) { e.printStackTrace(); } } // 获取已配对设备列表 public List<BluetoothDevice> getPairedDevices() { return new ArrayList<>(bluetoothAdapter.getBondedDevices()); } // 扫描回调接口 public interface ScanCallback { void onDeviceFound(BluetoothDevice device, int rssi); } } ``` ### 功能说明 1. **蓝牙初始化**: - 通过`initBluetooth()`获取系统蓝牙服务 - 需要`BLUETOOTH``BLUETOOTH_ADMIN`权限[^5] 2. **BLE扫描**: - `startBleScan()`使用低功耗蓝牙扫描 - 扫描结果通过回调返回设备对象信号强度$rssi$[^3] - Android 6.0+需要`ACCESS_FINE_LOCATION`权限 3. **设备配对**: - `createBond()`使用反射调用系统配对方法[^4] - 触发系统配对请求对话框 4. **A2DP连接**: - `connectA2dp()`通过反射建立音频传输连接[^1] - 需要先配对成功才能连接 5. **设备管理**: - `getPairedDevices()`获取已配对设备列表 - `deviceList`动态存储扫描发现的设备 ### 使用示例 ```java // 初始化 BluetoothUtils utils = new BluetoothUtils(); utils.initBluetooth(context); // 开始扫描 utils.startBleScan(new BluetoothUtils.ScanCallback() { @Override public void onDeviceFound(BluetoothDevice device, int rssi) { // 处理发现的设备 } }); // 配对设备 BluetoothDevice device = ... // 获取到的设备对象 utils.createBond(device); // 连接音频设备 utils.connectA2dp(device); ``` ### 注意事项 1. 在AndroidManifest.xml添加权限: ```xml <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- Android 6.0+ --> ``` 2. 反射方法可能因Android版本变化失效,需测试目标平台兼容性 3. 连接A2DP前需确保设备已配对,否则反射调用会失败[^1] 4. BLE扫描建议设置超时时间(例如10-30秒),避免耗电过高
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值