华为P9 Lite移除最近任务时重复重建STICKY Service与Application问题求助
我在华为P9 Lite上测试应用时遇到了一个既特殊又恼人的问题:当运行STICKY Service(onStartCommand方法返回START_STICKY标记)时,从最近任务列表移除应用任务,会触发Application和Service一同重建,即原有应用实例变为[DEAD]的同时,新应用实例的onCreate方法会被调用。
我已经按照相关指引,在系统设置中关闭了系统优化和高耗电应用自动清理功能。
我专门针对该问题编写了测试应用,对所有可重写的方法调用都做了日志记录,应用运行流程日志如下:
*** Starting the service *** I/MainService: onCreate I/MainService: onStartCommand *** Opening recent apps *** I/MainActivity: onPause I/MainActivity: onStop I/App: onTrimMemory I/MainService: onTrimMemory *** Removing my app from recents *** I/MainActivity: onDestroy I/MainService: onTaskRemoved *** app becomes [DEAD] *** *** New instance of my app and service is recreated (actually 3 times???) *** I/App: onCreate I/MainService: onCreate I/MainService: onStartCommand I/App: onCreate I/MainService: onCreate I/MainService: onStartCommand I/App: onCreate I/MainService: onCreate I/MainService: onStartCommand
对部分场景来说这个问题影响不大,因为很多服务的业务逻辑对重启并不敏感。但我的实际商用应用中,该服务需要维持与Bluetooth Low Energy设备的长连接,多次强制重连会导致蓝牙设备停止响应,完全无法正常使用。
目前该问题仅在华为P9 Lite设备上复现,小米等其他国产厂商的设备均不会触发该行为。我也发现有其他应用可以规避该问题,说明存在可行的解决方案,但我暂未找到对应实现方法。
如果您遇到过类似问题(不限华为设备)并成功解决,恳请提供帮助。
核心代码片段
均为标准的前台服务实现逻辑,无特殊自定义配置:
MainActivity
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val mainServiceIntent: Intent get() = Intent(application, MainService::class.java) ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.i("MainActivity", "onCreate") ... prepareServiceButton() } private fun prepareServiceButton() { binding.buttonService.setOnClickListener { if (isServiceRunning) stopMainService() else startMainService() } } private fun startMainService() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(mainServiceIntent) } else { startService(mainServiceIntent) } } ... }
MainService
class MainService : Service() { ... override fun onCreate() { super.onCreate() Log.i("MainService", "onCreate") ... } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.i("MainService", "onStartCommand") updateNotification() ... return START_STICKY } private fun updateNotification() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForeground(1, notification) } else { notificationManager.notify(1, notification) } } ... }
App
class App : Application() { override fun onCreate() { super.onCreate() Log.i("App", "onCreate") ... createNotificationChannel() } ... }
AndroidManifest
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.taskremovingtest"> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <application android:name=".App" ... > <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".MainService" android:enabled="true" android:exported="false" /> </application> </manifest>
1. 修正服务基础配置
- 修改
AndroidManifest.xml中Service的属性,添加android:stopWithTask="false",明确告知系统移除最近任务时不要停止当前服务:
<service android:name=".MainService" android:enabled="true" android:exported="false" android:stopWithTask="false" />
- 低版本安卓系统下统一调用
startForeground,不要仅使用notificationManager.notify,确保系统将服务识别为前台服务,降低被杀死的概率:
private fun updateNotification() { // 全版本都调用startForeground,避免低版本系统不识别前台状态 startForeground(1, notification) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { notificationManager.notify(1, notification) } }
2. 调整服务重启逻辑
华为早期EMUI系统对START_STICKY的调度存在缺陷,会触发多次重复重启,建议做如下修改:
- 将
onStartCommand的返回值改为START_REDELIVER_INTENT,该模式下服务被异常杀死后会携带原有Intent重建,不会出现多次空Intent触发重启的问题。 - 重写
onTaskRemoved方法,在用户移除最近任务时主动刷新前台通知,避免系统标记服务为可清理状态:
override fun onTaskRemoved(rootIntent: Intent?) { super.onTaskRemoved(rootIntent) updateNotification() }
3. 增加重复初始化拦截
针对已经出现的多次重启场景,添加全局标记拦截重复的蓝牙连接逻辑,避免蓝牙设备异常:
// 在App类中添加全局初始化标记 class App : Application() { companion object { var isBleInitialized = false } override fun onCreate() { super.onCreate() Log.i("App", "onCreate") createNotificationChannel() } } // 在Service中拦截重复初始化 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.i("MainService", "onStartCommand") updateNotification() if (!App.isBleInitialized) { // 仅首次启动时初始化蓝牙连接 initBleConnection() App.isBleInitialized = true } return START_REDELIVER_INTENT }
4. 极端场景适配
如果上述方案仍无法解决问题,可以将服务配置为独立进程运行,主进程被杀死不会影响服务进程,从根源避免服务随主进程反复重建:
<service android:name=".MainService" android:enabled="true" android:exported="false" android:stopWithTask="false" android:process=":bleService" />
独立进程模式下需要注意跨进程通信逻辑适配,蓝牙相关逻辑全部放在服务进程中实现即可。
内容的提问来源于stack exchange,提问作者sweak

