Context.startForegroundService () daha sonra Service.startForeground () öğesini çağırmadı


258

ServiceAndroid O OS'de Class kullanıyorum .

ServiceArka planda kullanmayı planlıyorum .

Android dokümantasyon devletler bu

Uygulamanız API seviyesi 26 veya daha üstünü hedefliyorsa, uygulamanın kendisi ön planda değilse sistem arka plan hizmetlerini kullanma veya oluşturma konusunda kısıtlamalar uygular. Bir uygulamanın ön plan hizmeti oluşturması gerekiyorsa, uygulama aramalıdır startForegroundService().

Eğer kullanırsanız startForegroundService(), Serviceaşağıdaki hatayı atar.

Context.startForegroundService() did not then call
Service.startForeground() 

Bunun nesi var?


IOW, lütfen minimal tekrarlanabilir bir örnek verin . Bu, tüm Java yığın izlemesini ve kilitlenmeyi tetikleyen kodu içerir.
CommonsWare

3
Hata hala API 26 ve 27'de (27.0.3). Etkilenen android sürümleri 8.0 ve 8.1'dir. OnFreate () ve onStartCommand () öğelerine startForeground () öğesini ekleyerek çökme sayısını azaltabilirsiniz, ancak bazı kullanıcılar için kilitlenmeler yine de gerçekleşir. Atm'yi düzeltmenin tek yolu build.gradle dosyasındaki targetSdkVersion 25'dir.
Alex


1
Bu çözümü şimdi kullanıyorum. charm gibi çalışır theandroiddeveloper.com/blog/…
Prasad Pawar

1
Aynı problemim var. Bu sorunu düzeltirim. Uygulamamı bu konuda paylaştım stackoverflow.com/questions/55894636/…
Beyazid

Yanıtlar:


99

Google'ın Android 8.0'daki dokümanlarından davranış değişiklikleri :

Sistem, uygulama arka planda olsa bile uygulamaların Context.startForegroundService () öğesini çağırmasına izin verir. Ancak uygulama, hizmet oluşturulduktan sonra beş saniye içinde bu hizmetin startForeground () yöntemini çağırmalıdır.

Çözüm: Çağrı startForeground()içinde onCreate()için Servicekullandığınız hangiContext.startForegroundService()

Ayrıca bkz: Android 8.0 için Arka Plan Yürütme Sınırları (Oreo)


19
Bunu onStartCommandyöntemde yaptım , ama hala bu hatayı alıyorum. Aradım startForegroundService(intent)skinTenimde MainActivity. Belki de servis çok yavaş başlatılır. Hizmetin hemen başlatılacağına söz vermeden önce beş saniyelik sınırın olmaması gerektiğini düşünüyorum.
Kimi Chiu

29
5 saniyelik süre kesinlikle yeterli değildir, bu istisna hata ayıklama oturumlarında çok sık görülür. Ayrıca zaman zaman serbest bırakma modunda olacağından şüpheleniyorum. Ve FATAL İSTİSNA olmak sadece app çöküyor! Ben Thread.setDefaultUncaughtExceptionHandler () ile yakalamaya çalıştım, ama oraya ve android görmezden bile uygulama donuyor. Bu istisna handleMessage () tetiklenir ve ana döngü etkili bir şekilde sona erer ... geçici çözüm arıyor çünkü.
48'de southerton

Yayın alıcısında Context.startForegroundService () yöntemini çağırdım. Peki bu durumda nasıl başa çıkılır? çünkü onCreate () yayın alıcısında mevcut değil
Anand Savjani

6
@southerton Bunun onStartCommand()yerine çağırmanız gerektiğini düşünüyorum onCreate, çünkü hizmeti kapatıp tekrar başlatırsanız, onStartCommand()arama yapmadan gidebilir onCreate...
android geliştirici

1
Google ekibinden gelen yanıtı buradan kontrol edebiliriz issuetracker.google.com/issues/76112072#comment56
Prags

82

aradım ContextCompat.startForegroundService(this, intent)Hizmeti başlatmak için

Serviste onCreate

 @Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();

            startForeground(1, notification);
        }
}

48
Ben de. Ama yine de ara sıra bu hatayı alıyorum. Belki Android onCreate5 saniye içinde arayacağını garanti edemez . Bu yüzden bizi kuralları takip etmeye zorlamadan önce yeniden tasarlamalılar.
Kimi Chiu

5
Ben onStartCommand () startForeground çağırıyordu ve zaman zaman bu hatayı alıyordu. OnCreate'a taşıdım ve o zamandan beri görmedim (parmakları çaprazlamak).
Tyler

4
Bu, Oreo'da "APP_NAME çalışıyor. Kapatmak veya bilgi görmek için hafifçe vurun" yazan yerel bir bildirim oluşturur. Bu bildirimi göstermeyi nasıl durdurabilirim?
Artist404

6
Hayır, Android Ekibi bunun amaçlanan bir davranış olduğunu iddia etti. Bu yüzden uygulamamı yeniden tasarladım. Bu gülünç. Bu "amaçlanan davranış" nedeniyle uygulamaları daima yeniden tasarlamanız gerekir. Bu üçüncü sefer.
Kimi Chiu

12
Bu sorunun temel nedeni, hizmetin ön plana yükseltilmeden önce durdurulmasıdır. Ancak hizmet yıkıldıktan sonra iddia durmadı. Aradıktan StopServicesonra ekleyerek bunu yeniden oluşturmayı deneyebilirsiniz startForegroundService.
Kimi Chiu

55

Bu sorun neden oluyor, Android çerçevesinin hizmetinizin 5 saniye içinde başlayacağını garanti edememesi, ancak diğer yandan çerçevenin hizmeti başlatmaya çalışıp çalışmadığını kontrol etmeden 5 saniye içinde ön plan bildirimi üzerinde katı bir sınırlamanın olması gerekiyor. .

Bu kesinlikle bir çerçeve sorun, ancak bu sorunla karşı karşıya olan tüm geliştiriciler ellerinden geleni yapmıyor:

  1. startForegroundbir bildirim her ikisinde de olmalıdır onCreateve onStartCommandhizmetiniz zaten oluşturulmuşsa ve etkinliğiniz bir şekilde yeniden başlatmaya çalışıyorsa onCreateçağrılmaz.

  2. bildirim kimliği 0 olmamalıdır, aksi takdirde aynı sebep olmasa bile aynı kilitlenme olur.

  3. stopSelfdaha önce çağrılmamalıdır startForeground.

Yukarıdaki 3 ile bu sorun biraz azaltılabilir, ancak yine de bir düzeltme olmayabilir, gerçek düzeltme veya diyelim ki geçici çözüm hedef sdk sürümünüzü 25'e düşürmektir.

Google'ın neler olup bittiğini bile anlamayı reddettiği ve bunun onların hatası olduğuna inanmadığı için büyük olasılıkla Android P'nin bu sorunu taşımaya devam edeceğini unutmayın, daha fazla bilgi için # 36 ve 56 numaralı makaleleri okuyun.


9
Neden hem onCreate hem de onStartCommand? Sadece onStartCommand içine koyabilir misiniz?
rcell

stopSelf, startForeground => 'dan önce veya hemen sonra çağrılmamalıdır, çünkü bunu yaptığınızda "startForeground" ı iptal etmiş gibi görünür.
Frank

1
Bazı testler yaptım ve hizmet zaten oluşturulmuşsa onCreate çağrılmasa bile, startForeground'ı tekrar çağırmanıza gerek yok. Bu nedenle, onStartCommand'da değil, yalnızca onCreate yönteminde çağırabilirsiniz. Muhtemelen, tek servis örneğini sonuna kadar ilk çağrıdan sonra ön planda olarak görüyorlar.
Lxu

İçin ilk çağrının bildirim kimliği olarak 0 kullanıyorum startForeground. Bunu 1 olarak değiştirmek benim için sorunu giderir (umarım)
mr5

Peki çözüm nedir?
IgorGanapolsky

35

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.


İyi cevap ama önemli bir sorun var, JobIntentServicehemen IntentServiceOreo'nun altında çalışıyor , ancak Oreo ve üstünde bir İş planlıyor, bu yüzden JobIntentServicehemen başlamıyor. Daha fazla bilgi
CopsOnRoad

1
@CopsOnRoad Çok kullanışlı ve hemen benim durumumda başlıyor, Yazdığım gibi, telefon ve akıllı saat arasındaki gerçek zamanlı etkileşimler için kullanılıyor. Hiçbir şekilde, bir kullanıcı bu ikisi arasında veri göndermek için 15 dakika beklerdi. Çok iyi çalışıyor ve asla çökmüyor.
Oleg Gryb

1
Kulağa hoş geliyor, bazı örnek kodları paylaşabiliyorsanız cevabınızı çok değerli hale getireceğim, Android Job...için yeniyim ve başlamanın zaman aldığını ve ilerlemenin sürümü olduğunu gördüm AlarmManager.
CopsOnRoad

2
Muhtemelen zaman izin verdiğinde yapacağım: Özel özel koddan
ayırmam

1
@batmaci - JobService, SAP tarafından dahili olarak kullanılır. Ayrıntılar için OP'deki Güncelleme 2'ye bakın.
Oleg Gryb

32

Aradığınızda Context.startForegroundService(...)ve daha Context.stopService(...)önce aradığınızda uygulamanız çökecektir Service.startForeground(...).

Burada açık bir Repro var ForegroundServiceAPI26

Bu konuda bir hata açtım: Google sorun izleyici

Bu konuda birkaç hata açılıp kapatılmayacak.

İnşallah net repro adımları ile mayın kesim yapacak.

Google ekibi tarafından sağlanan bilgiler

Google sorun izleyici Yorum 36

Bu bir çerçeve hatası değildir; kasıtlı. Uygulamanın bir hizmet örneği ile başlıyorsa startForegroundService(), o olmalı ön plan durumuna bu hizmet örneği geçiş ve bildirim gösterilir. Hizmet örneği daha önce durdurulursa startForeground(), bu vaat yerine getirilmez: bu uygulamadaki bir hatadır.

Re # 31 , diğer uygulamaların doğrudan başlayabileceği bir Hizmet yayınlamak temelde güvensizdir. Bu hizmetin tüm başlangıç ​​eylemlerini gerektiği gibi ele alarak biraz hafifletebilirsiniz startForeground(), ancak açıkçası aklınızdakiler olmayabilir.

Google sorun izleyici Yorum 56

Burada aynı sonuca götüren birkaç farklı senaryo var.

Açıkça anlaşılır anlambilimsel sorun, bir şeyi başlatmaktan başka bir şey startForegroundService()değil, aslında üzerinden ön plana geçmeyi ihmal etmektir.startForeground() bir semantik sorun olmasıdır. Bu, bir uygulama hatası olarak kabul edilir. Hizmeti ön plana geçirmeden önce durdurmak bir uygulama hatasıdır. OP'nin temel noktası buydu ve bu yüzden bu konu "amaçlandığı gibi çalışıyor" olarak işaretlendi.

Bununla birlikte, bu sorunun sahte olarak algılanmasıyla ilgili sorular da vardır . Bu , gerçek bir sorun olarak görülüyor, ancak bu belirli hata izleyici sorunundan ayrı olarak izleniyor. Biz şikayetçi sağır değiliz.


2
Gördüğüm en iyi güncelleme issuetracker.google.com/issues/76112072#comment56 . Context.tartForegroundService sorunlu çağrı tamamen önler Context.bindService kullanmak için kodumu yeniden yazdı. Örnek kodumu github.com/paulpv/ForegroundServiceAPI26/tree/bound/app/src/…
swooby

evet, yazdığım gibi, bağlama işe yaramalı, ama zaten JobServive'a geçtim ve bu da kırılmadıkça hiçbir şeyi değiştirmeyeceğim ':)
Oleg Gryb

20

Bunu birkaç gün boyunca araştırdım ve çözüm buldum. Şimdi Android O'da arka plan sınırlamasını aşağıdaki gibi ayarlayabilirsiniz

Hizmet sınıfını çağıran hizmet

Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    SettingActivity.this.startForegroundService(serviceIntent);
} else {
    startService(serviceIntent);
}

ve servis sınıfı

public class DetectedService extends Service { 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
        }


        // Do whatever you want to do here
    }
}

3
En az binlerce kullanıcısı olan bazı uygulamalarda denediniz mi? Denedim, bazı kullanıcıların hala kilitlenme sorunları var.
Alex

Hayır hayır Tam kodu kullanıyorum ve istemcilerin çöktüğünü görmüyorum.
Ahmad Arslan

Ne kadar kullanıcınız var? Ben günde 10k kullanıcı vardı app ~ 10-30 + (hata ile) çöküyor gördüm. Tabii ki, targetSdkVersion 27 olduğunda sadece android 8.0 ve android 8.1'e sahip kullanıcılar için olur. TargetSdkVersion'u 25 olarak ayarladıktan hemen sonra tüm sorunlar 0 çökmeye kaybolur.
Alex

sadece 2200 kullanıcı: - / ama herhangi bir çökme olmadı
Ahmad Arslan

1
@ Jeeva, pek değil. Atm, compileSdkVersion 27 ile targetSdkVersion 25 kullanıyorum. Birçok denemeden sonra en iyi yol gibi görünüyor .... Umarım geliştirici.android.com/ topic/libraries/architecture/… Ağustos 2018'den önce bitirecekler çünkü android-developers.googleblog .com /
Alex

16

Cihaz uyanıkken nispeten sık güncellemeler yapan bir widget'ım var ve sadece birkaç gün içinde binlerce çökme görüyordum.

Sorun tetikleyici

Pixel 3 XL'imde bile cihazın çok fazla yüke sahip olduğunu düşünmediğimde bile sorunu fark ettim. Ve tüm kod yolları kapsanmıştır startForeground(). Ama sonra fark ettim ki birçok durumda servisim işi çok çabuk hallediyor. Uygulamamın tetikleyicisinin, sistemin aslında bir bildirim göstermek için etrafta dolaşmadan önce hizmetin bitiyor olması gerektiğine inanıyorum.

Geçici çözüm / çözüm

Tüm kazalardan kurtulabildim. Yaptığım şey çağrıyı kaldırmaktı stopSelf(). (Bildirimin gösterildiğinden oldukça emin olana kadar durağı ertelemeyi düşünüyordum, ancak kullanıcının gerekli değilse bildirimi görmesini istemiyorum.) Hizmet bir dakika veya sistem boşta kaldığında herhangi bir istisna atmadan normal şekilde yok eder.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    stopForeground(true);
} else {
    stopSelf();
}

11

Bu konuda çok fazla zaman harcadığım için sadece bir uyarı. startForeground(..)İlk şey olarak adlandırmama rağmen bu istisnayı almaya devam ettim onCreate(..). Sonunda sorunun kullanımdan kaynaklandığını gördüm NOTIFICATION_ID = 0. Başka bir değer kullanmak bunu düzeltiyor gibi görünüyor.


Benim durumumda ID 1 kullanıyordum, teşekkürler, sorun çözüldü
Amin Pinjari

4
ID 101 kullanıyorum, yine de bazen bu hatayı alıyorum.
CopsOnRoad

9

Bu soruna geçici bir çözüm var. Bu düzeltmeyi kendi uygulamamda (300K + DAU) doğruladım, bu da bu tür bir çökmenin en az% 95'ini azaltabilir, ancak yine de% 100 bu sorunu önleyemez.

Bu sorun, hizmet Google'ın belgelediği gibi başladıktan hemen sonra startForeground () yöntemini çağırdığınızda bile oluşur. Bunun nedeni, hizmet oluşturma ve başlatma işleminin birçok senaryoda zaten 5 saniyeden uzun sürmesi olabilir, bu nedenle startForeground () yöntemini ne zaman ve nerede çağırırsanız arayın, bu çökme kaçınılmazdır.

Benim çözümüm, hizmetinizin ne kadar sürede oluşturulması ve başlatılması gerektiğine bakılmaksızın startForeground () yönteminin startForegroundService () yönteminden sonra 5 saniye içinde yürütülmesini sağlamaktır. İşte ayrıntılı çözüm.

  1. Başlangıçta startForegroundService kullanmayın, auto_create bayrağıyla bindService () kullanın. Hizmetin başlatılmasını bekleyecektir. İşte kod, benim örnek hizmet MusicService:

    final Context applicationContext = context.getApplicationContext();
    Intent intent = new Intent(context, MusicService.class);
    applicationContext.bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            if (binder instanceof MusicBinder) {
                MusicBinder musicBinder = (MusicBinder) binder;
                MusicService service = musicBinder.getService();
                if (service != null) {
                    // start a command such as music play or pause.
                    service.startCommand(command);
                    // force the service to run in foreground here.
                    // the service is already initialized when bind and auto_create.
                    service.forceForeground();
                }
            }
            applicationContext.unbindService(this);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }, Context.BIND_AUTO_CREATE);
  2. Sonra MusicBinder uygulaması:

    /**
     * Use weak reference to avoid binder service leak.
     */
     public class MusicBinder extends Binder {
    
         private WeakReference<MusicService> weakService;
    
         /**
          * Inject service instance to weak reference.
          */
         public void onBind(MusicService service) {
             this.weakService = new WeakReference<>(service);
         }
    
         public MusicService getService() {
             return weakService == null ? null : weakService.get();
         }
     }
  3. En önemli bölüm, MusicService gerçekleştirme, forceForeground () yöntemi startForeground () yönteminin startForegroundService () yönteminden hemen sonra çağrılmasını sağlar:

    public class MusicService extends MediaBrowserServiceCompat {
    ...
        private final MusicBinder musicBind = new MusicBinder();
    ...
        @Override
        public IBinder onBind(Intent intent) {
            musicBind.onBind(this);
            return musicBind;
        }
    ...
        public void forceForeground() {
            // API lower than 26 do not need this work around.
            if (Build.VERSION.SDK_INT >= 26) {
                Intent intent = new Intent(this, MusicService.class);
                // service has already been initialized.
                // startForeground method should be called within 5 seconds.
                ContextCompat.startForegroundService(this, intent);
                Notification notification = mNotificationHandler.createNotification(this);
                // call startForeground just after startForegroundService.
                startForeground(Constants.NOTIFICATION_ID, notification);
            }
        }
    }
  4. 1. adım kod snippet'ini, uygulamanızı açmadan bir widget'ta bir ön plan hizmetini (widget düğmesine tıklama) başlatmak istiyorsanız, beklemede olan bir amaçta çalıştırmak istiyorsanız, kod snippet'ini bir yayın alıcısına sarabilirsiniz ve başlat hizmeti komutu yerine bir yayın olayı başlatır.

Hepsi bu. Umarım yardımcı olur. İyi şanslar.



7

Burayı ziyaret eden herkes aynı şeyi yaşadığından, daha önce kimsenin denemediği çözümümü paylaşmak istiyorum (bu soruda zaten). Durdurulmuş bir kesme noktasında bile çalıştığından emin olabilirimBu yöntemi onaylayan .

Sorun, Service.startForeground(id, notification)servisin kendisinden arama yapmak , değil mi? Android Framework maalesef 5 saniye Service.startForeground(id, notification)içinde aramayı garanti etmiyor, Service.onCreate()ancak yine de istisnayı atıyor, bu yüzden bu şekilde geldim.

  1. Aramadan önce hizmeti, hizmetin bir bağlayıcısıyla bir bağlama bağlama Context.startForegroundService()
  2. Context.startForegroundService() Bağlama başarılı olursa, servis bağlantısından arayın ve hemen servis bağlantısının içini arayın Service.startForeground() .
  3. ÖNEMLİ NOT: Bir try-catchContext.bindService() içindeki yöntemi çağırın, çünkü bazı durumlarda çağrı bir istisna fırlatabilir, bu durumda doğrudan aramaya güvenmeniz ve başarısız olmayacağını ummanız gerekir. Bir örnek, yayın alıcı bağlamı olabilir, ancak uygulama bağlamını almak bu durumda bir istisna oluşturmaz, ancak içeriği doğrudan kullanır.Context.startForegroundService()

Bu, hizmeti bağladıktan sonra ve "startForeground" çağrısını tetiklemeden önce bir kesme noktasında beklediğimde bile çalışır. 3-4 saniye beklemek istisnayı tetiklemez, 5 saniye sonra istisnayı atar. (Cihaz 5 saniye içinde iki kod satırı yürütemezse, bunu çöp kutusuna atma zamanı.)

Bu nedenle, bir servis bağlantısı oluşturmaya başlayın.

// Create the service connection.
ServiceConnection connection = new ServiceConnection()
{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service)
    {
        // The binder of the service that returns the instance that is created.
        MyService.LocalBinder binder = (MyService.LocalBinder) service;

        // The getter method to acquire the service.
        MyService myService = binder.getService();

        // getServiceIntent(context) returns the relative service intent 
        context.startForegroundService(getServiceIntent(context));

        // This is the key: Without waiting Android Framework to call this method
        // inside Service.onCreate(), immediately call here to post the notification.
        myService.startForeground(myNotificationId, MyService.getNotification());

        // Release the connection to prevent leaks.
        context.unbindService(this);
    }

    @Override
    public void onBindingDied(ComponentName name)
    {
        Log.w(TAG, "Binding has dead.");
    }

    @Override
    public void onNullBinding(ComponentName name)
    {
        Log.w(TAG, "Bind was null.");
    }

    @Override
    public void onServiceDisconnected(ComponentName name)
    {
        Log.w(TAG, "Service is disconnected..");
    }
};

Hizmetinizin içinde, hizmetinizin örneğini döndüren bir bağlayıcı oluşturun.

public class MyService extends Service
{
    public class LocalBinder extends Binder
    {
        public MyService getService()
        {
            return MyService.this;
        }
    }

    // Create the instance on the service.
    private final LocalBinder binder = new LocalBinder();

    // Return this instance from onBind method.
    // You may also return new LocalBinder() which is
    // basically the same thing.
    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return binder;
    }
}

Ardından, hizmeti bu bağlamdan bağlamayı deneyin . Başarılı olursa ServiceConnection.onServiceConnected(), kullandığınız hizmet bağlantısından yöntemi çağırır . Ardından, mantığı yukarıda gösterilen kodda kullanın. Örnek bir kod şöyle görünecektir:

// Try to bind the service
try
{
     context.bindService(getServiceIntent(context), connection,
                    Context.BIND_AUTO_CREATE);
}
catch (RuntimeException ignored)
{
     // This is probably a broadcast receiver context even though we are calling getApplicationContext().
     // Just call startForegroundService instead since we cannot bind a service to a
     // broadcast receiver context. The service also have to call startForeground in
     // this case.
     context.startForegroundService(getServiceIntent(context));
}

Geliştirdiğim uygulamalar üzerinde çalışıyor gibi görünüyor, bu yüzden denediğinizde de çalışmalı.


5

Android O API 26 ile ilgili sorun

Hizmeti hemen durdurursanız (hizmetiniz gerçekten çalışmazsa (ifade / anlama) ve ANR aralığının altındaysanız, durmadan önce startForeground'u aramanız gerekir.

https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5

Bu Yaklaşımı Denedi Ama Hala Bir Hata Yaratıyor: -

if (Util.SDK_INT > 26) {
    mContext.startForegroundService(playIntent);
} else {
    mContext.startService(playIntent);
}

Hata Çözülene kadar Bunu Kullanıyorum

mContext.startService(playIntent);

3
Util.SDK_INT> = 26 olmalı, sadece daha büyük değil
Andris

4

Aynı sorunla karşı karşıyayım ve zaman geçirdikten sonra bir çözüm bulundu kod aşağıda deneyebilirsiniz. Eğer kullanmanız Servicedaha sonra bu kodu onCreate'a Intent Servicekoyarsanız, kullanımınızı bu kodu onHandleIntent'e koyun.

if (Build.VERSION.SDK_INT >= 26) {
        String CHANNEL_ID = "my_app";
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                "MyApp", NotificationManager.IMPORTANCE_DEFAULT);
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("")
                .setContentText("").build();
        startForeground(1, notification);
    }

4

Bu konuyu araştırıyorum ve şimdiye kadar keşfettiğim şey bu. Buna benzer bir kodumuz varsa, bu kilitlenme meydana gelebilir:

MyForegroundService.java

public class MyForegroundService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
    }
}

MainActivity.java

Intent serviceIntent = new Intent(this, MyForegroundService.class);
startForegroundService(serviceIntent);
...
stopService(serviceIntent);

Kural dışı durum, aşağıdaki kod bloğuna atılır:

ActiveServices.java

private final void bringDownServiceLocked(ServiceRecord r) {
    ...
    if (r.fgRequired) {
        Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
                  + r);
        r.fgRequired = false;
        r.fgWaiting = false;
        mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
        mAm.mHandler.removeMessages(
                    ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        if (r.app != null) {
            Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
            msg.obj = r.app;
            msg.getData().putCharSequence(
                ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
            mAm.mHandler.sendMessage(msg);
         }
    }
    ...
}

Bu yöntem daha önce yürütülür onCreate(), MyForegroundServiceçünkü Android ana işleyici üzerinde hizmetin oluşturulmasını zamanlar, ancak bir yarış koşulu olan a'da bringDownServiceLockedçağrılır BinderThread. Bu , kazaya neden olacak MyForegroundServicearama şansı olmadığı anlamına gelir startForeground.

Biz emin olmak zorunda Bunu düzeltmek için bringDownServiceLockedönce çağrılmaz onCreate()ait MyForegroundService.

public class MyForegroundService extends Service {

    private static final String ACTION_STOP = "com.example.MyForegroundService.ACTION_STOP";

    private final BroadcastReceiver stopReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            context.removeStickyBroadcast(intent);
            stopForeground(true);
            stopSelf();
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
        registerReceiver(
            stopReceiver, new IntentFilter(ACTION_STOP));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(stopReceiver);
    }

    public static void stop(Context context) {
        context.sendStickyBroadcast(new Intent(ACTION_STOP));
    }
}

Sabit yayınlar kullanarak biz yayın kaybetti ve almaz emin olun stopReceiverkısa sürede tescil edilmiştir olarak niyet durdurmasını almaktadır onCreate()ait MyForegroundService. Bu zamana kadar aradık startForeground(...). Ayrıca stopReceiver'ın bir dahaki sefere bildirilmesini önlemek için bu yapışkan yayını kaldırmamız gerekir.

Lütfen yöntemin sendStickyBroadcastkullanımdan kaldırıldığını ve bu sorunu gidermek için yalnızca geçici bir geçici çözüm olarak kullandığımı unutmayın .


Hizmeti durdurmak istediğinizde, context.stopService (serviceIntent) yerine çağırmalısınız.
makovkastar

4

Çok fazla cevap var ama hiçbiri benim durumumda işe yaramadı.

Bu şekilde hizmete başladım.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(intent);
} else {
    startService(intent);
}

Ve onStartCommand'daki hizmetimde

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("SmartTracker Running")
                .setAutoCancel(true);
        Notification notification = builder.build();
        startForeground(NOTIFICATION_ID, notification);
    } else {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("SmartTracker is Running...")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(true);
        Notification notification = builder.build();
        startForeground(NOTIFICATION_ID, notification);
    }

Ve sıfır olmayan NOTIFICATION_ID değerini ayarlamayı unutmayın

private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
private static final int NOTIFICATION_ID = 555;

SO her şey mükemmeldi ama yine de 8.1'de çöküyor, bu yüzden sebep aşağıdaki gibiydi.

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            stopForeground(true);
        } else {
            stopForeground(true);
        }

Kaldır notificaton ile dur ön plan çağırdı ama bir kez bildirim kaldırıldı hizmet arka plan haline ve arka plan hizmeti arka plan android O çalıştıramazsınız. push alındıktan sonra başladı.

Yani sihirli kelime

   stopSelf();

Şimdiye kadar hizmet çöküyor herhangi bir neden yukarıdaki tüm adımları izleyin ve keyfini çıkarın.


if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.O) {stopForeground (true); } else {stopForeground (true); } eğer başka ne içinse? her iki durumda da aynı şeyi yapıyordunuz, stopForeground (true);
user3135923

Sanırım stopSelf (); stopForeground (true) yerine else bloğunun içinde;
Justin Stanley

1
Evet, @ JustinStanley stopSelf () kullanır;
sandy

Olmamalı startForegroundde çağrılabilir onCreate?
Aba

Notification sınıfını değil NotificationManager'ı kullanıyorum. Bunu nasıl uygulayabilirim?
Prajwal K

3

https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)

StartService'e (Intent) benzer, ancak hizmetin çalışmaya başladıktan sonra Hizmetin startForeground (int, android.app.Notification) öğesini arayacağı üstü kapalı bir vaatle. Hizmet, bunu yapmak için ANR aralığıyla karşılaştırılabilir bir süre verilir, aksi takdirde sistem otomatik olarak hizmeti durdurur ve uygulamayı ANR olarak bildirir.

Sıradan startService'in (Niyet) aksine, bu yöntem, hizmeti barındıran uygulamanın bir ön planda olup olmadığına bakılmaksızın herhangi bir zamanda kullanılabilir.

Service.startForeground(int, android.app.Notification)onCreate () yöntemini çağırdığınızdan emin olun, böylece çağrılacaktır .. bunu yapmanızı engelleyebilecek herhangi bir durumunuz varsa, normal olanı kullanmaktan daha iyi olur Context.startService(Intent)ve Service.startForeground(int, android.app.Notification)kendinizi çağırırsınız .

Görünüşe göre yok edilmeden önce Context.startForegroundService()aradığınızdan emin olmak için bir bekçi köpeği ekliyor Service.startForeground(int, android.app.Notification)...


3

Lütfen onCreate () yöntemi içinde herhangi bir StartForgroundServices çağırma , işçi iş parçacığı yaptıktan sonra onStartCommand () içinde StartForground hizmetlerini çağırmanız gerekir, aksi takdirde her zaman ANR alırsınız, bu yüzden lütfen onStartCommand () ana iş parçacığına karmaşık giriş yazmayın ;

public class Services extends Service {

    private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker Running")
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(0, notification);
            Log.e("home_button","home button");
        } else {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker is Running...")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(0, notification);
            Log.e("home_button_value","home_button_value");

        }
        return super.onStartCommand(intent, flags, startId);

    }
}

EDIT: Dikkat! startForeground işlevi ilk bağımsız değişken olarak 0 değerini alamaz, bir istisna oluşturur! bu örnek yanlış işlev çağrısı içeriyorsa, 0'ı 0 veya Max'ten büyük olmayacak şekilde kendi sabitinize değiştirin (Int32)


2

Hatta çağırdıktan sonra startForegroundin Servicediyoruz eğer, bazı cihazlarda çöküyor stopServicehemen önce onCreateçağrılır. Bu nedenle, hizmeti ek bir bayrakla başlatarak bu sorunu çözdüm:

Intent intent = new Intent(context, YourService.class);
intent.putExtra("request_stop", true);
context.startService(intent);

ve durdurulmaya başlanıp başlanmadığını görmek içinStartCommand'a bir kontrol ekledi:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    //call startForeground first
    if (intent != null) {
        boolean stopService = intent.getBooleanExtra("request_stop", false);
        if (stopService) {
            stopSelf();
        }
    }

    //Continue with the background task
    return START_STICKY;
}

Not: Servis çalışmazsa, ilk önce bir servis olan hizmeti başlatır.


2

Hedef sdk 28 veya üstünü kullandığınızda android 9 cihazı için feryat olarak bir izin eklemelisiniz, yoksa istisna her zaman gerçekleşir:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

1

Sadece fonksiyonu PendingIntentçağırmadan önce null olup olmadığını kontrol ediyorum context.startForegroundService(service_intent).

bu benim için çalışıyor

PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_NO_CREATE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pendingIntent==null){
            context.startForegroundService(service_intent);
        }
        else
        {
            context.startService(service_intent);
        }
}

Bu aslında sadece startForegroundService yerine startService için hizmeti başlatır. İf ifadesinin iç içe yerleştirilmesi gerekir, aksi takdirde uygulama bir noktada hizmetleri sonraki bir sürümde kullanmaya çalışırken çökebilir.
a54studio

1

Hizmet veya IntentService Oluşturulduktan hemen sonra startForeground yöntemini çağırmanız yeterlidir. bunun gibi:

import android.app.Notification;
public class AuthenticationService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(1,new Notification());
    }
}

1
FATAL EXCEPTION: ana android.app.RemoteServiceException: start için hatalı bildirimDışı: java.lang.RuntimeException: hizmet bildirimi için geçersiz kanal: Bildirim (kanal = null pri = 0 contentView = boş titreşim = boş ses = boş varsayılanlar = 0x0 bayraklar = 0x40 android.app.ActivityThread $ android.os.Looper.loop (Looper'da android.os.Handler.dispatchMessage (Handler.java:106) adresindeki $ = handleMessage (ActivityThread.java:1821) adresindeki color = 0x00000000 vis = PRIVATE). java: 164) android.app.ActivityThread.main'de (ActivityThread.java:6626)
Gerry

1

Tamam, bu konuda fark ettiğim bir kaç kişiye de yardımcı olabilir. Bu kesinlikle gördüğüm olayları nasıl düzeltebileceğimi anlamak için test etmektir. Basitçe söylemek gerekirse, bunu sunum yapandan çağıran bir yöntemim olduğunu varsayalım.

context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));

try {
    Thread.sleep(10000);
} catch (InterruptedException e) {
    e.printStackTrace();
}       

Bu aynı hatayla çökecektir. Hizmet, yöntem tamamlanana kadar BAŞLATILMAZ, bu nedenle onCreate()hizmette hayır .

Bu nedenle, kullanıcı arayüzünü ana iş parçacığından güncelleseniz bile, bu yöntemi ondan sonra tutabilecek bir şey varsa, zamanında başlamaz ve size korkunç Ön Plan Hatası verir. Benim durumumda bir şeyleri bir kuyruğa yükledik ve her biri çağırdık startForegroundService, ama arka planda her biri ile bir miktar mantık vardı. Dolayısıyla, mantık arka arkaya çağrıldıklarından bu yöntemi tamamlamak için çok uzun sürerse, çarpışma süresi. YaşlıstartService sadece görmezden geldi ve yoluna devam etti ve her seferinde aradığımızdan, bir sonraki tur bitecekti.

Bu, arka planda bir iş parçacığından hizmeti çağırdıysam, tam olarak başlangıçta ve hemen çalıştırılamayacağını merak ettim, bu yüzden denemeye başladım. Bu hemen başlamıyor olsa da, çökmez.

new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
               context.startForegroundService(new Intent(context, 
           TaskQueueExecutorService.class));
               try {
                   Thread.sleep(10000);
               } catch (InterruptedException e) {
                  e.printStackTrace();
              }       
        }
});

Neden bu ana iplik zamanında işleyebilir kadar beklemek zorlar şüpheli neden çöküyor bilmek gibi olmayacak. Ana iş parçacığına bağlamak için ideal olmadığını biliyorum, ancak kullanımım arka planda çağırdığından, çökmekten ziyade tamamlanana kadar beklerse gerçekten endişelenmiyorum.


hizmetinizi arka plandan mı yoksa etkinlik olarak mı başlatıyorsunuz?
Mateen Chaudhry

Arka fon. Sorun, Android'in hizmetin ayrılan zamanda başlayacağını garanti edememesidir. Biri, istersen bunu görmezden gelmek için bir hata atacaklarını düşünür, ancak Android değil. Ölümcül olmalı. Bu bizim app yardımcı oldu, ama tamamen düzeltmek vermedi.
a54studio

1

@Humazed cevabında bazı kodlar ekliyorum. Yani ilk bildirim yok. Bu bir çözüm olabilir ama benim için işe yarıyor.

@Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("")
                    .setColor(ContextCompat.getColor(this, R.color.transparentColor))
                    .setSmallIcon(ContextCompat.getColor(this, R.color.transparentColor)).build();

            startForeground(1, notification);
        }
}

Bildirime küçük simge ve renkte şeffafColor ekliyorum. Çalışacak.


0

Hemen startService(intent)yerine hizmetin başlatılması Context.startForeground()ve çağrılmasıyla ilgili sorunu çözdüm . Ayrıca, önyüklemede hizmeti başlatırsanız, önyükleme yayınında hizmeti başlatan Etkinliği başlatabilirsiniz. Kalıcı bir çözüm olmamasına rağmen işe yarıyor.startForegound()super.OnCreate()


bazı Xiaomi cihazlarında etkinlikler arka plandan başlatılamaz
Mateen Chaudhry

0

İçindeki verileri güncelleme onStartCommand(...)

onBind (...)

onBind(...)başlatmak için daha iyi bir yaşam döngüsü olaydır startForegroundVS. onCreate(...)için onBind(...)bir in geçtiği Intentönemli verileri ihtiva edebilir Bundlebaşlatmak için gereken Service. Ancak, ilk kez oluşturulduğunda veya daha sonra tekrar onStartCommand(...)çağrıldığında olduğu gibi gerekli değildir Service.

onStartCommand (...)

startForegroundiçinde onStartCommand(...)güncellenmesi için önemlidir Servicezaten oluşturulduktan sonra.

A oluşturulduktan ContextCompat.startForegroundService(...)sonra çağrılır ve çağrılmaz. Bu nedenle, güncellenen veriler içine geçirilebilir aracılığıyla verileri güncelleştirmek için .ServiceonBind(...)onCreate(...)onStartCommand(...)Intent BundleService

Örneklem

Ben uygulamak için bu modeli kullanıyorum PlayerNotificationManageriçinde Coinverse cryptocurrency haber app.

Faaliyet / Fragment.kt

context?.bindService(
        Intent(context, AudioService::class.java),
        serviceConnection, Context.BIND_AUTO_CREATE)
ContextCompat.startForegroundService(
        context!!,
        Intent(context, AudioService::class.java).apply {
            action = CONTENT_SELECTED_ACTION
            putExtra(CONTENT_SELECTED_KEY, contentToPlay.content.apply {
                audioUrl = uri.toString()
            })
        })

AudioService.kt

private var uri: Uri = Uri.parse("")

override fun onBind(intent: Intent?) =
        AudioServiceBinder().apply {
            player = ExoPlayerFactory.newSimpleInstance(
                    applicationContext,
                    AudioOnlyRenderersFactory(applicationContext),
                    DefaultTrackSelector())
        }

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    intent?.let {
        when (intent.action) {
            CONTENT_SELECTED_ACTION -> it.getParcelableExtra<Content>(CONTENT_SELECTED_KEY).also { content ->
                val intentUri = Uri.parse(content.audioUrl)
                // Checks whether to update Uri passed in Intent Bundle.
                if (!intentUri.equals(uri)) {
                    uri = intentUri
                    player?.prepare(ProgressiveMediaSource.Factory(
                            DefaultDataSourceFactory(
                                    this,
                                    Util.getUserAgent(this, getString(app_name))))
                            .createMediaSource(uri))
                    player?.playWhenReady = true
                    // Calling 'startForeground' in 'buildNotification(...)'.          
                    buildNotification(intent.getParcelableExtra(CONTENT_SELECTED_KEY))
                }
            }
        }
    }
    return super.onStartCommand(intent, flags, startId)
}

// Calling 'startForeground' in 'onNotificationStarted(...)'.
private fun buildNotification(content: Content): Unit? {
    playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
            this,
            content.title,
            app_name,
            if (!content.audioUrl.isNullOrEmpty()) 1 else -1,
            object : PlayerNotificationManager.MediaDescriptionAdapter {
                override fun createCurrentContentIntent(player: Player?) = ...
                override fun getCurrentContentText(player: Player?) = ...
                override fun getCurrentContentTitle(player: Player?) = ...
                override fun getCurrentLargeIcon(player: Player?,
                                                 callback: PlayerNotificationManager.BitmapCallback?) = ...
            },
            object : PlayerNotificationManager.NotificationListener {
                override fun onNotificationStarted(notificationId: Int, notification: Notification) {
                    startForeground(notificationId, notification)
                }
                override fun onNotificationCancelled(notificationId: Int) {
                    stopForeground(true)
                    stopSelf()
                }
            })
    return playerNotificationManager.setPlayer(player)
}

-1

Hizmet

class TestService : Service() {

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate")

        val nBuilder = NotificationCompat.Builder(this, "all")
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("TestService")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        startForeground(1337, nBuilder.build())
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val rtn = super.onStartCommand(intent, flags, startId)

        if (intent?.action == STOP_ACTION) {
            Log.d(TAG, "onStartCommand -> STOP")
            stopForeground(true)
            stopSelf()
        } else {
            Log.d(TAG, "onStartCommand -> START")
        }

        return rtn
    }

    override fun onDestroy() {
        Log.d(TAG, "onDestroy")
        super.onDestroy()
    }

    override fun onBind(intent: Intent?): IBinder? = null

    companion object {

        private val TAG = "TestService"
        private val STOP_ACTION = "ly.zen.test.TestService.ACTION_STOP"

        fun start(context: Context) {
            ContextCompat.startForegroundService(context, Intent(context, TestService::class.java))
        }

        fun stop(context: Context) {
            val intent = Intent(context, TestService::class.java)
            intent.action = STOP_ACTION
            ContextCompat.startForegroundService(context, intent)
        }

    }

}

test cihazı

val nChannel = NotificationChannel("all", "All", NotificationManager.IMPORTANCE_NONE)
val nManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nManager.createNotificationChannel(nChannel)

start_test_service.setOnClickListener {
    TestService.start(this@MainActivity)
    TestService.stop(this@MainActivity)
}

Sonuç

D/TestService: onCreate
D/TestService: onStartCommand -> START
D/TestService: onStartCommand -> STOP
D/TestService: onDestroy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.