android - 前台服务被奥利奥杀死

标签 android android-service android-source

我已经编写了一个前台服务,它适用于所有低于 Oreo 的操作系统版本。来自 Oreo 的应用程序进程在关闭并从最近的应用程序中删除应用程序 5 分钟后被终止。

根据 background execution limitations 的安卓开发者文档操作系统不应终止正在运行前台服务且在通知窗口中显示通知的应用程序。

根据开发者文档指南。我按照以下步骤启动前台服务。

  1. 前台服务使用startForegroundService()方法启动
  2. 在启动服务后的 5 秒内,使用 startForeground()
  3. 显示服务的通知
  4. serviceonStartCommand() 返回 START_STICKY

我在以下手机上遇到这个问题:

  1. 一加 5T
  2. 体内
  3. 欧泊

我试图防止前台服务被破坏的方法是什么?

  1. 禁用 battery optimization通过显示应用程序 向用户发出系统对话框以禁用打瞌睡模式。

我尝试重新启动前台服务的原因是什么?

  1. 使用 AlarmManager 从 onTaskRemoved() 重新启动服务。 请查看this link了解详情。

据我了解,这些制造商已经自定义了 AOSP,并且不遵守允许前台服务运行的操作系统指南。可能这些制造商这样做是因为为用户提供了较长的电池续航时间。

前台服务类

    class DataCaptureService : Service() {

        private var isServiceStarted = false

        override fun onBind(intent: Intent?): IBinder? {
            return null
        }

override fun onCreate() {
        super.onCreate()
        wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
                    newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WakelockTag123").apply {
                        acquire()
                    }
                }
    }


        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            val serviceAction = intent?.action
            LogUtils.logD("OnStartCommand(). Action=$serviceAction")
            if (Constants.INTENT.ACTION_STOP_SERVICE == serviceAction) {
                LogUtils.logD("Stopping data capture service")
                stopForeground(true)
                stopSelf()
            } else if (Constants.INTENT.ACTION_START_SERVICE == serviceAction && !isServiceStarted) {
                LogUtils.logD("Starting data capture service")
                isServiceStarted = true
                // Here showing notification using a utility method (startForeground(id, notification))
                createNotification(this)
                // Doing some stuff here
                ----------------------
                //
            }
            return START_STICKY
        }

        override fun onDestroy() {
            super.onDestroy()
            if (isServiceStarted) {
                LogUtils.logD("onDestroy of DataCaptureService method is invoked")
                // Doing some stuff here
                ----------------------
                //
                isServiceStarted = false
                if (wakeLock.isHeld) {
                    wakeLock.release()
                }
            }
        }

        override fun onTaskRemoved(rootIntent: Intent?) {
            LogUtils.logD("onTaskRemoved of DataCaptureService method is invoked")
            ensureServiceStaysRunning()
            super.onTaskRemoved(rootIntent)
        }

        private fun ensureServiceStaysRunning() {
            val restartAlarmInterval = 60 * 1000
            val resetAlarmTimer = 30 * 1000L
            // From this broadcast I am restarting the service
            val restartIntent = Intent(this, ServiceRestartBroadcast::class.java)
            restartIntent.action = "RestartedViaAlarm"
            restartIntent.flags = Intent.FLAG_RECEIVER_FOREGROUND
            val alarmMgr = getSystemService(Context.ALARM_SERVICE) as AlarmManager
            val restartServiceHandler = @SuppressLint("HandlerLeak")
            object : Handler() {
                override fun handleMessage(msg: Message) {
                    val pendingIntent = PendingIntent.getBroadcast(applicationContext, 87, restartIntent, PendingIntent.FLAG_CANCEL_CURRENT)
                    val timer = System.currentTimeMillis() + restartAlarmInterval
                    val sdkInt = Build.VERSION.SDK_INT
                    if (sdkInt < Build.VERSION_CODES.KITKAT)
                        alarmMgr.set(AlarmManager.RTC_WAKEUP, timer, pendingIntent)
                    else if (Build.VERSION_CODES.KITKAT <= sdkInt && sdkInt < Build.VERSION_CODES.M)
                        alarmMgr.setExact(AlarmManager.RTC_WAKEUP, timer, pendingIntent)
                    else if (sdkInt >= Build.VERSION_CODES.M) {
                        alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timer, pendingIntent)
                    }
                    sendEmptyMessageDelayed(0, resetAlarmTimer)
                    stopSelf()
                }
            }
            restartServiceHandler.sendEmptyMessageDelayed(0, 0)
        }

    }

如果您遇到类似类型的问题并设法找到解决方案,请分享您的建议。

最佳答案

我在OnePlus上分析过这个问题,我和你的情况一样。正如我目前所见,没有解决方案。 OnePlus 显然遵循了一种不好的做法。

由于他们没有发布以这种方式杀死进程的源代码,我下载了一个 OnePlus ROM(我选择 OnePlus 5T 5.1.5),解压它,找到执行此操作的 .class(服务中的 OnePlusHighPowerDetector.class .vdex),反编译它,并试图找出发生了什么。

你可以在这里找到这个类的一个版本(不是我做的,可能和我用的不是同一个版本):https://github.com/joshuous/oneplus_blobs_decompiled/blob/master/com/android/server/am/OnePlusHighPowerDetector.java

不幸的是,最重要的功能没有成功反编译。但是无论如何我们都可以分析字节码。这是我发现的:

  • OnePlus 毫不留情地杀死了后台进程(似乎它是否有前台服务并不重要)。几乎所有的人。使用多少 CPU 资源并不重要。我的应用使用不到 1%,就被杀死了。
  • 如果该应用固定在最近的应用列表中(由 com_oneplus_systemui_recent_task_lockd_list 列表确定),它不会被杀死。因此,用户可以通过固定应用来保存应用。但这对他们来说很不方便。
  • Oneplus 提供了一个他们不会终止的进程列表。此列表位于 oneplus-framework-res.apk/res/values/array.xml 中,键为 string-array name="backgroundprocess_detection_app_whitelist"。此列表主要包含 map 和健身应用程序1
  • 也许还有其他因素可以避免进程被杀死,我没有进一步分析这个问题。如果有时间,请查看 OnePlusHighPowerDetector.java。

以下是反编译 .vdex 文件所需的步骤:

  • 使用Vdex Extractor从 .vdex 创建 .dex(也许您需要使用 --ignore-crc-error 选项)
  • 使用JADX反编译 .dex(我认为这是目前最好的反编译器),或使用 dex2jar从 .dex 创建 .jar,并使用任何反编译器反编译 .jar

1Rant:这表明当前情况有多么糟糕。 OnePlus,这真的是解决方案吗?您选择了 10-15 个可以在后台运行的应用程序,而您不关心所有其他应用程序?如何创建一个新的健身/ map /音乐播放器应用程序,在您的设备上安全运行?

关于android - 前台服务被奥利奥杀死,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52425222/

相关文章:

java - 从另一个 java 类重写该类中的方法(附加到其他方法)

Android 深层链接到服务?

android - 使用服务在 Activity 之间共享/保存数据

Android:在服务内的 TimerTask 中实例化处理程序

android - Nexus 7 刷完 AOSP build 后卡在 google 的 logo 上

android - 更改android打开文件的方式以触发系统服务功能

android - 从 App 到 Android 中的 SystemService 的通信

android - jquery focus() 在移动设备(android)中不起作用,phonegap-cordova

android - 如何拖放多个 Activity ?

Android ViewPager 上一页和下一页可见吗?