You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android:如何通过Application类启用蓝牙并维持持久连接?

解决Application类中蓝牙权限/启用的问题,实现持久蓝牙连接

嘿,我明白你想让蓝牙连接在整个应用生命周期保持持久的需求,但在Application类里处理权限请求和蓝牙启用确实踩了个常见的坑——Application类并不适合做需要用户交互的操作,因为它没有Activity的生命周期上下文,没法处理权限请求或蓝牙启用的回调结果。下面给你梳理下正确的实现思路和代码:

核心问题分析

Application是应用启动时最早创建的组件,但它没有界面,也无法处理startActivityForResult(或新版的Activity Result API)的回调,而蓝牙权限请求、蓝牙启用请求都需要用户交互,必须依托Activity的上下文才能完成。

正确实现步骤

1. 先在Manifest中声明必要权限

根据你的Android目标版本,添加对应的蓝牙权限:

<!-- 基础蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 10+ 蓝牙扫描需要位置权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Android 12+ 新增的蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

<!-- 声明应用使用蓝牙功能 -->
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />

2. 在启动Activity中处理权限和蓝牙启用

把权限请求、蓝牙启用的逻辑放在应用启动的第一个Activity(比如SplashActivity或MainActivity)里,这里可以正常处理用户交互和回调:

import android.Manifest
import android.bluetooth.BluetoothAdapter
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.activity.result.contract.ActivityResultContracts

class SplashActivity : AppCompatActivity() {
    // 注册蓝牙启用的Activity Result回调
    private val enableBluetoothLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == RESULT_OK) {
            // 蓝牙已启用,现在初始化蓝牙连接
            (application as MyApp).initBluetoothConnection()
            // 跳转到主界面
            startActivity(Intent(this, MainActivity::class.java))
            finish()
        } else {
            // 用户拒绝启用蓝牙,可以提示或退出应用
        }
    }

    // 注册权限请求的回调
    private val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        val allGranted = permissions.all { it.value }
        if (allGranted) {
            checkBluetoothStatus()
        } else {
            // 用户拒绝权限,提示并处理
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)
        
        // 检查权限和蓝牙状态
        checkPermissions()
    }

    private fun checkPermissions() {
        val requiredPermissions = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
            arrayOf(
                Manifest.permission.BLUETOOTH_CONNECT,
                Manifest.permission.BLUETOOTH_SCAN
            )
        } else {
            arrayOf(
                Manifest.permission.BLUETOOTH_ADMIN,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
        }

        val missingPermissions = requiredPermissions.filter {
            ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
        }

        if (missingPermissions.isNotEmpty()) {
            requestPermissionLauncher.launch(missingPermissions.toTypedArray())
        } else {
            checkBluetoothStatus()
        }
    }

    private fun checkBluetoothStatus() {
        val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        if (bluetoothAdapter == null) {
            // 设备不支持蓝牙
            return
        }
        if (!bluetoothAdapter.isEnabled) {
            // 请求用户启用蓝牙
            val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
            enableBluetoothLauncher.launch(enableBtIntent)
        } else {
            // 蓝牙已启用,初始化连接
            (application as MyApp).initBluetoothConnection()
            startActivity(Intent(this, MainActivity::class.java))
            finish()
        }
    }
}

3. 在Application类中管理持久蓝牙连接

当权限和蓝牙都就绪后,再在Application类中初始化并管理蓝牙连接,确保连接在整个应用生命周期保持:

import android.app.Application
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import java.io.IOException
import java.util.UUID

class MyApp : Application() {
    private lateinit var bluetoothSocket: BluetoothSocket
    // 替换成你的蓝牙设备UUID和目标设备地址
    private val MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
    private val DEVICE_ADDRESS = "XX:XX:XX:XX:XX:XX"

    fun initBluetoothConnection() {
        val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        val device = bluetoothAdapter.getRemoteDevice(DEVICE_ADDRESS)
        
        try {
            // 创建蓝牙Socket连接
            bluetoothSocket = device.createRfcommSocketToServiceRecord(MY_UUID)
            // 连接(注意:这是阻塞操作,建议放在子线程)
            Thread {
                try {
                    bluetoothSocket.connect()
                    // 连接成功,后续可以在这里处理数据读写
                } catch (e: IOException) {
                    // 连接失败,处理异常
                    try {
                        bluetoothSocket.close()
                    } catch (closeE: IOException) { }
                }
            }.start()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    // 提供获取Socket的方法,方便其他组件使用
    fun getBluetoothSocket(): BluetoothSocket? {
        return if (::bluetoothSocket.isInitialized && bluetoothSocket.isConnected) bluetoothSocket else null
    }

    override fun onTerminate() {
        super.onTerminate()
        // 应用销毁时关闭蓝牙连接
        try {
            bluetoothSocket.close()
        } catch (e: IOException) { }
    }
}

关键注意事项

  • 蓝牙连接必须放在子线程:connect()方法是阻塞操作,不能在主线程调用,否则会导致ANR。
  • 处理连接异常:蓝牙连接可能因为设备断开、信号差等原因失败,需要添加重连逻辑。
  • 后台蓝牙保持:如果需要应用在后台也保持蓝牙连接,可能需要结合前台服务,避免系统杀死进程。

内容的提问来源于stack exchange,提问作者RAR Raif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:40:12