Android熄屏状态下持续获取Sensor与GNSS数据的实现问题咨询
解决方案
1. 将普通Service改为前台服务(核心修改)
Android 10+ 长时间后台运行的任务必须使用前台服务,系统会为运行前台服务的进程提权,减少被后台查杀的概率。
步骤1:添加权限
在AndroidManifest.xml中添加以下权限:
<!-- 前台服务基础权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Android 10+ 后台采集位置所需权限 --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- 唤醒锁权限 --> <uses-permission android:name="android.permission.WAKE_LOCK" />
运行时权限申请除了原有的ACCESS_FINE_LOCATION,还要新增ACCESS_BACKGROUND_LOCATION,必须引导用户同意「始终允许位置访问」,否则熄屏后GNSS数据会被系统截断。
步骤2:启动时绑定前台通知
修改DataCollectorService的onStartCommand方法,启动服务时调用startForeground绑定常驻通知:
@RequiresApi(api = Build.VERSION_CODES.R) @Override public int onStartCommand(Intent intent, int flags, int startId) { // 注册前台服务通知渠道 String CHANNEL_ID = "sensor_collect_service"; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "传感器数据采集", NotificationManager.IMPORTANCE_HIGH ); channel.setSound(null, null); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("正在采集传感器/GNSS数据") .setContentText("关闭该通知会导致采集中断") .setSmallIcon(R.mipmap.ic_launcher) // 替换为你的应用图标 .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(Notification.CATEGORY_SERVICE) .build(); // 启动前台服务,通知ID不能为0 startForeground(1001, notification); // 原有逻辑补充参数持久化,避免进程重启后参数丢失 SharedPreferences sp = getSharedPreferences("collect_config", MODE_PRIVATE); if (intent != null) { appStartTime = intent.getLongExtra("startTime", 0); samplingFrequency = intent.getIntExtra("samplingFrequency", 10000); meanOfTrans = intent.getIntExtra("meanOfTrans", 0); // 存储参数到本地 sp.edit() .putLong("startTime", appStartTime) .putInt("samplingFrequency", samplingFrequency) .putInt("meanOfTrans", meanOfTrans) .apply(); } else { // 进程被系统杀掉重启时从本地读取参数 appStartTime = sp.getLong("startTime", System.currentTimeMillis()); samplingFrequency = sp.getInt("samplingFrequency", 10000); meanOfTrans = sp.getInt("meanOfTrans", 0); } if (initial) { registerSensors(); // WakeLock移到Service中作为成员变量持有,不要放在Activity中 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DLR_KN_MOBILITY_APP:SERVICE_WAKE_LOCK"); mWakeLock.acquire(150 * 60 * 1000L); // 150分钟,单位为毫秒 initial = false; } return START_STICKY; }
步骤3:修改服务启动方式
Android 8及以上版本启动前台服务需要调用startForegroundService,修改MainActivity的toggleBackgroundService方法:
if (on) { Intent serviceIntent = new Intent(this, DataCollectorService.class); serviceIntent.putExtra("startTime", appStartTime); serviceIntent.putExtra("samplingFrequency", samplingFrequency); serviceIntent.putExtra("meanOfTrans", meanOfTrans); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } }
步骤4:服务销毁时清理资源
修改DataCollectorService的onDestroy方法,释放资源:
@Override public void onDestroy() { super.onDestroy(); stopForeground(true); // 释放WakeLock if (mWakeLock != null && mWakeLock.isHeld()) { mWakeLock.release(); } unregisterReceiver(mReceiver); disableSensors(); }
2. 优化传感器回调逻辑
为传感器回调指定独立的工作线程,避免主线程被系统挂起导致回调中断:
// 在registerSensors方法中新增以下代码 HandlerThread sensorThread = new HandlerThread("sensorCallbackThread"); sensorThread.start(); Handler sensorHandler = new Handler(sensorThread.getLooper()); // 注册传感器时传入handler if (mAccelerometer != null) { mSensorManager.registerListener(this, mAccelerometer, samplingFrequency, sensorHandler); } // 其余传感器注册逻辑同理,都传入该handler
3. 厂商定制系统适配
你测试的机型中POCO属于小米系,有额外的后台限制,需要引导用户手动开启权限:
- 小米/POCO:进入应用信息页,开启「自启动权限」,将省电策略设置为「无限制」
- 三星:进入电池优化设置,将应用加入「不优化」列表
- 原生Pixel:仅需开启前台服务和后台位置权限即可正常运行
按照以上方案修改后,可实现熄屏状态下连续数小时的稳定数据采集,覆盖你需要的多场景测试需求。
内容的提问来源于stack exchange,提问作者Tarnum
相关产品推荐
相关产品推荐

