高端手机杀死了最少的Android前台服务 [英] Minimal android foreground service killed on high-end phone

查看:523
本文介绍了高端手机杀死了最少的Android前台服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个允许用户记录路线(位置/GPS)的应用.为了确保即使在屏幕关闭时也记录位置,我为位置记录创建了foreground service.我将位置存储在Room Database中,该位置已使用Dagger2注入到我的服务中.

I'm trying to create an app that lets users log routes (locations/GPS). To ensure locations are logged even when the screen is off, I have created a foreground service for the location logging. I store the locations in a Room Database which is injected into my service using Dagger2.

但是,此服务被Android杀死了,这当然不好.我可以订阅内存不足警告,但这不能解决潜在的问题,即在运行Android 8.0的现代高端手机上大约30分钟后我的服务被杀死

However, this service is killed by Android which is, of course, not good. I could subscribe to low memory warnings but that doesn't solve the underlying problem of my service getting killed after ~30 minutes on a modern high-end phone running Android 8.0

我创建了一个仅包含"Hello world"活动和服务的最小项目: https://github. com/RandomStuffAndCode/AndroidForegroundService

I have created a minimal project with only a "Hello world" activity and the service: https://github.com/RandomStuffAndCode/AndroidForegroundService

该服务在我的Application类中启动,并且路由记录通过Binder启动:

The service is started in my Application class, and route logging is started through a Binder:

// Application
@Override
public void onCreate() {
    super.onCreate();
    mComponent = DaggerAppComponent.builder()
            .appModule(new AppModule(this))
            .build();

    Intent startBackgroundIntent = new Intent();
    startBackgroundIntent.setClass(this, LocationService.class);
    startService(startBackgroundIntent);
}

// Binding activity
bindService(new Intent(this, LocationService.class), mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
// mConnection starts the route logging through `Binder` once connected. The binder calls startForeground()

我可能不需要BIND_AUTO_CREATE标志,我一直在测试不同的标志,以免使我的服务被杀死-到目前为止还算不上运气.

I probably don't need the BIND_AUTO_CREATE flag, I've been testing different flags in an attempt to not get my service killed - no luck so far.

使用事件探查器似乎没有任何内存泄漏,内存使用量稳定在〜35mb:

Using the profiler it does not seem like I have any memory leaks, memory usage is stable at ~35mb:

使用adb shell dumpsys activity processes > tmp.txt,我可以确认foregroundServices=true和我的服务在LRU列表中列在第八位:

Using adb shell dumpsys activity processes > tmp.txt i can confirm that foregroundServices=true and my service is listed 8th in the LRU list:

Proc # 3: prcp F/S/FGS trm: 0 31592:com.example.foregroundserviceexample/u0a93 (fg-service)

似乎无法创建可以信任的前台服务,以免被杀死.所以,我们能做些什么?好吧...

It seems like it is not possible to create a foreground service that you can trust to not get killed. So what can we do? Well...

  1. 将服务放在一个单独的过程中,以尝试让Android在不理会该服务的情况下终止UI/活动.可能会有所帮助,但似乎不能保证
  2. 在服务中
  3. 坚持一切房间数据库.每个变量,每个自定义类,每次进行任何更改,然后使用START_STICKY启动服务.这似乎是浪费的,不会导致代码非常漂亮,但是可能会起作用.取决于Android在终止服务后重新创建服务所需的时间,可能会丢失大部分位置.
  1. Put the service in a separate process, in an attempt to let Android kill the UI/Activities while leaving the service alone. Would probably help, but doesn't seem like a guarantee
  2. Persist everything in the service in e.g. a Room database. Every variable, every custom class, every time any of the changes and then start the service with START_STICKY. This seems kind of wasteful and doesn't lead to very beautiful code, but it would probably work... somewhat. Depending on how long it takes for Android to re-create the service after killing it, a large portion of locations may be lost.

这真的是Android后台在做事的当前状态吗?有没有更好的方法?

Is this really the current state of doing stuff in the background on Android? Isn't there a better way?

将应用列入白名单以优化电池(禁用它)并不能阻止我的服务被终止

Whitelisting the app for battery optimization (disabling it) does not stop my service from being killed

使用Context.startForegroundService()启动服务并不能改善这种情况

Using Context.startForegroundService() to start the service does not improve the situation

因此,这确实仅发生在某些设备上,但始终在它们上发生.我猜您必须选择不支持大量用户或编写非常丑陋的代码.很棒.

So this indeed only occurs on some devices, but it occurs consistently on them. I guess you have to make a choice of either not supporting a huge number of users or write really ugly code. Awesome.

推荐答案

startForeground启动的服务与第二重要的组可见流程:

A service started by startForeground besongs to the second most important group visible process:

  1. 一个可见的进程正在执行用户当前了解的工作, 因此将其杀死会对用户​​产生明显的负面影响 经验.以下过程被认为是可见的 条件:

  1. A visible process is doing work that the user is currently aware of, so killing it would have a noticeable negative impact on the user experience. A process is considered visible in the following conditions:

  1. 它正在运行一个Activity,该Activity在屏幕上对用户可见,但在前台却不可见(已调用其onPause()方法).这 例如,如果前台的活动"显示为 对话框,可以在其后看到以前的活动.

  1. It is running an Activity that is visible to the user on-screen but not in the foreground (its onPause() method has been called). This may occur, for example, if the foreground Activity is displayed as a dialog that allows the previous Activity to be seen behind it.

它具有一个通过Service.startForeground()作为前台服务运行的服务(要求系统对待 服务是用户了解或本质上可见的东西 他们).

It has a Service that is running as a foreground service, through Service.startForeground() (which is asking the system to treat the service as something the user is aware of, or essentially visible to them).

系统中运行的这些进程的数量较少 比前台进程要好,但仍然相对受控. 这些 流程被认为是非常重要的,不会被杀死 除非需要这样做才能使所有前台进程保持运行.

The number of these processes running in the system is less bounded than foreground processes, but still relatively controlled. These processes are considered extremely important and will not be killed unless doing so is required to keep all foreground processes running.

话虽如此,您永远无法确定自己的服务不会在任何时候被终止.例如.内存压力,电池电量低等.请参见谁活着,谁死了.

That being said, you can never be sure that your service is not killed at any time. E.g. memory pressure, low battery etc. See who-lives-and-who-dies.

关于如何处理它,基本上,您自己回答了这个问题.要走的路是 START_STICKY :

For how to handle it, basically you answered the question yourself. The way to go is START_STICKY:

对于已启动的服务,还有两种其他的主要模式 他们可以决定要运行的操作,具体取决于他们的价值 onStartCommand(): START_STICKY的返回用于服务 根据需要显式启动和停止,而 START_NOT_STICKYSTART_REDELIVER_INTENT用于仅应使用的服务 在处理发送给他们的任何命令时保持运行状态.见 链接的文档以获取有关语义的更多详细信息.

For started services, there are two additional major modes of operation they can decide to run in, depending on the value they return from onStartCommand(): START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them. See the linked documentation for more detail on the semantics.

作为一般准则,您应该在后台(前台)服务中尽可能少地进行操作,即仅进行位置跟踪,并将其他所有内容保留在前台活动中.仅跟踪应该只需要很少的配置,就可以快速加载.同样,您的服务越小,被杀死的可能性就越小.只要系统没有被杀死,您的活动就会由系统恢复到其进入后台之前的状态.另一方面,前台活动的冷启动"应该不是问题.
我认为这并不难看,因为这可以确保手机始终为用户提供最佳体验.这是它要做的最重要的事情.不幸的是,某些设备在30分钟后关闭了服务(可能没有用户交互).

As a general guideline you should do as little as possible in the background (ore foreground) service, i.e. only do the location tracking and keep everything else in your foreground activity. Only the tracking should require very little configuration an can be loaded quickly. Also the smaller your service is the less likely it is to be killed. Your activity will be restored by the system in the state that is was before it went into background, as long as it is not killed as well. A "cold-start" of the foreground activity on the other hand should not be a problem.
I don't consider that as ugly, because this guarantees that the phone always provides the best experience to the user. This is the most important thing it has to do. That some devices close services after 30 minutes (possibly without user interaction) is unfortunate.

所以,正如您所说,您必须

So, as you stated, you have to

例如,将服务中的所有内容持久化房间数据库.每一个 变量,每个自定义类,每次其中任何一个更改,然后 使用START_STICKY启动服务.

Persist everything in the service in e.g. a Room database. Every variable, every custom class, every time any of them changes and then start the service with START_STICKY.

请参见创建永无止境的服务

隐性问题:

具体取决于Android重新创建 服务杀死后,可能会丢失很大一部分位置.

Depending on how long it takes for Android to re-create the service after killing it, a large portion of locations may be lost.

这通常只需要很短的时间.尤其是因为您可以使用融合位置提供商Api 来进行位置更新,这是一项独立的系统服务,不太可能被杀死.因此,这主要取决于您需要在onStartCommand中重新创建服务的时间.

This usually takes only a really short time. Especially because you can use the Fused Location Provider Api for the location updates, which is an independent system service and very unlikely to be killed. So it mainly depends on the time you need to recreate the service in onStartCommand.

还要注意,从Android 8.0起,您需要使用 forground service因为背景位置 限制.

Also take note that from Android 8.0 onwards you need to use a forground service because of the background location limits.


正如最近在新闻中报道的那样: 一些制造商可能会给您带来困难,以保持您的服务正常运行.该网站 https://dontkillmyapp.com/跟踪制造商和设备的缓解措施. 一加当前(19.01.19)是最严重的违规者之一.


As recently covered in the news: Some manufacturers may give you a hard time to keep your service running. The site https://dontkillmyapp.com/ keeps track of the manufacturers and possible mitigations for your device. Oneplus is currently (29.01.19) one of the worst offenders.

在发布他们的1 + 5和1 + 6手机时,OnePlus推出了其中一款 迄今为止市场上最严格的背景限制,甚至相形见war 小米或华为表演的那些.用户不仅需要启用 使其应用程序正常运行的其他设置,但这些设置 甚至可以通过固件更新来重置,从而使应用再次中断,并且用户 需要定期重新启用这些设置.

When releasing their 1+5 and 1+6 phones, OnePlus introduced one of the most severe background limits on the market to date, dwarfing even those performed by Xiaomi or Huawei. Not only did users need to enable extra settings to make their apps work properly, but those settings even get reset with firmware update so that apps break again and users are required to re-enable those settings on a regular basis.

用户解决方案

关闭系统设置>应用>齿轮图标>特殊访问>电池 优化.

Turn off System Settings > Apps > Gear Icon > Special Access > Battery Optimization.

不幸的是

开发人员端没有已知的解决方案

No known solution on the developer end

这篇关于高端手机杀死了最少的Android前台服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆