이미 너무 많은 답변이 게시되었지만 사실은 startForegroundService를 앱 수준에서 수정할 수 없으므로 사용을 중지해야한다는 사실입니다. Context # startForegroundService ()가 호출 된 후 5 초 이내에 Service # startForeground () API를 사용하도록 Google에서 권장하는 것은 앱이 항상 할 수있는 것이 아닙니다.
Android는 많은 프로세스를 동시에 실행하며 Looper가 5 초 이내에 startForeground ()를 호출해야하는 대상 서비스를 호출한다는 보장은 없습니다. 대상 서비스가 5 초 내에 전화를받지 못하면 운이 나빠 사용자에게 ANR 상황이 발생합니다. 스택 추적에 다음과 같은 내용이 표시됩니다.
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는 여기에서 대기열을 분석하고 "abuser"를 발견하여 간단히 죽였습니다. 시스템은 현재 행복하고 건강하지만 개발자와 사용자는 그렇지 않지만 Google은 시스템에 대한 책임을 제한하기 때문에 왜 후자에 관심을 가져야합니까? 분명히 그들은하지 않습니다. 그들이 더 나아질 수 있을까? 물론, 예를 들어 "응용 프로그램이 사용 중입니다"대화 상자를 통해 사용자에게 앱 대기 또는 종료에 대한 결정을 요청했지만 왜 귀찮게하지 않습니까? 가장 중요한 것은 이제 시스템이 건강하다는 것입니다.
내 관찰에 따르면, 이것은 비교적 드물게 발생하며, 1K 사용자의 경우 한 달에 약 1 건의 충돌이 발생합니다. 재현 할 수 없으며 재현하더라도 영구적으로 고칠 수있는 방법이 없습니다.
이 스레드에서 "start"대신 "bind"를 사용하도록 제안한 다음 서비스가 준비되면 onServiceConnected를 처리하지만 다시 startForegroundService 호출을 전혀 사용하지 않음을 의미합니다.
Google 측의 옳고 정직한 행동은 모든 사람들에게 startForegourndServcie에 결함이있어서 사용해서는 안된다고 말하는 것입니다.
문제는 여전히 남아 있습니다 : 대신 무엇을 사용해야합니까? 다행히도 현재 전경 서비스를위한 더 나은 대안 인 JobScheduler와 JobService가 있습니다. 그 때문에 더 나은 옵션입니다.
작업이 실행되는 동안 시스템은 앱 대신 깨우기 잠금을 유지합니다. 따라서 작업 기간 동안 장치가 깨어있는 상태를 유지하기 위해 어떤 조치도 취할 필요가 없습니다.
즉, 더 이상 웨이크 록 처리에 신경 쓸 필요가 없으므로 포 그라운드 서비스와 다르지 않습니다. 구현 관점에서 볼 때 JobScheduler는 귀하의 서비스가 아니며 시스템의 시스템 일 것입니다. 아마 대기열을 올바르게 처리하고 Google은 자체 자식을 종료하지 않습니다. :)
Samsung은 Samsung Accessory Protocol (SAP)에서 startForegroundService에서 JobScheduler 및 JobService로 전환했습니다. 스마트 워치와 같은 장치가 전화와 같은 호스트와 대화해야 할 때 매우 유용합니다. 여기서 작업은 앱의 주요 스레드를 통해 사용자와 상호 작용해야합니다. 스케줄러가 작업을 기본 스레드에 게시하므로 가능합니다. 작업이 기본 스레드에서 실행 중이고 모든 무거운 물건을 다른 스레드 및 비동기 작업으로 오프로드한다는 것을 기억해야합니다.
이 서비스는 애플리케이션의 메인 스레드에서 실행되는 핸들러에서 각 수신 작업을 실행합니다. 즉, 실행 로직을 선택한 다른 스레드 / 핸들러 / AsyncTask로 오프로드해야합니다.
JobScheduler / JobService로 전환하는 유일한 함정은 이전 코드를 리팩터링해야한다는 것입니다. 지난 이틀 동안 새로운 삼성의 SAP 구현을 사용하기 위해 노력했습니다. 충돌 보고서를보고 충돌이 다시 발생하는지 알려 드리겠습니다. 이론적으로는 그렇게해서는 안되지만, 우리가 알지 못하는 세부 사항이 항상 있습니다.
업데이트
Play 스토어에서 더 이상 충돌이보고되지 않았습니다. 이는 JobScheduler / JobService에 그러한 문제가 없으며이 모델로 전환하는 것이 startForegroundService 문제를 한 번 그리고 영원히 없애는 올바른 접근법입니다. Google / Android가이 내용을 읽고 결국에는 모든 사람을위한 공식 지침에 대해 언급 / 권고 / 제공하기를 바랍니다.
업데이트 2
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 클래스가 필요하며 장치에서 오는 모든 메시지를 구현하고 처리해야합니다.
결론적으로, 그것은 간단하지 않으며 내부 및 코딩에 대한 파기를 필요로하지만 작동하며 가장 중요하게 충돌하지 않습니다.