Android 12 蓝牙适配 Java版(上)

简介: Android 12 蓝牙适配 Java版(上)

前言


  本身已经写过一篇关于蓝牙适配的文章了,不过因为是Kotlin,很多读者看不懂,对此我深感无奈,一开始也没有想过再写Java版本的,但是后面发现看不懂的越来越多了,我意识到不对劲了,因此我觉得再写一个Java版本的。

aa134fdb28934464ac23d8d9ef96a9bf.gif


正文


  在Android系统版本中,蓝牙的变化有,但是不多,这里简要说明一下。


一、Android版本中蓝牙简介

  • Android1.5 中增加了蓝牙功能,立体声 Bluetooth 支持:A2DP [Advanced Audio DistributionProfile]、AVCRP [Audio/Video Remote Control Profile],自动配对。
  • Android2.0 中支持Bluetooth2.1协议。
  • Android3.0 中能让应用查询已经连接上 Bluetooth 设备的 Bluetooth Profile、音频状态等,然后通知用户。
  • Android3.1 中系统可以通过 Bluetooth HID 方式同时接入一到多款输入设备。
  • Android4.0 中新增支持连接 Bluetooth HDP [Health Device Profile)] 设备,通过第三方应用的支持,用户可以连接到医院、健身中心或者家庭等场合中的无线医疗设备和传感器。
  • Android4.2 中引入了一种新的针对 Android 设备优化的 Bluetooth 协议栈 BlueDroid,从而取代 BlueZ 协议栈。Bluedroid 协议栈由 Google 和 Broadcom 公司共同开发,相对于 BlueZ 协议栈,BlueDroid 提升了兼容性和可靠性。
  • Android4.3 中增加了对低功耗蓝牙的支持,内置支持 Bluetooth AVRCP 1.3,基于 Google 和 Broadcom 公司功能研发的针对于 Android 设备优化的新的蓝牙协议栈 BlueDroid。
  • Android4.4 中新增两种新 Proifle 支持:HID [Human Interface Device]、MAP [Message Access Profile]
  • Android5.0 中支持Bluetooth4.1协议。
  • Android6.0 中扫描蓝牙需要动态获取定位才行。
  • Android7.0 中支持Bluetooth4.2协议。
  • Android8.0 中支持Bluetooth5.0协议,强化了蓝牙音频的表现。比如编码/传输格式可选SBC、AAC、aptX/aptX HD、LDAC等四种,音质依次提高。
  • Android10.0 中支持Bluetooth5.1协议,在5.0的基础上,增加了侧向功能和厘米级定位服务,大幅度提高了定位精度。使室内定位更精准。
  • Android11.0 中支持Bluetooth5.2协议,增强版ATT协议,LE功耗控制和信号同步,连接更快,更稳定,抗干扰性更好。

Android12.0 中支持Bluetooth5.3协议,增强了经典蓝牙BR/EDR(基础速率和增强速率)的安全性。蓝牙5.3的延迟更低、抗干扰性更强、提升了电池续航时间。系统引入了新的运行时权限BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE 和 BLUETOOTH_CONNECT权限,用于更好地管理应用于附近蓝牙设备的连接。


二、新建项目

  在Android12.0中新增加了三个运行时权限,我们依次来说明一下,这里我们依然创建一个项目来说明,新建一个Android12Bluetooth-Java项目,如下图所示:

56c6032ee16b43958af0e3a14678a74f.png

点击Finish,完成项目的创建。


① 配置build.gradle

然后来配置一下项目的依赖库,在app的build.gradle中增加

  buildFeatures {
        viewBinding true
        dataBinding true
    }


增加位置如下图所示:

c8968a83cf4744f3af797d61cb643089.png

注意是在android{}闭包下,然后Sync Now。


② 配置AndroidManifest.xml

下面配置AndroidMainfest.xml,权限如下所示:

    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <!--Android12 的蓝牙权限 如果您的应用与已配对的蓝牙设备通信或者获取当前手机蓝牙是否打开-->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
    <!--Android12 的蓝牙权限 如果您的应用查找蓝牙设备(如蓝牙低功耗 (BLE) 外围设备)-->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
    <!--Android12 的蓝牙权限 如果您的应用使当前设备可被其他蓝牙设备检测到-->
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
    <!--Android 6-11 定位权限-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />


增加位置如下图所示:

7800b24ddba64c8ca930222699931351.png

这里我们需要蓝牙的权限,还有定位的权限,在Android 12及以上版本使用蓝牙相关权限,Android 12以下版本使用定位相关权限。


三、打开蓝牙

  下面我们构建一下activity_main.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
    <Button
        android:id="@+id/btn_open_bluetooth"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="10dp"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="打开蓝牙"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>


 这里就是写一个按钮,用来点击打开系统蓝牙的开关的。在Android12.0之前打开蓝牙的之前需要先判断蓝牙是否打开,我们可以这样来写,在MainActivity中增加如下代码:

    private boolean isOpenBluetooth() {
        BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        BluetoothAdapter adapter = manager.getAdapter();
        if (adapter == null) {
            return false;
        }
        return adapter.isEnabled();
    }


同样我们还需要一个方法判断当前是否为Android12及以上版本。

    private boolean isAndroid12() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
    }


同样还有一个检查此权限是否授予的方法和一个显示Toast的方法:

    private boolean hasPermission(String permission) {
        return checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
    }
    private void showMsg(CharSequence msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }


① 打开蓝牙意图

在MainActivity中新增如下代码:

  private ActivityResultLauncher<Intent> enableBluetooth;         //打开蓝牙意图
    private void registerIntent() {
      //打开蓝牙意图
        enableBluetooth = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            if (result.getResultCode() == Activity.RESULT_OK) {
                if (isOpenBluetooth()){
                    BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
                    mBluetoothAdapter = manager.getAdapter();
                    scanner = mBluetoothAdapter.getBluetoothLeScanner();
                    showMsg("蓝牙已打开");
                } else {
                    showMsg("蓝牙未打开");
                }
            }
        });
    }


  这里声明了一个变量,然后在方法中对变量进行赋值,此方法就替代了之前的startActivityForResult,现在我们使用registerForActivityResult。在返回中可以得知当前是否打开了蓝牙,因为是在Java中使用,因此我们写了一个registerIntent()方法,我们需要在onCreate之前调用这个方法,如图所示:

d4bb766fa8a649acad9b99a0fd47d71c.png

② 请求BLUETOOTH_CONNECT权限意图

registerForActivityResult不光能用于页面获取值,也能用于请求权限,先在MainActivity中声明变量,代码如下:

    private ActivityResultLauncher<String> requestBluetoothConnect; //请求蓝牙连接权限意图


然后在registerIntent()方法中,增加如下代码:

    //请求BLUETOOTH_CONNECT权限意图
        requestBluetoothConnect = registerForActivityResult(new ActivityResultContracts.RequestPermission(), result -> {
            if (result) {
                enableBluetooth.launch(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));
            } else {
                showMsg("Android12中未获取此权限,无法打开蓝牙。");
            }
        });


请求权限返回无非就是同意不同意,如果同意了我们就调用。

  enableBluetooth.launch(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));


去打开系统蓝牙,不同意就提示一下。下面再用ViewBinding来配置一下:

bf3e38b0c02b4004809613a39461cb24.png

下面我们再写一个initView()方法,在这个函数中我们对按钮的点击事件进行操作,新增initView()方法,代码如下:

    private void initView() {
        binding.btnOpenBluetooth.setOnClickListener(v -> {
            //蓝牙是否已打开
            if (isOpenBluetooth()) {
                showMsg("蓝牙已打开");
                return;
            }
            //是Android12
            if (isAndroid12()) {
                //检查是否有BLUETOOTH_CONNECT权限
                if (hasPermission(Manifest.permission.BLUETOOTH_CONNECT)) {
                    //打开蓝牙
                    enableBluetooth.launch(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));
                } else {
                    //请求权限
                    requestBluetoothConnect.launch(Manifest.permission.BLUETOOTH_CONNECT);
                }
                return;
            }
            //不是Android12 直接打开蓝牙
            enableBluetooth.launch(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));
        });
    }


  这里的代码就比较好理解,首先判断蓝牙是否已经打开了,打开了就不往下执行,没打开,再判断当前是否为Android12,不是就直接打开系统蓝牙,是Android12,再去检查是否授予BLUETOOTH_CONNECT权限,授予了就打开系统蓝牙,没有授予就去请求此权限,不要忘记在onCreate()方法中调用它。

1b50db0969c24f7baf8b3510faa0315a.png

下面我们运行一下:

b694a022e23d476baac11c04c8de5ead.gif


Android 12 蓝牙适配 Java版(下)https://developer.aliyun.com/article/1407523

相关文章
|
2月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
121 1
|
2月前
|
调度 Android开发 UED
Android经典实战之Android 14前台服务适配
本文介绍了在Android 14中适配前台服务的关键步骤与最佳实践,包括指定服务类型、请求权限、优化用户体验及使用WorkManager等。通过遵循这些指南,确保应用在新系统上顺畅运行并提升用户体验。
183 6
|
3月前
|
存储 搜索推荐 Java
探索安卓开发中的自定义视图:打造个性化UI组件Java中的异常处理:从基础到高级
【8月更文挑战第29天】在安卓应用的海洋中,一个独特的用户界面(UI)能让应用脱颖而出。自定义视图是实现这一目标的强大工具。本文将通过一个简单的自定义计数器视图示例,展示如何从零开始创建一个具有独特风格和功能的安卓UI组件,并讨论在此过程中涉及的设计原则、性能优化和兼容性问题。准备好让你的应用与众不同了吗?让我们开始吧!
|
3月前
|
Android开发
Android 配置蓝牙遥控器键值
本文详细介绍了Android系统中配置蓝牙遥控器键值的步骤,包括查看设备号、配置键位映射文件(kl文件)、部署kl文件以及调试过程,确保蓝牙遥控器的按键能正确映射到Android系统对应的按键功能。
131 1
|
3月前
|
Java 调度 Android开发
Android经典实战之Kotlin的delay函数和Java中的Thread.sleep有什么不同?
本文介绍了 Kotlin 中的 `delay` 函数与 Java 中 `Thread.sleep` 方法的区别。两者均可暂停代码执行,但 `delay` 适用于协程,非阻塞且高效;`Thread.sleep` 则阻塞当前线程。理解这些差异有助于提高程序效率与可读性。
71 1
|
3月前
|
Java Android开发
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
455 1
|
3月前
|
Android开发
Cannot create android app from an archive...containing both DEX and Java-bytecode content
Cannot create android app from an archive...containing both DEX and Java-bytecode content
35 2
|
3月前
|
IDE Java Linux
探索安卓开发:从基础到进阶的旅程Java中的异常处理:从基础到高级
【8月更文挑战第30天】在这个数字时代,移动应用已经成为我们日常生活中不可或缺的一部分。安卓系统由于其开放性和灵活性,成为了开发者的首选平台之一。本文将带领读者踏上一段从零开始的安卓开发之旅,通过深入浅出的方式介绍安卓开发的基础知识、核心概念以及进阶技巧。我们将一起构建一个简单的安卓应用,并探讨如何优化代码以提高性能和应用的用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供宝贵的知识和启发。
|
4月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
【7月更文挑战第28天】在 Android 开发中, NDK 让 Java 与 C++ 混合编程成为可能, 从而提升应用性能。**为何选 NDK?** C++ 在执行效率与内存管理上优于 Java, 特别适合高性能需求场景。**环境搭建** 需 Android Studio 和 NDK, 工具如 CMake。**JNI** 构建 Java-C++ 交互, 通过声明 `native` 方法并在 C++ 中实现。**实战** 示例: 使用 C++ 计算斐波那契数列以提高效率。**总结** 混合编程增强性能, 但增加复杂性, 使用前需谨慎评估。
140 4
|
4月前
|
SQL Java Unix
Android经典面试题之Java中获取时间戳的方式有哪些?有什么区别?
在Java中获取时间戳有多种方式,包括`System.currentTimeMillis()`(毫秒级,适用于日志和计时)、`System.nanoTime()`(纳秒级,高精度计时)、`Instant.now().toEpochMilli()`(毫秒级,ISO-8601标准)和`Instant.now().getEpochSecond()`(秒级)。`Timestamp.valueOf(LocalDateTime.now()).getTime()`适用于数据库操作。选择方法取决于精度、用途和时间起点的需求。
63 3