前台服务正在被Android杀死
更新 : 我还没有find真正的解决scheme的问题。 我想到的是一种在丢失连接时自动重新连接到以前的蓝牙设备的方法。 这不是理想的,但似乎工作得很好。 我很想听到更多关于这方面的build议。
我和这个问题有很多相同的问题: 服务在保持唤醒locking的情况下被中止,并且在调用包括设备(Asus Transformer)在内的startForeground之后 ,停止服务之前的时间长度(30-45分钟),使用唤醒锁,使用startForeground(),以及在屏幕熄灭时应用程序处于打开状态时不会发生问题的事实。
我的应用程序与其他设备保持蓝牙连接,并在两者之间发送数据,所以它必须始终处于活动状态以侦听数据。 用户可以随意启动和停止服务,实际上这是我实现启动或停止服务的唯一方法。 一旦服务重新启动,与其他设备的蓝牙连接就会丢失。
根据链接问题的答案,startForeground()“减less了服务被杀死的可能性,但是并不妨碍它”。 我明白,是这样的,但是我已经看到了许多其他的应用程序没有这个问题的例子(例如塔斯克)。
我的应用程序的实用性将大大降低,无法运行服务,直到用户停止。 有什么办法可以避免这个?
我看到这在我的logcat每当服务停止:
ActivityManager: No longer want com.howettl.textab (pid 32321): hidden #16 WindowManager: WIN DEATH: Window{40e2d968 com.howettl.textab/com.howettl.textab.TexTab paused=false ActivityManager: Scheduling restart of crashed service com.howettl.textab/.TexTabService in 5000ms
编辑:我也应该注意到,这似乎并没有发生在我连接到的其他设备:HTC Legend running Cyanogen
编辑:这是adb shell dumpsys activity services
的输出:
* ServiceRecord{40f632e8 com.howettl.textab/.TexTabService} intent={cmp=com.howettl.textab/.TexTabService} packageName=com.howettl.textab processName=com.howettl.textab baseDir=/data/app/com.howettl.textab-1.apk resDir=/data/app/com.howettl.textab-1.apk dataDir=/data/data/com.howettl.textab app=ProcessRecord{40bb0098 2995:com.howettl.textab/10104} isForeground=true foregroundId=2 foregroundNoti=Notification(contentView=com.howettl.textab/0x1090087 vibrate=null,sound=null,defaults=0x0,flags=0x6a) createTime=-25m42s123ms lastActivity=-25m42s27ms executingStart=-25m42s27ms restartTime=-25m42s124ms startRequested=true stopIfKilled=false callStart=true lastStartId=1 Bindings: * IntentBindRecord{40a02618}: intent={cmp=com.howettl.textab/.TexTabService} binder=android.os.BinderProxy@40a9ff70 requested=true received=true hasBound=true doRebind=false * Client AppBindRecord{40a3b780 ProcessRecord{40bb0098 2995:com.howettl.textab/10104}} Per-process Connections: ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8} All Connections: ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}
并且adb shell dumpsys activity
输出结果adb shell dumpsys activity
:
* TaskRecord{40f5c050 #23 A com.howettl.textab} numActivities=1 rootWasReset=false affinity=com.howettl.textab intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab} realActivity=com.howettl.textab/.TexTab lastActiveTime=4877757 (inactive for 702s) * Hist #1: ActivityRecord{40a776c8 com.howettl.textab/.TexTab} packageName=com.howettl.textab processName=com.howettl.textab launchedFromUid=2000 app=ProcessRecord{40bb0098 2995:com.howettl.textab/10104} Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab } frontOfTask=true task=TaskRecord{40f5c050 #23 A com.howettl.textab} taskAffinity=com.howettl.textab realActivity=com.howettl.textab/.TexTab base=/data/app/com.howettl.textab-1.apk/data/app/com.howettl.textab-1.apk data=/data/data/com.howettl.textab labelRes=0x7f060000 icon=0x7f020000 theme=0x0 stateNotNeeded=false componentSpecified=true isHomeActivity=false configuration={ scale=1.0 imsi=0/0 loc=en_CA touch=3 keys=2/1/1 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=6} launchFailed=false haveState=true icicle=Bundle[mParcelledData.dataSize=1644] state=STOPPED stopped=true delayedResume=false finishing=false keysPaused=false inHistory=true visible=false sleeping=true idle=true fullscreen=true noDisplay=false immersive=false launchMode=2 frozenBeforeDestroy=false thumbnailNeeded=false connections=[ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}]
…
Proc #15: adj=prcp /F 40e75070 959:android.process.acore/10006 (provider) com.android.providers.contacts/.ContactsProvider2<=Proc{40bb0098 2995:com.howettl.textab/10104} Proc #16: adj=bak+2/F 40bb0098 2995:com.howettl.textab/10104 (foreground-service)
这些似乎表明服务在前台运行。
对。 我已经经历了这个问题。 以下是如何继续。 有错误。 本文将介绍如何分析实施中的错误并解决问题。
总结一下,这里是应该如何工作的。 运行服务将每隔30分钟进行一次清理和终止。 希望保持活动时间超过此时间的服务必须调用Service.startForeground,它会在通知栏上发出通知,以便用户知道您的服务是永久运行的并可能吸收电池寿命。 在任何时候,只有三个服务进程可以提名自己为前台服务。 如果有三个以上的前台服务,Android将提名最早的服务作为清理和终止的候选人。
不幸的是,在优先化前台服务方面,Android存在一些错误,这些错误是由各种服务绑定标志组合触发的。 即使您已正确指定您的服务为前台服务,但无论如何,如果您的进程中的任何服务连接都使用了某些绑定标志的组合,那么Android可能会终止您的服务。 详情如下。
请注意,很less有服务需要作为前台服务。 一般来说,如果您持续有效或长时间运行某种可打开和closures的互联网连接,或者用户取消,您只需要成为前台服务。 需要前台状态的服务示例:UPNP服务器,长时间运行的超大文件下载,通过wi-fi同步文件系统以及播放音乐。
如果您只是偶尔进行轮询,或者等待系统广播接收器或系统事件,那么最好是通过定时器唤醒您的服务,或者响应广播接收器,然后让服务完成。 这是服务的devise行为。 如果你只是要保持活力,然后阅读。
在已知的需求(例如调用Service.startForeground)上检查框后,下一个要查找的地方是在Context.bindService调用中使用的标志。 用于绑定的标志以各种意想不到的方式影响目标服务进程的优先级。 最特别的是,使用某些绑定标志可能会导致Android错误地将您的前台服务降级为常规服务。 用于分配进程优先级的代码已经非常严重。 值得注意的是,在使用旧的绑定标志时,API 14+中的修订可能会导致错误; 4.2.1中有明确的错误。
你所有的朋友都是sysdump实用程序,它可以用来确定活动pipe理器为你的服务进程分配了什么优先级,并且发现它指定了不正确优先级的情况。 启动并运行您的服务,然后从主机上的命令提示符处发出以下命令:
adb shell dumpsys活动进程> tmp.txt
使用记事本(不是写字板/写)来检查内容。
首先validation您是否成功地设法在前台状态下运行您的服务。 dumpsys文件的第一部分包含每个进程的ActivityManager属性的描述。 在dumpsys文件的第一部分寻找一条如下所示的对应于您的应用程序的行:
APP UID 10068 ProcessRecord {41937d40 2205:tunein.service / u0a10068}
validation在以下部分foregroundServices = true。 不要担心隐藏和空的设置; 他们描述了stream程中的活动状态,似乎并不特别与其中的服务stream程相关。 如果foregroundService不是true,则需要调用Service.startForeground以使其为true。
接下来需要看的是靠近文件末尾的部分,标题为“进程LRU列表(按oom_adjsorting):”。 通过此列表中的条目,您可以确定Android是否已将您的应用程序实际分类为前台服务。 如果你的过程是在这个清单的底部,那么它是总结性灭绝的主要候选者。 如果你的程序靠近列表的顶部,它几乎是坚不可摧的。
让我们看看这个表中的一行:
Proc #31: adj=prcp /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)
这是一个正确的前景服务的例子。 这里的关键字段是“adj =”字段。 这表示在完成所有事情之后,您的stream程由ActivityManagerService分配的优先级。 你希望它是“adj = prcp”(可见的前景服务); 或“adj = vis”(具有活动的可见过程)或“前”(具有前景活动的过程)。 如果它是“adj = svc”(服务进程),或者“adj = svcb”(传统服务?)或“adj = bak”(空后台进程),那么你的进程是终止的可能候选者,即使没有任何回收记忆的压力,也不会less于每30分钟一次。 线上的其余标志主要是Google工程师的诊断debugging信息。 终止决定是根据调整字段进行的。 简而言之,/ FS表示一个前台服务; / FA表示一个活动的前台进程。 / B表示后台服务。 最后的标签显示了过程被赋予优先权的一般规则。 通常它应该匹配adj =字段; 但是由于与其他服务或活动的活动绑定上的绑定标志,在某些情况下可以向上或向下调整adj =值。
如果你已经绑定了一个带有绑定标志的错误,那么dumpsys行就像这样:
Proc #31: adj=bak /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)
注意adj字段的值如何被错误地设置为“adj = bak”(空的后台进程),大致转换为“请现在终止我,以便我可以结束这个毫无意义的存在”,用于进程清理。 还要注意行尾的(fg-service)标志,它表示“地面服务规则被用来确定”adj“设置。尽pipe使用了fg服务规则,但是这个过程被分配了一个adj设置“bak”,它不会活很长时间,说白了,这是个bug。
所以我们的目标是确保你的过程总是得到“adj = prcp”(或者更好)。 实现这一目标的方法是调整绑定标志,直到您设法避免优先级分配中的错误。
这是我知道的错误。 (1)如果任何服务或活动使用Context.BIND_ABOVE_CLIENT绑定到服务,那么即使该绑定不再活动,您也有可能将adj =设置降级为“bak”。 如果您也有服务之间的绑定,则尤其如此。 4.2.1源代码中的一个明确的错误。 (2)绝对不要使用BIND_ABOVE_CLIENT来实现服务到服务的绑定。 不要将它用于活动到服务的连接。 用于实现BIND_ABOVE_CLIENT行为的标志似乎是以每个进程为基础设置的,而不是每个连接的基础,因此即使没有活动的服务对象,也会触发服务到服务绑定的错误绑定与国旗集。 在进程中存在多个服务的情况下,使用服务到服务的绑定似乎也存在确定优先级的问题。 在服务到服务绑定上使用Context.BIND_WAIVE_PRIORITY(API 14)似乎有帮助。 从活动绑定到服务时,Context.BIND_IMPORTANT似乎是一个或多或less的好主意。 当活动处于前台时,这样做会使stream程的优先级提高一个档次,而在活动暂停或完成时不会造成任何明显的伤害。
但总的来说,策略是调整你的bindService标志,直到sysdump指出你的进程已经收到正确的优先级。
为我的目的,使用Context.BIND_AUTO_CREATE | 活动到服务绑定的Context.BIND_IMPORTANT和Context.BIND_AUTO_CREATE | 服务到服务绑定的Context.BIND_WAIVE_PRIORITY似乎是正确的。 你的里程可能不同。
我的应用程序相当复杂:两个后台服务,每个后台服务可以独立保持前台服务状态,再加上三个后台服务也可以处于前台服务状态; 两个服务有条件地相互绑定; 总是第三个绑定到第一个。 另外,Activites在一个单独的过程中运行(使animation更平滑)。 在相同的过程中运行活动和服务似乎没有任何区别。
清除进程的规则(和用于生成sysdump文件内容的源代码)的执行可以在核心的android文件中find
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java.
好机会。
PS:这是Android 5.0的sysdumpstring的解释。 我没有和他们一起工作,所以他们会做什么。 我相信你希望4是'A'或'S',5是'IF'或'IB',1要尽可能低(可能低于3,因为只有3个前台服务进程保持活动在默认configuration)。
Example: Proc # : prcp F/S/IF trm: 0 31719: neirotech.cerebrum.attention:blePrcs/u0a77 (fg-service) Format: Proc # {1}: {2} {3}/{4}/{5} trm: {6} {7}: {8}/{9} ({10} 1: Order in list: lower is less likely to get trimmed. 2: Not sure. 3: B: Process.THREAD_GROUP_BG_NONINTERACTIVE F: Process.THREAD_GROUP_DEFAULT 4: A: Foreground Activity S: Foreground Service ' ': Other. 5: -1: procState = "N "; ActivityManager.PROCESS_STATE_PERSISTENT: procState = "P "; ActivityManager.PROCESS_STATE_PERSISTENT_UI:procState = "PU"; ActivityManager.PROCESS_STATE_TOP: procState = "T "; ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: procState = "IF"; ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: procState = "IB"; ActivityManager.PROCESS_STATE_BACKUP:procState = "BU"; ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: procState = "HW"; ActivityManager.PROCESS_STATE_SERVICE: procState = "S "; ActivityManager.PROCESS_STATE_RECEIVER: procState = "R "; ActivityManager.PROCESS_STATE_HOME: procState = "HO"; ActivityManager.PROCESS_STATE_LAST_ACTIVITY: procState = "LA"; ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: procState = "CA"; ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: procState = "Ca"; ActivityManager.PROCESS_STATE_CACHED_EMPTY: procState = "CE"; {6}: trimMemoryLevel {8} Process ID. {9} process name {10} appUid
如果它说“不再需要…”,那么该进程没有活动的服务,当前处于startForeground()状态。 检查一下,确保你的调用实际上是成功的 – 你看到通知发布,日志中没有任何消息抱怨什么,等等。同样使用“adb shell dumpsys活动服务”来看看您的服务状态,并确保它实际上标记为前景。 另外,如果前景正确,那么在“adb shell dumpsys activity”的输出中,您将在显示进程的OOM adj的部分中看到,由于该服务,您的进程当前处于前台级别。