Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{1946947 u0 ...MessageService}
main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x763e01d8 self=0x7d77814c00
| sysTid=11171 nice=-10 cgrp=default sched=0/0 handle=0x7dfe411560
| state=S schedstat=( 1337466614 103021380 2047 ) utm=106 stm=27 core=0 HZ=100
| stack=0x7fd522f000-0x7fd5231000 stackSize=8MB
| held mutexes=
#00 pc 00000000000712e0 /system/lib64/libc.so (__epoll_pwait+8)
#01 pc 00000000000141c0 /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
#02 pc 000000000001408c /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+60)
#03 pc 000000000012c0d4 /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
at android.os.MessageQueue.next (MessageQueue.java:326)
at android.os.Looper.loop (Looper.java:181)
at android.app.ActivityThread.main (ActivityThread.java:6981)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)
据我所知,Looper已经分析了这里的队列,发现了一个 "滥用者",并直接将其杀死。现在系统是快乐和健康的,而开发者和用户则不是,但由于谷歌限制了他们对系统的责任,他们为什么要关心后两者?显然,他们不知道。他们可以让它变得更好吗?当然,例如,他们可以提供 "应用程序正在忙 "的对话框,要求用户做出决定,是等待还是关闭应用程序,但为什么要这样做,这不是他们的责任。最重要的是,这个系统现在是健康的。
根据我的观察,这种情况发生得相对较少,在我的案例中,1000个用户一个月大约有一次崩溃。复制它是不可能的,而且即使它被复制了,你也无法永久地修复它。
在这个主题中,有一个很好的建议,即使用 "bind "而不是 "start",然后当服务准备好时,处理onServiceConnected,但同样,这意味着根本不使用startForegroundService调用。
我认为,谷歌方面正确和诚实的行动是告诉大家,startForegourndServcie有缺陷,不应该被使用。
问题仍然存在:用什么来代替?幸运的是,现在有JobScheduler和JobService,它们是前台服务的一个更好的选择。这是一个更好的选择,因为如此。
当一个作业正在运行时,系统会代表你的应用程序持有一个wakelock。
应用程序。由于这个原因,你不需要采取任何行动来保证
保证设备在作业期间保持清醒状态。
这意味着你不需要再关心处理wakelocks的问题,这就是为什么它与前台服务没有区别。从实现的角度来看,JobScheduler不是你的服务,它是一个系统的服务,想必它将正确处理队列,而且谷歌永远不会终止它自己的孩子:)
三星在他们的三星附件协议(SAP)中从startForegroundService切换到JobScheduler和JobService。当智能手表等设备需要与手机等主机对话时,这是非常有帮助的,其中作业确实需要通过应用程序的主线程与用户互动。由于作业是由调度员发布到主线程上的,所以它成为可能。但你应该记住,作业是在主线程上运行的,并将所有繁重的东西卸给其他线程和异步任务。
该服务在运行在你的应用程序主线程上的处理程序上执行每个传入的作业。
应用程序的主线程上运行的处理程序上执行每个传入的作业。这意味着,你必须将你的
执行逻辑到你选择的另一个线程/处理程序/AsyncTask上
转换到JobScheduler/JobService的唯一隐患是你需要重构旧的代码,这并不好玩。在过去的两天里,我一直在做这个工作,以使用新的三星的SAP实现。我会注意我的崩溃报告,如果再看到崩溃,就会告诉你。理论上它不应该发生,但总有一些细节是我们可能不知道的。
不再有Play Store报告的崩溃了。这意味着JobScheduler/JobService没有这样的问题,并且切换到这种模式是永远摆脱startForegroundService问题的正确方法。我希望,谷歌/安卓能读到它,并最终为大家提供评论/建议/提供官方指导。
对于那些使用SAP的人来说并询问SAP V2如何利用JobService的解释如下。
在你的自定义代码中,你需要初始化SAP(它是Kotlin)。
SAAgentV2.requestAgent(App.app?.applicationContext,
MessageJobs::class.java!!.getName(), mAgentCallback)
现在你需要反编译三星的代码,看看里面发生了什么。在SAAgentV2中,看一下requestAgent的实现和下面一行。
SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);
where d defined as below
private SAAdapter d;
现在转到SAAdapter类,找到onServiceConnectionRequested函数,该函数使用以下调用安排工作。
SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12);
SAJobService只是Android的JobService的一个实现,它是做工作调度的一个。
private static void a(Context var0, String var1, String var2, long var3, String var5, SAPeerAgent var6) {
ComponentName var7 = new ComponentName(var0, SAJobService.class);
Builder var10;
(var10 = new Builder(a++, var7)).setOverrideDeadline(3000L);
PersistableBundle var8;
(var8 = new PersistableBundle()).putString("action", var1);
var8.putString("agentImplclass", var2);
var8.putLong("transactionId", var3);
var8.putString("agentId", var5);
if (var6 == null) {
var8.putStringArray("peerAgent", (String[])null);
} else {
List var9;
String[] var11 = new String[(var9 = var6.d()).size()];
var11 = (String[])var9.toArray(var11);
var8.putStringArray("peerAgent", var11);
var10.setExtras(var8);
((JobScheduler)var0.getSystemService("jobscheduler")).schedule(var10.build());
正如你所看到的,这里的最后一行使用Android的JobScheduler来获得这个系统服务并安排一个工作。
在 requestAgent 调用中,我们传递了 mAgentCallback,这是一个回调函数,当一个重要事件发生时,它将接受控制。这就是我的应用程序中回调的定义方式。
private val mAgentCallback = object : SAAgentV2.RequestAgentCallback {
override fun onAgentAvailable(agent: SAAgentV2) {
mMessageService = agent as? MessageJobs
App.d(Accounts.TAG, "Agent " + agent)
override fun onError(errorCode: Int, message: String) {
App.d(Accounts.TAG, "Agent initialization error: $errorCode. ErrorMsg: $message")
MessageJobs是我实现的一个类,用于处理来自三星智能手表的所有请求。这不是完整的代码,只是一个骨架。
class MessageJobs (context:Context) : SAAgentV2(SERVICETAG, context, MessageSocket::class.java) {
public fun release () {
override fun onServiceConnectionResponse(p0: SAPeerAgent?, p1: SASocket?, p2: Int) {
super.onServiceConnectionResponse(p0, p1, p2)
App.d(TAG, "conn resp " + p1?.javaClass?.name + p2)
override fun onAuthenticationResponse(p0: SAPeerAgent?, p1: SAAuthenticationToken?, p2: Int) {
super.onAuthenticationResponse(p0, p1, p2)
App.d(TAG, "Auth " + p1.toString())
override protected fun onServiceConnectionRequested(agent: SAPeerAgent) {
override fun onFindPeerAgentsResponse(peerAgents: Array<SAPeerAgent>?, result: Int) {
override fun onError(peerAgent: SAPeerAgent?, errorMessage: String?, errorCode: Int) {
super.onError(peerAgent, errorMessage, errorCode)
override fun onPeerAgentsUpdated(peerAgents: Array<SAPeerAgent>?, result: Int) {
正如你所看到的,MessageJobs也需要MessageSocket类,你需要实现它来处理来自设备的所有消息。
一句话,这不是那么简单,它需要对内部和编码进行一些挖掘,但它可以工作,最重要的是--它不会崩溃。