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

在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中添加以下内容:

  1. 注册服务(放在<application>标签内):
<service android:name=".BLEBackgroundService" android:foregroundServiceType="bluetooth_scan" />
  1. 添加必要权限(放在<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防止被覆盖:

  1. 在同包路径下创建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);
        }
    }
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 16:35:24