Android应用后台/被杀死时BeaconManagerListener停止运行如何解决
问题原因及修复方案
现有代码核心问题
- BeaconManagerListener被声明为TimerTask内部的局部变量,没有全局持有引用,当应用退到后台后,该实例很容易被系统GC回收,导致回调无法触发
- 重复注册Listener:每隔2秒重复执行
setBeaconManagerListener替换原有监听,属于完全不必要的逻辑,反而会导致回调断连 - 服务重启策略错误:
onStartCommand返回START_NOT_STICKY,当服务被系统资源回收后不会自动重启,无法保持后台持续运行 - 缺少后台运行必要权限:Android 10及以上版本需要后台位置权限才能在后台扫描蓝牙Beacon,Android 12及以上还需要专门的蓝牙扫描权限
- 未处理厂商系统限制:国内主流厂商(华为、小米、OPPO、VIVO等)的系统默认会限制后台应用的蓝牙扫描行为,未加入电池优化白名单的应用退后台后扫描功能会被系统拦截
具体修复步骤
- 调整Listener的持有逻辑
将BeaconManagerListener和Timer都改为Service的全局成员变量,避免被GC回收,同时移除TimerTask内部重复注册Listener的逻辑,仅在服务启动时注册一次监听。 - 修改服务重启策略
将onStartCommand的返回值改为START_STICKY,服务被系统杀死后会自动重启,保持检测能力。 - 补全所需权限
在AndroidManifest.xml中添加以下权限:
<!-- 基础蓝牙权限 --> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <!-- Android 12+ 蓝牙扫描权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- 位置权限,Beacon扫描必需 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- Android 10+ 后台位置权限 --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
同时要在应用启动时主动请求用户授权上述所有权限,缺少任意权限都会导致后台扫描失效。
4. 引导用户关闭电池优化
应用首次启动时引导用户将应用加入系统电池优化白名单,避免厂商系统拦截后台扫描行为。
修正后的核心Service代码示例
public class MyService extends Service { public static final String CHANNEL_ID = "ForegroundServiceChannel"; int service_timer=0; Timer timer = new Timer(); SensoroManager sensoroManager; // 全局持有Listener引用,避免GC回收 BeaconManagerListener beaconManagerListener; @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { sensoroManager = SensoroManager.getInstance(MyService.this); String input = intent.getStringExtra("inputExtra"); System.out.println("Sensoro 2" ); createNotificationChannel(); Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("Foreground Service") .setContentText(input) .setSmallIcon(R.drawable.ic_launcher_background) .setContentIntent(pendingIntent) .build(); startForeground(1,notification); // 初始化Listener,仅注册一次 initBeaconListener(); if (sensoroManager.isBluetoothEnabled()) { sensoroManager.setCloudServiceEnable(true); try { sensoroManager.startService(); } catch (Exception e) { e.printStackTrace(); } } else { Toast.makeText(MyService.this,"Bluetooth off",Toast.LENGTH_LONG).show(); } // 仅保留日志打印逻辑,移除重复注册Listener的代码 new Timer().scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println("Sensoro 2" ); service_timer++; } }, 0, 2000); // 修改为START_STICKY,服务被杀后自动重启 return START_STICKY; } private void initBeaconListener() { beaconManagerListener = new BeaconManagerListener() { @Override public void onUpdateBeacon(ArrayList<Beacon> beacons) { for (Beacon beacon : beacons) { System.out.println("Sensoro 3" ); } } @Override public void onNewBeacon(Beacon beacon) { if (beacon.getSerialNumber().equals("0117C59B243C")){ System.out.println("Sensoro 3" ); System.out.println("Sensoro acc" +beacon.getAccuracy()); } } @Override public void onGoneBeacon(Beacon beacon) { if (beacon.getSerialNumber().equals("0117C59B243C")){ System.out.println("Sensoro acc gone"); System.out.println("Sensoro acc Timer" +service_timer); } } }; sensoroManager.setBeaconManagerListener(beaconManagerListener); } @Override public void onDestroy() { super.onDestroy(); // 服务销毁时解绑Listener,避免内存泄漏 if (sensoroManager != null) { sensoroManager.setBeaconManagerListener(null); } } @Nullable @Override public IBinder onBind(Intent intent) { return null; } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel serviceChannel = new NotificationChannel( CHANNEL_ID, "Foreground Service Channel", NotificationManager.IMPORTANCE_DEFAULT ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(serviceChannel); } } }
内容的提问来源于stack exchange,提问作者Saleh Refaai
相关产品推荐
相关产品推荐

