前台服务被Doze模式终止,无法持续上传位置求助
问题描述
我在应用进入后台时启动前台服务,每10秒向服务器发送一次经纬度。但该服务仅在应用处于前台或后台(屏幕开启状态)时能正常发送数据,当屏幕关闭一段时间后便停止工作,前台服务也被清除。我已将应用加入电池优化白名单,但问题仍未解决。
位置回调中启动服务的代码
locationCallback = object : LocationCallback() { override fun onLocationResult(location: LocationResult) { super.onLocationResult(location) val latitude = location.lastLocation.latitude val longitude = location.lastLocation.longitude val serviceIntent = Intent(this@MainActivity,LocationService::class.java) serviceIntent.putExtra("latitude",latitude) serviceIntent.putExtra("longitude",longitude) startService(serviceIntent) }
前台服务及数据上报代码
@AndroidEntryPoint class LocationService : Service() { @Inject lateinit var sharedPreferences : SharedPreferences override fun onBind(intent: Intent?): IBinder? = null @SuppressLint("NewApi") override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val latitude = intent?.getDoubleExtra("latitude",0.0) val longitude = intent?.getDoubleExtra("longitude",0.0) postValuesToServer(latitude!!, longitude!!) showNotification(latitude,longitude) return START_STICKY } @RequiresApi(Build.VERSION_CODES.M) private fun showNotification(latitude: Double, longitude: Double) { val intent = Intent(this,MainActivity::class.java) val pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_IMMUTABLE) val notificationCompat = NotificationCompat.Builder(this,Utils.NOTIFICATION_ID) .setContentTitle("经度 : $longitude") .setContentText("纬度 : $latitude") .setSmallIcon(R.drawable.ic_baseline_circle_notifications_24) .setContentIntent(pendingIntent) .build() if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ startForeground(1,notificationCompat) } else { NotificationManagerCompat.from(this).notify(1,notificationCompat) } }
清单文件中服务注册配置
<service android:name=".services.LocationService" android:foregroundServiceType="location|dataSync"/>
解决方案
1. 修正前台服务启动逻辑
当前showNotification方法仅在Android O+版本将服务设为前台,低版本只是发送普通通知,导致服务未真正进入前台模式,系统仍可能在后台杀死它。修改为所有版本都调用startForeground:
@RequiresApi(Build.VERSION_CODES.M) private fun showNotification(latitude: Double, longitude: Double) { val intent = Intent(this,MainActivity::class.java) val pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_IMMUTABLE) val notificationCompat = NotificationCompat.Builder(this,Utils.NOTIFICATION_ID) .setContentTitle("经度 : $longitude") .setContentText("纬度 : $latitude") .setSmallIcon(R.drawable.ic_baseline_circle_notifications_24) .setContentIntent(pendingIntent) .build() // 所有版本统一调用startForeground,确保服务处于前台状态 startForeground(1, notificationCompat) }
2. 确保屏幕关闭时位置更新不被节流
当前依赖LocationCallback触发服务启动,但屏幕关闭后系统可能对位置更新进行节流。需要配置更高优先级的位置请求参数,并确保申请后台位置权限:
// 创建位置请求时设置高优先级和短间隔 val locationRequest = LocationRequest.create().apply { interval = 10000 // 10秒间隔 fastestInterval = 5000 // 最快5秒更新一次 priority = LocationRequest.PRIORITY_HIGH_ACCURACY // 适配Android 12+的后台位置更新限制 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { setMinUpdateIntervalMillis(10000) } } // 请求持续位置更新 LocationServices.getFusedLocationProviderClient(this) .requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
注意:Android 10+需在清单文件中声明ACCESS_BACKGROUND_LOCATION权限,并在运行时引导用户授予。
3. 修复服务重启时的空指针问题
START_STICKY会在服务被系统杀死后重启,但重启时intent为null,当前代码强制解包latitude!!会导致崩溃。修改onStartCommand逻辑:
@SuppressLint("NewApi") override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val latitude = intent?.getDoubleExtra("latitude", 0.0) val longitude = intent?.getDoubleExtra("longitude", 0.0) // 有位置数据则直接上报,否则读取本地缓存 if (latitude != null && longitude != null) { postValuesToServer(latitude, longitude) // 保存位置到本地缓存 sharedPreferences.edit() .putFloat("last_lat", latitude.toFloat()) .putFloat("last_lng", longitude.toFloat()) .apply() showNotification(latitude,longitude) } else { // 读取本地缓存的最后位置 val lastLat = sharedPreferences.getFloat("last_lat", 0f).toDouble() val lastLng = sharedPreferences.getFloat("last_lng", 0f).toDouble() if (lastLat != 0.0 && lastLng != 0.0) { postValuesToServer(lastLat, lastLng) showNotification(lastLat, lastLng) } } return START_STICKY }
4. 适配厂商后台管控
即使加入电池优化白名单,小米、华为、OPPO等国产厂商仍有独立的后台管控机制,需要引导用户手动开启后台运行权限或自启动权限。可在应用内添加引导页面,提示用户前往系统设置开启对应权限。
内容的提问来源于stack exchange,提问作者Taki
相关产品推荐
相关产品推荐

