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
相关产品推荐
相关产品推荐

