İleti dizileri sayesinde daha iyi performans

Android'de ileti dizilerini doğru şekilde kullanmak, uygulamanızın performansını artırmanıza yardımcı olabilir. bazı yolları da görmüştük. Bu sayfada, ileti dizileriyle çalışmanın çeşitli yönleri ele alınmaktadır: kullanıcı arayüzü veya ana iş parçacığı ile çalışma; uygulama yaşam döngüsü ile ileti dizisi önceliği; ve platformun iş parçacığının yönetilmesine yardımcı olmak için sağladığı yöntemler gerekir. Bu alanların her birinde, bu sayfada olası tehlikeler ve stratejilere değineceğiz.

Ana iş parçacığı

Kullanıcı, uygulamanızı başlattığında Android, yeni bir Linux oluşturur süreciyle birlikte gösterir. Bu ana ileti dizisinde, UI iş parçacığı olarak da bilinir, gerçekleşen her şeyden ekranda görebilirsiniz. Nasıl çalıştığını anlamak, uygulamanızı ana ileti dizisi geliştirmenizi sağlar.

Dahililer

Ana iş parçacığı çok basit bir tasarıma sahip: Tek işi, alan adını alıp İş parçacığı açısından güvenli bir iş sırasından, uygulaması sonlandırılana kadar yapılan iş blokları. İlgili içeriği oluşturmak için kullanılan bu çalışma bloklarının bazılarını çeşitli kaynaklardan oluşturur. Bu yerler, yaşam döngüsü bilgileriyle ilişkili geri çağırmaları, veya diğer uygulama ya da işlemlerden gelen etkinlikler olarak tanımlar. Ayrıca uygulama blokları kendi başlarına, çerçeveyi kullanmadan açıkça kuyruğa sokabilir.

Neredeyse tüm uygulamanızın yürüttüğü kod bloğu bir etkinlik geri çağırmasına bağlıdır (örneğin, giriş, veya çizim kullanabilirsiniz. Bir şey tetiklendiğinde, etkinliğin gerçekleştiği ileti dizisi etkinliği kendi dışına iter ve ana ileti dizisinin iletisine sıra. Böylece ana iş parçacığı etkinliğe hizmet verebilir.

Bir animasyon veya ekran güncellemesi gerçekleşirken sistem bir animasyon veya ekran güncellemesi her 16 ms’de bir (ekranın çiziminden sorumlu olan) bir iş bloku oluşturmak için 60. saniyede kare/saniye sayısı. Sistemin bu hedefe ulaşması için Kullanıcı Arayüzü/Görünüm hiyerarşisi ana ileti dizisinde güncellenmesi gerekir. Ancak ana ileti dizisinin mesajlaşma sırasında ana iş parçacığının göremeyeceği kadar çok veya çok uzun olan görevler içerir. güncellemeyi yeterince hızlı bir şekilde tamamlarsa uygulama bu çalışmayı bir çalışana ileti dizisi. Ana iş parçacığı, iş bloklarını yürütmeyi 16 ms içinde bitiremezse Kullanıcı, girişte takılma, gecikme veya kullanıcı arayüzünün yanıt verme yeteneğinin zayıf olduğunu fark edebilir. Ana iş parçacığı yaklaşık beş saniye boyunca engellenirse sistem, Uygulama Kullanıcının uygulamayı doğrudan kapatmasına olanak tanıyan Yanıt Vermiyor (ANR) iletişim kutusu.

Çok sayıda veya uzun görevi, ana iş parçacığından engellememeleri için taşıma Kolay oluşturma ve kullanıcı girişine hızlı yanıt verme özellikleri, Bunun için Android'i kullanabilirsiniz.

İleti dizileri ve kullanıcı arayüzü nesne referansları

Tasarım gereği, Android Görünüm nesneleri iş parçacığı açısından güvenli değil. Bir uygulamanın oluşturması, kullanması ve kullanıcı arayüzü nesnelerini yok edin. Bir önceki videoda hatta ana iş parçacığı dışında bir iş parçacığında bir UI nesnesine referansta bulunursanız istisnalar, sessiz hatalar, kilitlenmeler ve tanımlanmamış diğer hatalı davranışlar olabilir.

Referanslarla ilgili sorunlar iki farklı kategoriye ayrılır: açık referanslar hem de örtülü referanslar.

Uygunsuz referanslar

Ana olmayan iş parçacıklarındaki birçok görevin nihai hedefi, kullanıcı arayüzü nesnelerini güncellemektir. Ancak bu iş parçacıklarından biri görünüm hiyerarşisindeki bir nesneye erişirse uygulama kararsızlığı ortaya çıkabilir: Bir çalışan iş parçacığının başka bir iş parçacığının nesneye referansta bulunduğu sırada bu nesneyi sonuçlar tanımlanmadı.

Örneğin, bir kullanıcı arayüzü nesnesine doğrudan referans içeren bir uygulama çalışan iş parçacığı. Çalışan iş parçacığındaki nesne, View; ancak iş tamamlanmadan önce View görünüm hiyerarşisinden kaldırılır. Bu iki işlem aynı anda gerçekleştiğinde referans, View nesnesini bellekte tutar ve nesne üzerindeki özellikleri ayarlar. Ancak, kullanıcı başvuru kaldırıldıktan sonra uygulama nesneyi siler.

Başka bir örnekte, View nesneleri etkinliğe ilişkin referanslar içeriyor sahibi olan kullanıcılara aitti. Eğer ancak etkinliğin son haline geldiğini ancak doğrudan veya dolaylı olarak başvuruda bulunduğu için çöp toplayıcı yürütülmesi bitene kadar işleme devam eder.

Bu senaryo, dizi halinde çalışmanın bulunduğu durumlarda bir soruna bazı etkinlik yaşam döngüsü etkinlikleri (ör. ekran döndürme) gerçekleşirken uçuşun bir kısmını takip edin. Sistem, yayın yapana kadar atık toplama işlemi gerçekleştiremez yardımcı olur. Sonuç olarak, içinde iki Activity nesne olabilir bellekte kalmasını sağlar.

Buna benzer senaryolarda uygulamanızda uygunsuz içerik barındırmamasını öneririz iş parçacıklı iş görevlerindeki kullanıcı arayüzü nesnelerine yapılan referanslar. Bu tür referanslardan kaçınmak, bir projeyi yürütmek için iş parçacığı anlaşmazlığından uzak durmaya çalışacak.

Her durumda, uygulamanız yalnızca ana iş parçacığındaki kullanıcı arayüzü nesnelerini güncellemelidir. Bu birden çok iş parçacığının aşağıdakileri yapmasına izin veren bir görüşme politikası en önemli etkinliği veya en önemli aktiviteyi içeren ana ileti dizisine öğesini kullanın.

Dolaylı referanslar

İş parçacıklı nesnelerde yaygın olarak görülen bir kod tasarımı hatası, aşağıdaki kodu kullanabilirsiniz:

Kotlin

class MainActivity : Activity() {
    // ...
    inner class MyAsyncTask : AsyncTask<Unit, Unit, String>() {
        override fun doInBackground(vararg params: Unit): String {...}
        override fun onPostExecute(result: String) {...}
    }
}

Java

public class MainActivity extends Activity {
  // ...
  public class MyAsyncTask extends AsyncTask<Void, Void, String>   {
    @Override protected String doInBackground(Void... params) {...}
    @Override protected void onPostExecute(String result) {...}
  }
}

Bu snippet'teki kusur, kodun iş parçacığı nesnesini tanımlamasıdır. Bazı etkinliklerin (veya iç sınıfın) statik olmayan iç sınıfı olarak MyAsyncTask ). Bu beyan, ek Activity öğesine örtülü bir referans oluşturuyor kullanır. Sonuç olarak nesne, ileti dizisindeki çalışmaların tamamlanması, referans verilen etkinliğin kaldırılmasında gecikmeye neden olur. Bu gecikme de hafıza üzerinde daha fazla baskı yaratır.

Bu soruna doğrudan bir çözüm, aşırı yüklenen sınıfınızı tanımlamaktır. ya da kendi dosyalarında sınıflandırmayı sağlayan örnekler içerir. Böylece, örtülü referanstır.

Diğer bir çözüm de arka plan görevlerini her zaman iptal edip uygun Activity yaşam döngüsü geri çağırması (ör. onDestroy). Bu yaklaşım, ve hataya açıktır. Genel bir kural olarak, kullanıcı arayüzü dışında karmaşık ve doğrudan inceleyebiliyor. Ayrıca, AsyncTask artık kullanımdan kaldırıldı ve yeni kodda kullanılması önerilmez. Android'de Threading (Android'de Threading) bölümüne göz atın. inceleyin.

İleti dizileri ve uygulama etkinliği yaşam döngüleri

Uygulama yaşam döngüsü, iş parçacıklarının uygulamanızda çalışma şeklini etkileyebilir. Bir ileti dizisinin etkinlik yok sayılır. Bununla birlikte, proje hedefleri ile ileti dizisi önceliklendirme ve bir etkinliğin ön planda mı yoksa arka plan.

Devam eden ileti dizileri

İleti dizileri, bunları oluşturan etkinliklerin ömrü boyunca var olur. Mesaj dizileri yaratılmasından veya ortadan kaldırılmasından bağımsız olarak, kesintisiz olarak devam etmek için gerekli işlemler yapılmadığında, başvuru süreciyle birlikte sonlandırılır. daha etkin uygulama bileşenleri içerir. Bazı durumlarda bu sebat istenen bir durumdur.

Bir etkinliğin dizili iş blokları oluşturduğu bir örneği düşünün. ardından bir çalışan iş parçacığı blokları yürütmeden önce kaldırılır. Uygulama ne gibi bir uygulama söz konusu?

Bloklar artık mevcut olmayan bir kullanıcı arayüzünü güncelleyecekse bunun bir nedeni yoktur. devam etmesi için ekibi teşvik etmek içindir. Örneğin, çalışma olarak kullanıcı bilgilerinin yüklenmesi görünümlerini güncellerseniz ileti dizisine artık gerek kalmaz.

Bunun aksine, çalışma paketlerinin kapsamı dışında kalan Kullanıcı arayüzü. Bu durumda, ileti dizisini sürdürmeniz gerekir. Örneğin, paketler bir görüntüyü indirmek, diskte önbelleğe almak ve ilişkili View nesne algılandı. Nesne artık mevcut olmasa da, indirme ve resmin önbelleğe alınması, kullanıcının sayfaya geri dönmesi ihtimaline karşı etkinliği yok etti.

Tüm iş parçacığı nesneleri için yaşam döngüsü yanıtlarının manuel olarak yönetilmesi karmaşık hale getirebilirsiniz. Bu kuralları doğru şekilde yönetmezseniz uygulamanız ve performans sorunları olduğunu unutmayın. Şunları birleştirme: ViewModel LiveData ile şunları yapabilirsiniz: verileri yükle ve değişiklik olduğunda bildirim alın endişelenmenize gerek yok. ViewModel nesne tek bir çözüm olduğunu düşünebilir. ViewModel'ler, görüntüleme verilerinizi saklamanız için kolay bir yol sunar. ViewModels hakkında daha fazla bilgi için ViewModel rehberini inceleyin ve LiveData, LiveData kılavuzuna bakın. Şu durumda: uygulama mimarisi hakkında daha fazla bilgi edinmek isterseniz Uygulama Mimarisi Kılavuzu.

İleti dizisi önceliği

İşlemler ve Uygulama Yaşam Döngüsü gibi düşünebilirsiniz. kısmen uygulamanın yaşam döngüsünde nerede bulunduğuna bağlıdır. Projeyi oluştururken ve ileti dizilerini yönetmeniz gerekiyorsa, ileti dizilerinin önceliklerini doğru ileti dizilerinin doğru zamanda doğru öncelikleri alması. Çok yüksek bir değere ayarlanırsa iş parçacığınız UI iş parçacığını ve RenderThread'i kesintiye uğratarak uygulamanızın çerçeveleri kullanabilirsiniz. Çok düşük bir değere ayarlanırsa eşzamansız görevlerinizi (ör. daha yavaş olabilir.

Her ileti dizisi oluşturduğunuzda setThreadPriority() Sistemin iş parçacığı planlayıcı, yüksek öncelikli ileti dizilerine öncelik verir ve bunları ve öncelikleri netleştirmeye odaklanmasına yardımcı olur. Genellikle ileti dizileri ön planda grup, toplam yürütme süresinin yaklaşık%95'ini, arka plan grubu yaklaşık %5 olur.

Sistem ayrıca Process sınıf.

Varsayılan olarak sistem, ileti dizisinin önceliğini aynı önceliğe ve gruba ayarlar üyelik satın alabilirsiniz. Ancak uygulamanız, ileti dizisi önceliğini setThreadPriority()

Process sınıfının, en yüksek alaka düzeyine sahip olmasının yanı sıra, uygulamanızın, iş parçacığı önceliklerini belirlemek için kullanabileceği sabit değerler kümesi. Örneğin, THREAD_PRIORITY_DEFAULT. bir ileti dizisinin varsayılan değerini temsil eder. Uygulamanız ileti dizisinin önceliğini şu şekilde ayarlamalıdır: THREAD_PRIORITY_BACKGROUND. .

Uygulamanız THREAD_PRIORITY_LESS_FAVORABLE ile kullanılabilir ve THREAD_PRIORITY_MORE_FAVORABLE sabitleri artımlı şekilde ayarlayabilirsiniz. Şunlar için: ileti dizilerinin önceliklerini Şu değerde THREAD_PRIORITY sabit değer: Process sınıfı.

Daha fazla bilgi için daha fazla bilgi edinmek için aşağıdaki referans belgelerine Thread ve Process sınıf.

İleti dizisi oluşturma için yardımcı sınıfları

Birincil dili olarak Kotlin'i kullanan geliştiriciler eş yordamları kullanmalarını öneririz. Eş yordamlar, geri çağırma olmadan eşzamansız kod yazma ve kapsam oluşturma, iptal ve hata işleme için yapılandırılmış eşzamanlılık sağlar.

Bu çerçeve, aynı Java sınıflarını ve temel öğelerini sağlayarak ileti dizisi (ör. Thread, Runnable) ve Executors sınıf varsa ve HandlerThread gibi başka uzantılar da dahil edilir. Daha fazla bilgi için Android'de İleti Dizisi Oluşturma konusuna bakın.

HandlerThread sınıfı

İşleyici iş parçacığı, bir kuyruktaki işleri alan ve çalışan bir iş parçacığıdır. tıklayın.

Cihazınızdan önizleme kareleri almayla ilgili sık karşılaşılan Camera nesne algılandı. Kamera önizleme çerçeveleri için kaydolduğunuzda, bu çerçeveleri onPreviewFrame(). geri çağırması, çağrının yapıldığı etkinlik ileti dizisinde çağrılır. Bu büyük pikselle ilgilenme görevi, kullanıcı arayüzü iş parçacığında geri çağırma oluşturma ve etkinlik işleme çalışmalarını etkileyebilir.

Bu örnekte, uygulamanız Camera.open() komutunu bir kullanıcıya işleyici iş parçacığındaki iş bloğu, ilişkili onPreviewFrame() geri arama kullanıcı arayüzü iş parçacığı yerine işleyici iş parçacığına çıkar. Uzun süreli çalışma için çalıştığında, bu sizin için daha iyi bir çözüm olabilir.

Uygulamanız HandlerThread kullanarak bir ileti dizisi oluşturduğunda ileti dizisinin önceliklerini belirleyebilirsiniz. Unutmayın, CPU'lar yalnızca az sayıda iş parçacığının paralel olarak işlenmesini sağlar. Öncelikleri belirlemek, sistem bu çalışmayı planlayacak doğru yolları bilir: dikkat çekmek için mücadele ediyor.

ThreadPoolExecutor sınıfı

Bazı iş türleri büyük oranda paralel olarak incelenebilir, ya da dağıtılmış görevleri de göz önünde bulundurmalısınız. Örneğin, bu tür bir görev her bir filtre için 8 megapiksellik bir resimden oluşan 8x8 blok. İş paketlerinin hacminin oldukça yüksek olması oluşturuyorsa HandlerThread, kullanılması uygun bir sınıf değildir.

ThreadPoolExecutor, şu konulara yardımcı olacak bir sınıftır: kolaylaştıracaktır. Bu sınıf, bir ileti dizisi grubunun, ve çalışmanın bu ileti dizileri arasında nasıl dağıtıldığını yönetir. İş yükü arttıkça veya azaldıkça sınıf hızlanır ya da daha fazla ileti dizisini kaldırır. iş yüküne uyum sağlamasıdır.

Bu sınıf, uygulamanızın optimum sayıda iş parçacığı üretmesine de yardımcı olur. Zaman bir ThreadPoolExecutor oluşturur nesne olursa, uygulama bir minimum ve maksimum iş parçacığı sayısı. Projeye verilen iş yükü olarak ThreadPoolExecutor artıyor, ve en iyi iş parçacıklarının sayısını hesaplamak için ve bekleyen iş miktarını göz önünde bulundurmanız gerekir. Bunları temel alarak nasıl faktör olacağına ThreadPoolExecutor karar verir. ileti dizileri her zaman yayında olmalıdır.

Kaç ileti dizisi oluşturmanız gerekir?

Yazılım düzeyinde olsa da, kodunuz, yüzlerce Aksi takdirde performans sorunlarıyla karşılaşabilirsiniz. Uygulamanız sınırlı CPU'ya sahip arka plan hizmetleri, oluşturucu, ses motoru ve birçok işlevi vardır. CPU'lar aslında yalnızca az sayıda iş parçacığını paralel olarak işleyebilme; üzerinde çalışan her şey öncelik ve planlama sorunlarına değineceğiz. Bu nedenle, yalnızca ihtiyaç duyduğunuz kadar iş parçacığı oluşturabilirsiniz.

Pratikte bunun için birçok değişken var ancak bir değer seçme (örneğin, ilk olarak 4) ve onu Systrace: kesinlikle güçlü bir stratejidir. Cevaplarınızı en üst düzeye çıkarmak için deneme yanılma yöntemini sorun yaşamadan kullanabileceğiniz minimum ileti dizisi sayısı.

Kaç ileti dizisine sahip olacağınıza karar verirken dikkate almanız gereken diğer bir nokta da bunlar hafızada yer kaplar. Her iş parçacığının en az 64 k bellek maliyeti vardır. Bu, bir cihazda yüklü olan birçok uygulama arasında, özellikle de önemli ölçüde büyüdüğü durumlar için geçerlidir.

Birçok sistem işlemi ve üçüncü taraf kitaplığı genellikle kendi ileti dizileri. Uygulamanız mevcut bir ileti dizisi havuzunu yeniden kullanabiliyorsa bu yeniden kullanım yöntemi yardımcı olabilir performansı artırır.