Biliyorum, zaten çok fazla cevap yayınlandı, ancak gerçek şu ki - startForegroundService bir uygulama düzeyinde sabitlenemez ve kullanmayı bırakmalısınız. Google'ın Context # startForegroundService () çağrıldıktan sonra 5 saniye içinde Service # startForeground () API'sini kullanması önerisi, bir uygulamanın her zaman yapabileceği bir şey değildir.
Android aynı anda birçok işlem gerçekleştirir ve Looper'ın 5 saniye içinde startForeground () öğesini çağırması gereken hedef hizmetinizi arayacağına dair bir garanti yoktur. Hedef hizmetiniz aramayı 5 saniye içinde almazsa, şansınız kalmaz ve kullanıcılarınız ANR durumu ile karşılaşır. Yığın izinizde böyle bir şey görürsünüz:
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)
Anladığım kadarıyla, Looper buradaki kuyruğu analiz etti, bir "istismarcı" buldu ve sadece öldürdü. Sistem şimdi mutlu ve sağlıklıyken, geliştiriciler ve kullanıcılar memnun değiller, ancak Google sisteme karşı sorumluluklarını sınırladığından, son ikisini neden önemsemeliler? Görünüşe göre öyle değiller. Daha iyi yapabilirler mi? Tabii ki, örneğin "Uygulama meşgul" iletişim kutusundan hizmet verebilirler, bir kullanıcıdan uygulamayı beklemek veya öldürmek hakkında bir karar vermesini isterler, ancak neden rahatsız olurlar, bu onların sorumluluğu değildir. Ana şey, sistemin şimdi sağlıklı olmasıdır.
Gözlemlerimden, bu nispeten nadiren olur, benim durumumda 1K kullanıcıları için ayda yaklaşık 1 kaza. Yeniden üretmek imkansızdır ve yeniden üretilse bile, kalıcı olarak düzeltmek için yapabileceğiniz hiçbir şey yoktur.
Bu iş parçacığında "start" yerine "bind" kullanmak için iyi bir öneri vardı ve daha sonra hizmet hazır olduğunda onServiceConnected işlemini gerçekleştirin, ancak yine startForegroundService çağrılarının kullanılmaması anlamına gelir.
Bence, Google tarafından yapılan doğru ve dürüst eylem, startForegourndServcie'nin bir eksikliği olduğunu ve kullanılmaması gerektiğini herkese söylemek olacaktır.
Soru hala devam ediyor: bunun yerine ne kullanılır? Neyse ki bizim için, ön plan hizmetleri için daha iyi bir alternatif olan JobScheduler ve JobService var. Bu daha iyi bir seçenek, çünkü:
Bir iş çalışırken, sistem uygulamanız adına bir uyanıklık düzenler. Bu nedenle, cihazın iş süresince uyanık kalmasını sağlamak için herhangi bir işlem yapmanıza gerek yoktur.
Bu, artık wakelock'ları ele almanıza gerek kalmayacağınız anlamına gelir ve bu yüzden ön plan hizmetlerinden farklı değildir. Uygulama açısından JobScheduler sizin hizmetiniz değildir, bir sistemdir, muhtemelen kuyruğu doğru ele alacak ve Google asla kendi çocuğunu sonlandırmayacaktır :)
Samsung, Samsung Aksesuar Protokolünde (SAP) startForegroundService'ten JobScheduler ve JobService'e geçti. Akıllı saatler gibi cihazların, işin bir uygulamanın ana iş parçacığı aracılığıyla bir kullanıcıyla etkileşim kurması gereken telefonlar gibi ana bilgisayarlarla konuşması gerektiğinde çok yararlıdır. İşler zamanlayıcı tarafından ana iş parçacığına gönderildiği için mümkün olur. Ancak işin ana iş parçacığında çalıştığını ve tüm ağır şeyleri diğer iş parçacıklarına ve zaman uyumsuz görevlere yüklediğini unutmayın.
Bu hizmet, gelen her işi uygulamanızın ana iş parçacığında çalışan bir işleyicide yürütür. Bu, yürütme mantığınızı seçtiğiniz başka bir iş parçacığına / işleyiciye / AsyncTask'a boşaltmanız gerektiği anlamına gelir
JobScheduler / JobService'e geçmenin tek tuzağı eski kodu yeniden düzenlemeniz gerekecek ve eğlenceli değil. Son iki günü yeni Samsung'un SAP uygulamasını kullanmak için yaptım. Çökme raporlarımı izleyeceğim ve çökmeleri tekrar görüp görmeyeceğinizi size bildireceğim. Teorik olarak olmamalı, ama her zaman farkında olmayabileceğimiz detaylar var.
GÜNCELLEME
Play Store tarafından bildirilen başka kilitlenme yok. Bu, JobScheduler / JobService'in böyle bir sorunu olmadığı ve startForegroundService sorunundan bir kez ve sonsuza kadar kurtulmak için bu modele geçişin doğru yaklaşım olduğu anlamına gelir. Umarım, Google / Android okur ve sonunda herkes için resmi bir rehberlik eder / önerir / sağlar.
GÜNCELLEME 2
SAP kullanan olanlar için ve SAP V2 kullanan nasıl soran JobService açıklaması verilmiştir.
Özel kodunuzda SAP'yi başlatmanız gerekir (Kotlin'dir):
SAAgentV2.requestAgent(App.app?.applicationContext,
MessageJobs::class.java!!.getName(), mAgentCallback)
Şimdi içeride neler olduğunu görmek için Samsung'un kodunu kodalamanız gerekiyor. SAAgentV2'de requestAgent uygulamasına ve aşağıdaki satıra bir göz atın:
SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);
where d defined as below
private SAAdapter d;
Şimdi SAAdapter sınıfına gidin ve aşağıdaki çağrıyı kullanarak bir işi zamanlayan onServiceConnectionRequested işlevini bulun:
SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12);
SAJobService sadece Android'd JobService'in bir uygulamasıdır ve iş planlaması yapan budur:
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());
}
Gördüğünüz gibi, buradaki son satır, bu sistem hizmetini almak ve bir iş planlamak için Android'd JobScheduler'ı kullanıyor.
RequestAgent çağrısında, önemli bir olay meydana geldiğinde kontrolü alacak bir geri çağrı işlevi olan mAgentCallback'i geçtik. Geri arama uygulamamda şu şekilde tanımlanır:
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, Samsung akıllı saatten gelen tüm istekleri işlemek için uyguladığım bir sınıf. Tam kod değil, sadece bir iskelet:
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) {
}
}
Gördüğünüz gibi MessageJobs, uygulamanız gereken ve cihazınızdan gelen tüm iletileri işleyen MessageSocket sınıfını da gerektirir.
Alt satırda, o kadar basit değil ve iç kısımlara ve kodlamaya biraz kazma gerektirir, ancak çalışır ve en önemlisi - çökmez.