You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

华为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 01:54:03