React Native应用被杀后Beacon扫描启动却无法检测信标问题
React Native应用被杀后无法检测Beacon的问题
我正在开发一款React Native应用,要求即使应用被杀也能检测Beacon。当前实现下,应用在前台或后台时可正常检测Beacon,但被杀后无任何反应。不过logcat中出现以下日志:
2022-09-15 06:12:48.869 11188-11188/com.package.name I/ScanJob: ScanJob Lifecycle START: org.altbeacon.beacon.service.ScanJob@c368a4 2022-09-15 06:12:48.887 11188-12719/com.package.name I/BeaconManager: BeaconManager started up on pid 11188 named 'com.package.namep' for application package 'com.package.name'. isMainProcess=true 2022-09-15 06:12:48.899 11188-12719/com.package.name D/BeaconParser: Parsing beacon layout: m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25 2022-09-15 06:12:48.993 11188-12719/com.tipme.app I/CycledLeScanner: Using Android O scanner 2022-09-15 06:12:49.001 11188-12719/com.package.name I/ScanJob: Using immediateScanJobId from manifest: 208352939 2022-09-15 06:12:49.001 11188-12719/com.package.name I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@c368a4 2022-09-15 06:12:49.001 11188-12719/com.package.name I/ScanJob: scanJob version 2.19.4 is starting up on the main process 2022-09-15 06:12:49.010 11188-12719/com.package.name W/ModelSpecificDistanceCalculator: Cannot find match for this device. Using default 2022-09-15 06:12:49.011 11188-12719/com.package.name W/ModelSpecificDistanceCalculator: Cannot find match for this device. Using default 2022-09-15 06:12:49.029 11188-12719/com.package.name I/BluetoothAdapter: BluetoothAdapter() : com.package.name 2022-09-15 06:12:49.030 11188-12719/com.package.name I/BluetoothAdapter: STATE_ON 2022-09-15 06:12:49.034 11188-12719/com.package.name I/BluetoothAdapter: STATE_ON 2022-09-15 06:12:49.035 11188-12719/com.package.name D/BluetoothLeScanner: Stop Scan with callback intent 2022-09-15 06:12:49.056 11188-12719/com.package.name I/BluetoothAdapter: STATE_ON 2022-09-15 06:12:49.057 11188-12720/com.package.name I/BluetoothAdapter: STATE_ON 2022-09-15 06:12:49.058 11188-12720/com.package.name I/BluetoothAdapter: STATE_ON 2022-09-15 06:12:49.060 11188-12720/com.package.name I/BluetoothAdapter: STATE_ON 2022-09-15 06:12:49.060 11188-12719/com.package.name I/ScanJob: Scan job running for 15000 millis 2022-09-15 06:12:49.061 11188-12720/com.package.name D/BluetoothLeScanner: Start Scan with callback 2022-09-15 06:12:49.068 11188-11336/com.package.name D/BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=10 mScannerId=0 2022-09-15 06:12:49.216 11188-11336/com.package.name I/BluetoothAdapter: BluetoothAdapter() : com.package.name 2022-09-15 06:12:49.278 11188-12722/com.package.name I/ScanHelper: Non-distinct packets detected in a single scan. Restarting scans unecessary. 2022-09-15 06:13:04.062 11188-11188/com.package.name I/ScanJob: Scan job runtime expired: org.altbeacon.beacon.service.ScanJob@c368a4 2022-09-15 06:13:04.062 11188-11188/com.package.name I/BluetoothAdapter: STATE_ON 2022-09-15 06:13:04.065 11188-11188/com.package.name I/BluetoothAdapter: STATE_ON 2022-09-15 06:13:04.066 11188-11188/com.package.name D/BluetoothLeScanner: Stop Scan with callback intent 2022-09-15 06:13:04.071 11188-12720/com.package.name I/BluetoothAdapter: STATE_ON 2022-09-15 06:13:04.071 11188-12720/com.package.name D/BluetoothLeScanner: Stop Scan with callback 2022-09-15 06:13:04.125 11188-11188/com.package.name I/ScanJob: We are inside a beacon region. We will not scan between cycles.
该日志在应用被杀约30分钟后出现,这是默认行为,说明扫描任务已成功启动,但未检测到信标,也未调用推送通知方法。请问是我的实现存在问题,还是React Native不支持该功能?
public class ListenerModule extends ReactContextBaseJavaModule { private static final String TAG = "TipmeListener"; private final ReactApplicationContext mReactContext; private BeaconManager beaconManager; private static final String BEACON_UUID = "76b08b60-3322-4534-a3bf-2d10adc8ebd2"; private static final String BEACON_LAYOUT = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"; private final static String tipmeID = "some-unique-string"; private final static String tipmeChannelID = "some-unique-string-channel"; private final Region region = new Region(tipmeID, Identifier.parse(BEACON_UUID), null, null); private final HashMap<Integer, LocalDateTime> pastBeacons = new HashMap<>(); public ListenerModule (ReactApplicationContext context) { super(context); this.mReactContext = context; } @Override public void initialize() { this.beaconManager = BeaconManager.getInstanceForApplication(mReactContext); beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(BEACON_LAYOUT)); beaconManager.setBackgroundBetweenScanPeriod(0); beaconManager.setBackgroundScanPeriod(1100); } @NonNull @Override public String getName() { return TAG; } // Monitor notifier private MonitorNotifier monitorNotifier = new MonitorNotifier() { @Override public void didEnterRegion(Region region) { Log.d(TAG, "didEnterRegion"); beaconManager.startRangingBeacons(region); } @Override public void didExitRegion(Region region) { Log.d(TAG, "didExitRegion"); } @Override public void didDetermineStateForRegion(int state, Region region) { Log.d(TAG, "didDetermineStateForRegion"); if (state == MonitorNotifier.INSIDE) { beaconManager.startRangingBeacons(region); } } }; // Ranging notifier RangeNotifier rangeNotifier = new RangeNotifier() { @Override public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) { if (beacons.size() > 0) { sendNotification(); Log.d(TAG, "didRangeBeaconsInRegion called with beacon count: "+beaconsToNotify.size()); } } }; @ReactMethod public void start() { try { beaconManager.addMonitorNotifier( monitorNotifier); beaconManager.addRangeNotifier(rangeNotifier); beaconManager.startMonitoring(region); beaconManager.startRangingBeacons(region); Log.d(TAG, "start called"); } catch (Exception e) { Log.e(TAG, "Error while starting beacon monitoring", e); } } @ReactMethod public void stop() { try { this.beaconManager.stopMonitoring(region); this.beaconManager.stopRangingBeacons(region); } catch (Exception e) { Log.e(TAG, "Error while stopping beacon monitoring", e); } } private void sendNotification() { NotificationManager notificationManager = (NotificationManager) this.mReactContext.getSystemService(Context.NOTIFICATION_SERVICE); Notification.Builder builder; NotificationChannel channel = new NotificationChannel(tipmeID, tipmeID, NotificationManager.IMPORTANCE_HIGH); channel.enableLights(true); channel.enableVibration(true); channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); notificationManager.createNotificationChannel(channel); builder = new Notification.Builder(this.mReactContext, channel.getId()); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.mReactContext); stackBuilder.addNextIntent(new Intent(this.mReactContext, MainActivity.class)); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent( 0, PendingIntent.FLAG_UPDATE_CURRENT ); builder.setSmallIcon(R.drawable.ic_stat_onesignal_default); builder.setContentTitle("A new Performance"); builder.setContentText("There is a new performance nearby, click to see more"); builder.setContentIntent(resultPendingIntent); notificationManager.notify(1, builder.build()); } }
问题分析与解决方案
核心问题:应用被杀后监听器未自动注册
从日志能看到AltBeacon的ScanJob确实在应用被杀后启动了,但你的MonitorNotifier和RangeNotifier仅在JS层调用start()方法时才注册。应用被杀后重启(由ScanJob触发),JS层不会自动执行,start()方法不会被调用,导致监听器没有绑定到BeaconManager,自然无法触发回调和通知。
具体修复步骤
1. 模块初始化时自动注册监听器
修改initialize()方法,在模块初始化阶段就完成监听器注册和扫描启动,无需依赖JS层触发:
@Override public void initialize() { this.beaconManager = BeaconManager.getInstanceForApplication(mReactContext); beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(BEACON_LAYOUT)); beaconManager.setBackgroundBetweenScanPeriod(0); beaconManager.setBackgroundScanPeriod(1100); // 直接注册监听器并启动扫描 beaconManager.addMonitorNotifier(monitorNotifier); beaconManager.addRangeNotifier(rangeNotifier); try { beaconManager.startMonitoring(region); beaconManager.startRangingBeacons(region); Log.d(TAG, "Beacon monitoring initialized automatically"); } catch (Exception e) { Log.e(TAG, "Error initializing beacon monitoring", e); } }
2. 修复代码变量错误
你的RangeNotifier中使用了未定义的beaconsToNotify变量,会导致运行时崩溃,修正如下:
RangeNotifier rangeNotifier = new RangeNotifier() { @Override public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) { if (beacons.size() > 0) { sendNotification(); Log.d(TAG, "didRangeBeaconsInRegion called with beacon count: "+beacons.size()); } } };
3. 完善Android权限与服务配置
在AndroidManifest.xml中添加必要权限和AltBeacon服务配置:
<!-- 蓝牙与位置权限 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- AltBeacon后台服务配置 --> <service android:name="org.altbeacon.beacon.service.BeaconService" android:enabled="true"/> <service android:name="org.altbeacon.beacon.service.ScanJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:enabled="true"/> <receiver android:name="org.altbeacon.beacon.startup.StartupBroadcastReceiver" android:enabled="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/> </intent-filter> </receiver>
4. 处理Android后台限制
- Android 8+需确保应用获取后台位置权限,否则后台扫描会被系统限制。
- 部分国产ROM(小米、华为等)有额外后台管控,需将应用加入后台运行白名单,允许后台持续运行。
结论
React Native本身完全支持后台Beacon检测,问题出在你的实现逻辑上:监听器依赖JS层触发注册,应用被杀后无法自动恢复。通过上述修改,即可实现应用被杀后仍能检测Beacon并发送通知。
内容的提问来源于stack exchange,提问作者Emmanuel Yeboah
相关产品推荐
相关产品推荐

