在Ionic Capacitor中添加Android后台蓝牙服务的技术求助
Capacitor Android 后台BLE监听解决方案
一、正确的目录结构与服务创建
Capacitor生成的Android项目是标准Android Studio结构,自定义服务必须放在与MainActivity同包路径下:
- 打开项目根目录下的
android文件夹(用Android Studio打开) - 找到包名目录(比如
app/src/main/java/com/your_app_id/,MainActivity就在这里) - 右键该目录 → New → Service → Service,命名为
BLEBackgroundService
二、编写前台服务代码(适配Android 12+)
Android 12+对后台服务限制极严,必须用前台服务才能持续运行。以下是核心代码示例:
package com.your_app_id; // 必须和MainActivity同包名 import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Intent; import android.os.Build; import android.os.IBinder; import android.bluetooth.BluetoothAdapter; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.pm.PackageManager; public class BLEBackgroundService extends Service { private static final int NOTIFICATION_ID = 1001; private static final String CHANNEL_ID = "BLE_SCAN_CHANNEL"; private BluetoothLeScanner scanner; @Override public void onCreate() { super.onCreate(); // 创建通知通道(Android 8+要求) createNotificationChannel(); // 启动前台服务 startForeground(NOTIFICATION_ID, buildNotification()); // 初始化BLE扫描 initBLEScanner(); } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "BLE后台扫描", NotificationManager.IMPORTANCE_LOW ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } private Notification buildNotification() { return new Notification.Builder(this, CHANNEL_ID) .setContentTitle("BLE后台扫描中") .setContentText("正在监听目标设备") .setSmallIcon(R.mipmap.ic_launcher) .build(); } private void initBLEScanner() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter == null || !adapter.isEnabled()) return; scanner = adapter.getBluetoothLeScanner(); ScanSettings settings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .build(); // 开始扫描 scanner.startScan(null, settings, scanCallback); } private ScanCallback scanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); // 过滤目标设备(根据UUID或设备名) if (result.getDevice().getName() != null && result.getDevice().getName().contains("目标设备标识")) { // 发送广播通知前端 Intent broadcastIntent = new Intent("BLE_DEVICE_FOUND"); broadcastIntent.putExtra("device_address", result.getDevice().getAddress()); sendBroadcast(broadcastIntent); // 可选:发现后暂停扫描,连接完成后重启 scanner.stopScan(this); } } }; @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; // 服务被杀死后自动重启 } @Override public IBinder onBind(Intent intent) { return null; // 无需绑定,返回null } @Override public void onDestroy() { super.onDestroy(); if (scanner != null) scanner.stopScan(scanCallback); } }
三、注册服务与权限配置
在app/src/main/AndroidManifest.xml中添加以下内容:
- 注册服务(放在
<application>标签内):
<service android:name=".BLEBackgroundService" android:foregroundServiceType="bluetooth_scan" />
- 添加必要权限(放在
<manifest>标签内,<application>之前):
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- Android 11及以下需要位置权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
四、在应用启动时启动服务
避免直接修改Capacitor自动生成的MainActivity.java,创建自定义Activity继承CapacitorActivity防止被覆盖:
- 在同包路径下创建
CustomMainActivity.java:
package com.your_app_id; import android.content.Intent; import android.os.Bundle; import com.getcapacitor.BridgeActivity; public class CustomMainActivity extends BridgeActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 启动BLE后台服务 Intent serviceIntent = new Intent(this, BLEBackgroundService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } } }
- 修改
AndroidManifest.xml中的启动Activity为自定义的:
<activity android:name=".CustomMainActivity" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:launchMode="singleTask" android:theme="@style/AppTheme.NoActionBarLaunch" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 注释或删除原来的MainActivity启动配置 -->
五、前端(Angular)接收设备发现事件
安装@capacitor-community/broadcast-receiver插件后,在Angular代码中监听广播:
import { BroadcastReceiver } from '@capacitor-community/broadcast-receiver'; import { BLE } from '@capacitor-community/ble'; async ngOnInit() { await BroadcastReceiver.addListener({ action: 'BLE_DEVICE_FOUND' }, (data) => { const deviceAddress = data.extras.device_address; // 调用BLE插件连接设备 this.connectDevice(deviceAddress); }); } async connectDevice(address: string) { // 你的BLE连接逻辑 await BLE.connect({ deviceId: address }); }
注意事项
- Android 12+必须指定
foregroundServiceType,否则前台服务无法启动 - 后台扫描需确保权限申请完整,尤其是
BLUETOOTH_SCAN的权限标志 START_STICKY仅在系统资源充足时重启服务,可搭配WorkManager做兜底扫描
内容的提问来源于stack exchange,提问作者Kelley B
相关产品推荐
相关产品推荐

