Okey dokey. Bu sorunu cehenneme çevirip geri döndüm. İşte nasıl devam edeceğiniz. Böcekler var. Bu gönderi, uygulamadaki hataların nasıl analiz edileceğini ve sorunların nasıl çözüleceğini açıklamaktadır.
Özetlemek gerekirse, işlerin nasıl yürümesi gerektiği burada. Çalışan hizmetler rutin olarak atılacak ve yaklaşık her 30 dakikada bir sonlandırılacaktır. Bundan daha uzun süre hayatta kalmak isteyen hizmetler Service.startForeground'u aramalıdır, bu da bildirim çubuğuna bir bildirim yerleştirir, böylece kullanıcılar hizmetinizin kalıcı olarak çalıştığını ve potansiyel olarak pil ömrünü emdiğini bilir. Herhangi bir zamanda yalnızca 3 hizmet süreci kendilerini ön plan hizmetleri olarak aday gösterebilir. Üçten fazla ön plan hizmeti varsa, Android en eski hizmeti toplama ve fesih için aday olarak gösterecektir.
Ne yazık ki, Android'de ön plan hizmetlerinin önceliklendirilmesiyle ilgili olarak çeşitli hizmet bağlama bayrakları kombinasyonları tarafından tetiklenen hatalar vardır. Hizmetinizi bir ön plan hizmeti olarak doğru bir şekilde aday göstermiş olsanız bile, sürecinizdeki hizmetlere herhangi bir bağlantı belirli bağlayıcı bayrak kombinasyonlarıyla yapılmışsa, Android hizmetinizi yine de sonlandırabilir. Detaylar aşağıda verilmiştir.
Çok az hizmetin ön plan hizmetleri olması gerektiğini unutmayın. Genel olarak, yalnızca açılıp kapatılabilen veya kullanıcılar tarafından iptal edilebilen bir tür sürekli aktif veya uzun süreli bir internet bağlantınız varsa, yalnızca bir ön plan hizmeti olmanız gerekir. Ön plan durumuna ihtiyaç duyan hizmetlere örnekler: UPNP sunucuları, çok büyük dosyaların uzun süreli indirmeleri, dosya sistemlerini wi-fi ile senkronize etme ve müzik çalma.
Sadece ara sıra yoklama yapıyorsanız veya sistem yayın alıcılarını veya sistem olaylarını bekliyorsanız, hizmetinizi bir zamanlayıcıda veya yayın alıcılarına yanıt olarak uyandırmanız ve ardından hizmetinizin tamamlandığında ölmesine izin vermeniz daha iyi olur. Hizmetler için tasarlanmış davranış budur. Hayatta kalmanız gerekiyorsa okumaya devam edin.
İyi bilinen gereksinimlerdeki kutuları işaretledikten sonra (örneğin, Service.startForeground'u çağırmak), bakılacak bir sonraki yer Context.bindService çağrılarında kullandığınız bayraklardır. Bağlamak için kullanılan bayraklar, hedef hizmet sürecinin önceliğini çeşitli beklenmedik şekillerde etkiler. Özellikle, belirli bağlama bayraklarının kullanılması, Android'in ön plan hizmetinizi yanlış bir şekilde normal bir hizmete düşürmesine neden olabilir. Proses önceliğini atamak için kullanılan kod oldukça yoğun bir şekilde karıştırıldı. Özellikle, API 14 + 'da eski bağlama bayraklarını kullanırken hatalara neden olabilecek revizyonlar vardır; ve 4.2.1'de kesin hatalar var.
Tüm bunlarda arkadaşınız, Etkinlik yöneticisinin hizmet sürecinize hangi önceliği atadığını anlamak ve yanlış bir öncelik atadığı durumları tespit etmek için kullanılabilecek sysdump yardımcı programıdır. Hizmetinizi çalıştırın ve çalıştırın ve ardından ana bilgisayarınızdaki komut isteminden aşağıdaki komutu verin:
adb shell dumpsys etkinlik işlemleri> tmp.txt
İçeriği incelemek için not defteri (wordpad / write değil) kullanın.
Öncelikle, hizmetinizi başarıyla ön planda çalıştırmayı başardığınızı doğrulayın. Dumpsys dosyasının ilk bölümü, her işlem için ActivityManager özelliklerinin bir açıklamasını içerir. Dumpsys dosyasının ilk bölümünde uygulamanıza karşılık gelen aşağıdakine benzer bir satır arayın:
APP UID 10068 ProcessRecord {41937d40 2205: tunein.service / u0a10068}
Aşağıdaki bölümde foregroundServices = true olduğunu doğrulayın. Gizli ve boş ayarlar konusunda endişelenmeyin; Süreçteki Faaliyetlerin durumunu açıklarlar ve içlerinde hizmetler bulunan süreçlerle özellikle alakalı görünmüyorlar. ForegroundService doğru değilse, bunu gerçekleştirmek için Service.startForeground'u aramanız gerekir.
Bakmanız gereken sonraki şey, dosyanın sonuna yakın olan "LRU listesini işle (oom_adj'ye göre sıralı):" başlıklı bölümdür. Bu listedeki girişler, Android'in uygulamanızı gerçekten bir ön plan hizmeti olarak sınıflandırıp sınıflandırmadığını belirlemenize olanak tanır. Süreciniz bu listenin en altındaysa, özet yok etme için birincil adaydır. Süreciniz listenin en üstüne yakınsa, neredeyse yok edilemez.
Bu tablodaki bir satıra bakalım:
Proc
Bu, her şeyi doğru yapan bir ön plan hizmetinin bir örneğidir. Buradaki anahtar alan "adj =" alanıdır. Bu, her şey söylendikten sonra işleminizin ActivityManagerService tarafından atandığı önceliği gösterir. "Adj = prcp" (görünür ön plan hizmeti) olmasını istiyorsunuz; veya "adj = vis" (bir faaliyet içeren görünür süreç) veya "ön" (ön plan etkinliği olan süreç). "Adj = svc" (hizmet süreci) veya "adj = svcb" (eski hizmet?) Veya "adj = bak" (boş arka plan işlemi) ise, işleminiz büyük olasılıkla sonlandırma adayıdır ve sonlandırılır hafızayı geri kazanmak için herhangi bir baskı olmasa bile her 30 dakikadan daha az sıklıkta değil. Satırda kalan işaretler, çoğunlukla Google mühendisleri için teşhis hata ayıklama bilgileridir. Fesih kararları, adj alanlarına göre verilir. Kısaca, / FS bir ön plan hizmetini belirtir; / FA, bir etkinlik içeren bir ön plan sürecini gösterir. / B, bir arka plan hizmetini belirtir. Sondaki etiket, işlemin öncelik atandığı genel kuralı gösterir. Genellikle adj = alanıyla eşleşmelidir; ancak adj = değeri, bazı durumlarda diğer hizmetler veya etkinliklerle etkin bağlamalar üzerindeki bağlayıcı bayraklar nedeniyle yukarı veya aşağı doğru ayarlanabilir.
Bağlama bayrakları olan bir hataya takıldıysanız, dumpsys satırı şöyle görünecektir:
Proc
Adj alanının değerinin nasıl yanlış bir şekilde "adj = bak" (boş arka plan işlemi) olarak ayarlandığına dikkat edin, bu da kabaca "lütfen beni şimdi sonlandırın, böylece bu anlamsız varoluşu sona erdirebilirim" anlamına gelir. Ayrıca, "adj" ayarını belirlemek için "yer hizmet kuralları kullanıldığını gösteren satırın sonundaki (fg-hizmet) bayrağına dikkat edin. Fg-hizmet kuralları kullanılmasına rağmen, bu işleme bir adj ayarı atandı "bak" ve çok uzun yaşamayacak. Açıkça söylemek gerekirse, bu bir hata.
Dolayısıyla amaç, sürecinizin her zaman "adj = prcp" (veya daha iyi) olmasını sağlamaktır. Ve bu hedefe ulaşmanın yöntemi, öncelik atamasında hatalardan kaçınmayı başarana kadar bağlama bayraklarını ince ayar yapmaktır.
İşte bildiğim hatalar. (1) HERHANGİ bir hizmet veya etkinlik Context.BIND_ABOVE_CLIENT kullanarak hizmete bağlanmışsa, bu bağlama artık etkin olmasa bile adj = ayarının "bak" değerine düşürülmesi riskini alırsınız. Bu, özellikle hizmetler arasında bağlantılarınız varsa geçerlidir. 4.2.1 kaynaklarında açık bir hata. (2) Bir hizmetten hizmete bağlama için kesinlikle BIND_ABOVE_CLIENT kullanmayın. Aktivite-hizmet bağlantıları için de kullanmayın. BIND_ABOVE_CLIENT davranışını uygulamak için kullanılan bayrak, bağlantı bazında değil, işlem bazında ayarlanmış gibi görünüyor, bu nedenle hizmetten hizmete etkin bir etkinlik olmasa bile hizmetten hizmete bağlanmalarıyla hataları tetikler bayrak kümesi ile bağlanma. Süreçte hizmetten hizmete bağlamaları olan birden fazla hizmet olduğunda öncelik belirlemede sorunlar var gibi görünmektedir. Hizmetten hizmete bağlamalarda Context.BIND_WAIVE_PRIORITY (API 14) kullanmak yardımcı görünmektedir. Context.BIND_IMPORTANT, bir Aktiviteden bir hizmete bağlanırken az çok iyi bir fikir gibi görünüyor. Bunu yapmak, Etkinlik ön plandayken, Etkinlik duraklatıldığında veya bittiğinde görünürde herhangi bir zarar vermeden işlem önceliğinizi bir kademe yükseltir.
Ancak genel olarak strateji, sysdump işleminizin doğru önceliği aldığını gösterene kadar bindService bayraklarınızı ayarlamaktır.
Benim amaçlarım için, Context.BIND_AUTO_CREATE kullanarak | Etkinlikten hizmete bağlamaları için Context.BIND_IMPORTANT ve Context.BIND_AUTO_CREATE | Hizmetten hizmete bağlamalar için Context.BIND_WAIVE_PRIORITY doğru olanı yapıyor gibi görünüyor. Kilometreniz farklı olabilir.
Uygulamam oldukça karmaşık: her biri bağımsız olarak ön plan hizmet durumlarını barındırabilen iki arka plan hizmeti ve ayrıca ön plan hizmet durumunu da alabilen üçüncüsü; hizmetlerden ikisi şartlı olarak birbirine bağlanır; üçüncü her zaman birinciye bağlanır. Ek olarak, Aktiviteler ayrı bir süreçte çalışır (animasyonu daha pürüzsüz hale getirir). Faaliyetleri ve Hizmetleri aynı süreçte çalıştırmak herhangi bir fark yaratmıyor gibi görünüyordu.
Atma işlemleri için kuralların uygulanması (ve sysdump dosyalarının içeriğini oluşturmak için kullanılan kaynak kod) çekirdek android dosyasında bulunabilir.
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java.
İyi şans.
Not: İşte Android 5.0 için sysdump dizelerinin yorumu. Onlarla çalışmadım, o yüzden ne yaparsan yap onları. 4'ün "A" veya "S" olmasını ve 5'in "IF" veya "IB" olmasını ve 1'in mümkün olduğunca düşük olmasını istediğinize inanıyorum (muhtemelen 3'ün altında, çünkü yalnızca 3 ön plan hizmet süreci etkin durumda tutuluyor varsayılan yapılandırmada).
Example:
Proc # : prcp F/S/IF trm: 0 31719: neirotech.cerebrum.attention:blePrcs/u0a77 (fg-service)
Format:
Proc # {1}: {2} {3}/{4}/{5} trm: {6} {7}: {8}/{9} ({10}
1: Order in list: lower is less likely to get trimmed.
2: Not sure.
3:
B: Process.THREAD_GROUP_BG_NONINTERACTIVE
F: Process.THREAD_GROUP_DEFAULT
4:
A: Foreground Activity
S: Foreground Service
' ': Other.
5:
-1: procState = "N ";
ActivityManager.PROCESS_STATE_PERSISTENT: procState = "P ";
ActivityManager.PROCESS_STATE_PERSISTENT_UI:procState = "PU";
ActivityManager.PROCESS_STATE_TOP: procState = "T ";
ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: procState = "IF";
ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: procState = "IB";
ActivityManager.PROCESS_STATE_BACKUP:procState = "BU";
ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: procState = "HW";
ActivityManager.PROCESS_STATE_SERVICE: procState = "S ";
ActivityManager.PROCESS_STATE_RECEIVER: procState = "R ";
ActivityManager.PROCESS_STATE_HOME: procState = "HO";
ActivityManager.PROCESS_STATE_LAST_ACTIVITY: procState = "LA";
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: procState = "CA";
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: procState = "Ca";
ActivityManager.PROCESS_STATE_CACHED_EMPTY: procState = "CE";
{6}: trimMemoryLevel
{8} Process ID.
{9} process name
{10} appUid