如何在Android Oreo中长时间运行后台服务且不被系统杀死?
嘿,这个问题我太有发言权了——自从Android Oreo把后台服务的生存空间砍得七零八落之后,想在后台持续跑个传感器监听确实挺头疼的,尤其是你还不想用前台服务的通知打扰用户。咱们来聊聊几个可行的方案,按靠谱程度排序:
辅助功能服务的进程优先级极高,系统几乎不会随便杀它,而且天然支持后台持续运行。你完全可以把传感器监听逻辑放在这里,完美适配你的摇晃开手电筒需求。
不过有个前提:得引导用户手动开启你的应用的辅助功能权限,这是敏感权限,没法自动授予,所以你得在应用里做个友好的引导,告诉用户怎么去系统设置里开启。
具体步骤:
首先在AndroidManifest.xml里声明服务:
<service android:name=".ShakeAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service>
然后在res/xml下创建accessibility_service_config.xml配置文件:
<?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeAllMask" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault" android:description="@string/accessibility_service_desc" />
这里的description要写清楚你的服务是干嘛的,比如“监听摇晃动作以控制手电筒”,用户在设置里能看到这个描述。
最后实现服务类:
class ShakeAccessibilityService : AccessibilityService() { private lateinit var sensorManager: SensorManager private var accelerometer: Sensor? = null private val shakeListener = object : SensorEventListener { private var lastShakeTime = 0L private val SHAKE_THRESHOLD = 800 // 摇晃阈值,可根据实际调整 private val SHAKE_INTERVAL = 1000 // 1秒内只响应一次,防止重复触发 override fun onSensorChanged(event: SensorEvent?) { event ?: return if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) { val x = event.values[0] val y = event.values[1] val z = event.values[2] // 计算加速度变化 val acceleration = abs(x + y + z - 9.81) * 1000 val currentTime = System.currentTimeMillis() if (acceleration > SHAKE_THRESHOLD && currentTime - lastShakeTime > SHAKE_INTERVAL) { lastShakeTime = currentTime // 触发手电筒开关逻辑 toggleFlashlight() } } } override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} } override fun onServiceConnected() { super.onServiceConnected() sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) accelerometer?.let { sensorManager.registerListener(shakeListener, it, SensorManager.SENSOR_DELAY_NORMAL) } } override fun onInterrupt() {} override fun onDestroy() { super.onDestroy() sensorManager.unregisterListener(shakeListener) } private fun toggleFlashlight() { // 记得申请CAMERA权限 val cameraManager = getSystemService(CAMERA_SERVICE) as CameraManager val cameraId = cameraManager.cameraIdList[0] val isFlashOn = cameraManager.getTorchMode(cameraId) == CameraManager.TORCH_MODE_ON cameraManager.setTorchMode(cameraId, !isFlashOn) } }
虽然你说不想用前台服务,但有些厂商允许创建一个几乎看不见的通知来满足前台服务的要求。不过这个方法踩坑点很多:不同厂商系统表现不一,而且Google Play可能判定这是欺骗用户,导致应用被拒。
如果只是自己玩玩或者小众分发,可以试试:
private fun startStealthForegroundService() { // 创建一个透明图标,注意要真·透明,不然会显示系统默认图标 val notification = NotificationCompat.Builder(this, "STEALTH_CHANNEL") .setSmallIcon(R.drawable.ic_transparent) .setContentTitle("") .setContentText("") .setPriority(NotificationCompat.PRIORITY_MIN) .setVisibility(NotificationCompat.VISIBILITY_SECRET) .build() startForeground(1, notification) }
再次强调:这个方法不适合上线的应用,风险很高。
你可以在自定义Application的onCreate()里注册传感器监听器,这样只要应用进程活着就能监听。但问题是,Oreo及以上系统在应用后台时,很容易杀死闲置进程,尤其是内存不足的时候。
可以配合监听屏幕状态来优化,比如屏幕亮时重新注册,屏幕灭时取消监听,能稍微提高存活概率:
class MyApp : Application() { private lateinit var sensorManager: SensorManager private var accelerometer: Sensor? = null private val shakeListener = object : SensorEventListener { // 和之前的监听器逻辑一样 } override fun onCreate() { super.onCreate() sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) registerShakeListener() // 监听屏幕状态 val screenFilter = IntentFilter(Intent.ACTION_SCREEN_ON) screenFilter.addAction(Intent.ACTION_SCREEN_OFF) registerReceiver(screenReceiver, screenFilter) } private fun registerShakeListener() { accelerometer?.let { sensorManager.registerListener(shakeListener, it, SensorManager.SENSOR_DELAY_NORMAL) } } private fun unregisterShakeListener() { sensorManager.unregisterListener(shakeListener) } private val screenReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { when (intent?.action) { Intent.ACTION_SCREEN_ON -> registerShakeListener() Intent.ACTION_SCREEN_OFF -> unregisterShakeListener() } } } }
这个方案的保活效果最差,只能作为备选。
总的来说,最合规、最稳定的方案就是用辅助功能服务,虽然需要用户手动授权,但这是系统允许的正规路径,不会有被封杀的风险。
内容的提问来源于stack exchange,提问作者Gurleen Sethi

