Saya tahu, sudah terlalu banyak jawaban yang telah dipublikasikan, namun kenyataannya adalah - startForegroundService tidak dapat diperbaiki pada tingkat aplikasi dan Anda harus berhenti menggunakannya. Rekomendasi Google untuk menggunakan API Service # startForeground () dalam waktu 5 detik setelah Konteks # startForegroundService () dipanggil bukan sesuatu yang selalu bisa dilakukan oleh suatu aplikasi.
Android menjalankan banyak proses secara bersamaan dan tidak ada jaminan bahwa Looper akan memanggil layanan target Anda yang seharusnya memanggil startForeground () dalam waktu 5 detik. Jika layanan target Anda tidak menerima panggilan dalam 5 detik, Anda kurang beruntung dan pengguna Anda akan mengalami situasi PPA. Di jejak tumpukan Anda, Anda akan melihat sesuatu seperti ini:
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)
Seperti yang saya mengerti, Looper telah menganalisis antrian di sini, menemukan "pelaku" dan hanya membunuhnya. Sistem itu bahagia dan sehat sekarang, sementara pengembang dan pengguna tidak, tetapi karena Google membatasi tanggung jawab mereka pada sistem, mengapa mereka harus peduli pada dua yang terakhir? Rupanya tidak. Bisakah mereka membuatnya lebih baik? Tentu saja, misalnya mereka dapat melayani dialog "Aplikasi sedang sibuk", meminta pengguna untuk membuat keputusan tentang menunggu atau membunuh aplikasi, tetapi mengapa repot, itu bukan tanggung jawab mereka. Yang utama adalah sistemnya sehat sekarang.
Dari pengamatan saya, ini terjadi relatif jarang, dalam kasus saya sekitar 1 crash dalam sebulan untuk pengguna 1K. Mereproduksi itu tidak mungkin, dan bahkan jika direproduksi, tidak ada yang dapat Anda lakukan untuk memperbaikinya secara permanen.
Ada saran yang bagus di utas ini untuk menggunakan "bind" bukannya "start" dan kemudian ketika layanan siap, proses onServiceConnected, tapi sekali lagi, itu berarti tidak menggunakan panggilan startForegroundService sama sekali.
Saya pikir, tindakan yang benar dan jujur dari pihak Google adalah untuk memberi tahu semua orang bahwa startForegourndServcie memiliki kekurangan dan tidak boleh digunakan.
Pertanyaannya tetap: apa yang harus digunakan? Untungnya bagi kami, ada JobScheduler dan JobService sekarang, yang merupakan alternatif yang lebih baik untuk layanan latar depan. Itu pilihan yang lebih baik, karena itu:
Saat pekerjaan sedang berjalan, sistem memegang wakelock atas nama aplikasi Anda. Karena alasan ini, Anda tidak perlu melakukan tindakan apa pun untuk menjamin bahwa perangkat tetap terjaga selama pekerjaan berlangsung.
Ini berarti bahwa Anda tidak perlu lagi peduli menangani wakelocks dan itulah mengapa itu tidak berbeda dengan layanan latar depan. Dari sudut pandang implementasi, JobScheduler bukan layanan Anda, ini adalah sistem, mungkin akan menangani antrian dengan benar, dan Google tidak akan pernah menghentikan anaknya sendiri :)
Samsung telah beralih dari startForegroundService ke JobScheduler dan JobService di Samsung Accessory Protocol (SAP) mereka. Ini sangat membantu ketika perangkat seperti jam tangan pintar perlu berbicara dengan host seperti ponsel, di mana pekerjaan itu perlu berinteraksi dengan pengguna melalui utas utama aplikasi. Karena pekerjaan diposting oleh penjadwal ke utas utama, itu menjadi mungkin. Namun Anda harus ingat bahwa pekerjaan sedang berjalan di utas utama dan melepas semua tugas berat ke utas lain dan tugas asinkron.
Layanan ini mengeksekusi setiap pekerjaan masuk pada Handler yang berjalan di utas utama aplikasi Anda. Ini berarti Anda harus melepaskan logika eksekusi Anda ke utas / penangan / AsyncTask lain yang Anda pilih
Satu-satunya perangkap beralih ke JobScheduler / JobService adalah Anda harus memperbaiki kode lama, dan itu tidak menyenangkan. Saya telah menghabiskan dua hari terakhir melakukan hal itu untuk menggunakan implementasi SAP Samsung yang baru. Saya akan menonton laporan kerusakan saya dan memberi tahu Anda jika melihat lagi macet. Secara teoritis itu seharusnya tidak terjadi, tetapi selalu ada detail yang mungkin tidak kita sadari.
UPDATE
Tidak ada lagi crash yang dilaporkan oleh Play Store. Ini berarti bahwa JobScheduler / JobService tidak memiliki masalah seperti itu dan beralih ke model ini adalah pendekatan yang tepat untuk menghilangkan masalah startForegroundService sekali dan selamanya. Saya harap, Google / Android membacanya dan pada akhirnya akan memberikan komentar / saran / memberikan panduan resmi untuk semua orang.
PEMBARUAN 2
Untuk mereka yang menggunakan SAP dan bertanya bagaimana SAP V2 menggunakan penjelasan JobService ada di bawah ini.
Dalam kode khusus Anda, Anda perlu menginisialisasi SAP (ini Kotlin):
SAAgentV2.requestAgent(App.app?.applicationContext,
MessageJobs::class.java!!.getName(), mAgentCallback)
Sekarang Anda perlu mendekompilasi kode Samsung untuk melihat apa yang terjadi di dalamnya. Dalam SAAgentV2 lihat implementasi requestAgent dan baris berikut:
SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);
where d defined as below
private SAAdapter d;
Buka kelas SAAdapter sekarang dan temukan fungsiServiceConnectionRequested yang menjadwalkan pekerjaan menggunakan panggilan berikut:
SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12);
SAJobService hanyalah sebuah implementasi dari Android'd JobService dan ini adalah salah satu yang melakukan penjadwalan pekerjaan:
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());
}
Seperti yang Anda lihat, baris terakhir di sini menggunakan Android'd JobScheduler untuk mendapatkan layanan sistem ini dan untuk menjadwalkan pekerjaan.
Dalam panggilan requestAgent kami telah melewati mAgentCallback, yang merupakan fungsi panggilan balik yang akan menerima kontrol ketika peristiwa penting terjadi. Ini adalah bagaimana callback didefinisikan di aplikasi saya:
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 di sini adalah kelas yang telah saya terapkan untuk memproses semua permintaan yang datang dari smartwatch Samsung. Ini bukan kode lengkap, hanya kerangka:
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) {
}
}
Seperti yang Anda lihat, MessageJobs membutuhkan kelas MessageSocket juga yang perlu Anda implementasikan dan yang memproses semua pesan yang berasal dari perangkat Anda.
Intinya, ini tidak sesederhana itu dan itu membutuhkan beberapa penggalian untuk internal dan pengkodean, tetapi bekerja, dan yang paling penting - tidak crash.