Kullanıcı arayüzü oluşturma, uygulamanızdan bir kare oluşturma ve bu kareyi ekranda görüntüleme işlemidir. Kullanıcıların uygulamanızla sorunsuz bir şekilde etkileşimde bulunmasını sağlamak için uygulamanızın, saniyede 60 kareye (fps) ulaşmak için kareleri 16 ms.den kısa bir sürede oluşturması gerekir. 60 fps'nin neden tercih edildiğini anlamak için Android Performans Kalıpları: Neden 60 fps? bölümüne bakın. 90 fps'ye ulaşmaya çalışıyorsanız bu pencere 11 ms'ye düşer ve 120 fps için 8 ms.
Bu pencereyi 1 ms. aşmanız karenin 1 ms. geç görüntülendiği anlamına gelmez ancak Choreographer
kareyi tamamen bırakır. Uygulamanızda kullanıcı arayüzü oluşturma yavaşsa sistem, kareleri atlamaya zorlanır ve kullanıcı uygulamanızda takılma olduğunu algılar. Buna jank adı verilir. Bu sayfada, jank sorununun nasıl teşhis edilip düzeltileceği gösterilmektedir.
View
sistemini kullanmayan oyunlar geliştiriyorsanız
Choreographer
atlanırsınız. Bu durumda Frame Pacing Kitaplığı, OpenGL ve Vulkan oyunlarının Android'de sorunsuz oluşturma ve doğru kare hızı elde etmesine yardımcı olur.
Android, uygulama kalitesini iyileştirmeye yardımcı olmak için uygulamanızı jank hatası olup olmadığını belirlemek üzere otomatik olarak izler ve bilgileri Android vitals kontrol panelinde gösterir. Verilerin nasıl toplandığı hakkında bilgi edinmek için Uygulamanızın teknik kalitesini Android vitals ile izleme konusuna bakın.
Arızayı tanımlama
Uygulamanızda duraklamaya neden olan kodu bulmak zor olabilir. Bu bölümde, jank'ı tanımlamak için kullanabileceğiniz üç yöntem açıklanmaktadır:
Görsel inceleme, uygulamanızdaki tüm kullanım alanlarını birkaç dakika içinde incelemenize olanak tanır ancak Systrace kadar ayrıntılı bilgi sağlamaz. Systrace daha fazla ayrıntı sunar ancak Systrace'i uygulamanızdaki tüm kullanım alanları için çalıştırırsanız analiz edilmesi zor olabilecek çok fazla veriyle karşılaşabilirsiniz. Hem görsel inceleme hem de Systrace, yerel cihazınızdaki olumsuzlukları algılar. Yerel cihazlarda olumsuzlukları yeniden oluşturamıyorsanız sahada çalışan cihazlarda uygulamanızın belirli bölümlerini ölçmek için özel performans izleme oluşturabilirsiniz.
Görsel inceleme
Görsel denetim, jank sorununa neden olan kullanım alanlarını belirlemenize yardımcı olur. Görsel bir inceleme gerçekleştirmek için uygulamanızı açın ve manuel olarak uygulamanızın farklı bölümlerini gözden geçirin ve kullanıcı arayüzünüzde olumsuzluk olup olmadığını kontrol edin.
Görsel inceleme yapmayla ilgili bazı ipuçlarını aşağıda bulabilirsiniz:
- Uygulamanızın sürümünü veya en azından hata ayıklaması mümkün olmayan bir sürümünü çalıştırın. ART çalışma zamanı, hata ayıklama özelliklerini desteklemek için bazı önemli optimizasyonu devre dışı bırakır. Bu nedenle, kullanıcının gördüğüne benzer bir şey görüntülediğinizden emin olun.
- Profil GPU Oluşturmayı Etkinleştir. Profil GPU Oluşturma, kare başına 16 ms. değerine göre kullanıcı arayüzü penceresinin karelerini oluşturmak için gereken sürenin görsel bir temsilini sağlayan ekranda çubuklar görüntüler. Her çubuk, oluşturma ardışık düzenindeki bir aşamayla eşleşen renkli bileşenlere sahiptir, böylece hangi bölümün en uzun süre aldığını görebilirsiniz. Örneğin, çerçeve girişle ilgili çok fazla zaman harcıyorsa kullanıcı girişini işleyen uygulama kodunuza bakın.
RecyclerView
gibi yaygın olumsuzluk kaynakları olan bileşenleri çalıştırın.- Uygulamayı sıfırdan başlangıçta başlatın.
- Sorunu şiddetlendirmek için uygulamanızı daha yavaş bir cihazda çalıştırın.
Düşük duraklamaya neden olan kullanım alanları bulduğunuzda, uygulamanızda duraklamaya neyin neden olduğu konusunda fikir sahibi olabilirsiniz. Daha fazla bilgiye ihtiyacınız varsa Systrace'i kullanarak sorunun nedenini daha ayrıntılı inceleyebilirsiniz.
Systrace
Systrace, tüm cihazın ne yaptığını gösteren bir araç olsa da uygulamanızdaki olumsuzlukları tanımlamak açısından faydalı olabilir. Systrace'in sistem yükü minimum düzeyde olduğundan enstrümantasyon sırasında gerçekçi bir duraklama deneyimi yaşayabilirsiniz.
Cihazınızda kötü kullanım alanı gerçekleştirirken Systrace ile bir iz kaydedin. Systrace'in nasıl kullanılacağıyla ilgili talimatlar için Komut satırında sistem izlemesi yakalama başlıklı makaleyi inceleyin. Systrace, süreçler ve iş parçacıklarına bölünür. Uygulamanızın Systrace'teki sürecini bulun. Bu süreç Şekil 1'e benzer.
Şekil 1'deki Systrace örneği, jank'ı tanımlamak için aşağıdaki bilgileri içerir:
- Systrace, uzun oluşturma sürelerini vurgulamak için her karenin ne zaman çizildiğini gösterir ve her kareye renk kodları verir. Bu sayede, olumsuz kareleri ayrı ayrı görsel incelemeye kıyasla daha doğru bir şekilde bulabilirsiniz. Daha fazla bilgi için Kullanıcı arayüzü çerçevelerini ve uyarıları inceleme bölümüne bakın.
- Systrace, uygulamanızdaki sorunları tespit edip uyarıları hem bağımsız çerçevelerde hem de uyarılar panelinde gösterir. Uyarıdaki talimatları uygulamanız önerilir.
- Android çerçevesinin ve kitaplıkların bazı bölümleri (ör.
RecyclerView
) iz işaretçileri içerir. Dolayısıyla, systrace zaman çizelgesi, bu yöntemlerin kullanıcı arayüzü iş parçacığında ne zaman yürütüldüğünü ve yürütülmelerinin ne kadar sürdüğünü gösterir.
Systrace çıktısına baktıktan sonra, uygulamanızda jank'a neden olduğundan şüphelendiğiniz yöntemler olabilir. Örneğin, zaman çizelgesi, yavaş karenin RecyclerView
uzun sürmesinden kaynaklandığını gösteriyorsa ilgili koda özel izleme etkinlikleri ekleyebilir ve daha fazla bilgi için Systrace'i yeniden çalıştırabilirsiniz. Yeni Systrace'te zaman çizelgesi, uygulamanızın yöntemlerinin ne zaman çağrıldığını ve uygulanmalarının ne kadar sürdüğünü gösterir.
Systrace, kullanıcı arayüzü iş parçacığı çalışmasının neden uzun sürdüğü hakkında ayrıntılı bilgi göstermiyorsa örneklenmiş veya araçlı bir yöntem izlemesi kaydetmek için Android CPU Profiler'ı kullanın. Yöntem izleri, yoğun ek yük nedeniyle yanlış pozitif olumsuzluklar ürettikleri ve iş parçacıklarının çalışıp çalışmadığını veya engellendiğini göremediği için jank'ı tanımlamak için uygun değildir. Ancak yöntem izleme, uygulamanızda en çok zaman alan yöntemleri belirlemenize yardımcı olabilir. Bu yöntemleri belirledikten sonra İzleme işaretçileri ekleyin ve bu yöntemlerin jank'a neden olup olmadığını görmek için Systrace'i yeniden çalıştırın.
Daha fazla bilgi için Systrace'i Anlama bölümünü inceleyin.
Özel performans izleme
Yerel bir cihazda duraklama oluşturamıyorsanız alandaki cihazlarda olumsuzlukların kaynağını belirlemeye yardımcı olması için uygulamanızda özel performans izleme derleyebilirsiniz.
Bu amaçla, FrameMetricsAggregator
ile uygulamanızın belirli bölümlerinden kare oluşturma sürelerini toplayın ve Firebase Performance Monitoring'i kullanarak verileri kaydedip analiz edin.
Daha fazla bilgi edinmek için Android için Performance Monitoring'i kullanmaya başlama bölümüne bakın.
Donmuş kare
Donmuş kareler, oluşturulması 700 ms'den uzun süren kullanıcı arayüzü kareleridir. Uygulamanız, kare oluşturulurken neredeyse bir saniye boyunca takılı kaldı ve kullanıcı girişine yanıt vermediğinden bu bir sorun teşkil eder. Uygulamanın sorunsuz bir kullanıcı arayüzü için 16 ms. içinde bir kareyi oluşturacak şekilde optimize edilmesini öneririz. Ancak uygulamanız başlatılırken veya farklı bir ekrana geçiş yapılırken ilk karenin çiziminin 16 ms.den uzun sürmesi normaldir. Bunun nedeni, uygulamanızın görünümleri şişirmesi, ekranı yerleştirmesi ve ilk çizimi tamamen sıfırdan yapmasıdır. Bu nedenle Android, donmuş kareleri yavaş oluşturma işleminden ayrı olarak izler. Uygulamanızdaki hiçbir karenin oluşturulması 700 ms'den uzun sürmemelidir.
Android, uygulama kalitesini iyileştirmenize yardımcı olmak amacıyla uygulamanızı donmuş karelere karşı otomatik olarak izler ve bilgileri Android vitals kontrol panelinde gösterir. Verilerin nasıl toplandığı hakkında bilgi edinmek için Uygulamanızın teknik kalitesini Android vitals ile izleme makalesine göz atın.
Donmuş kareler, yavaş oluşturmanın aşırı bir biçimi olduğundan sorunu teşhis etme ve düzeltme prosedürü aynıdır.
İzleme olumsuzluğu
Perfetto'daki FrameTimeline, yavaş veya donmuş kareleri izlemeye yardımcı olabilir.
Yavaş kareler, donmuş kareler ve ANR'ler arasındaki ilişki
Yavaş kareler, donmuş kareler ve ANR'ler, uygulamanızın karşılaşabileceği farklı duraklama türleridir. Farkı anlamak için aşağıdaki tabloya bakın.
Yavaş kare sayısı | Donmuş kare | ANR'ler | |
---|---|---|---|
Oluşturma süresi | 16 ms ile 700 ms. arasında | 700 ms ile 5 sn. arasında | 5 saniyeden uzun |
Kullanıcı etki alanı görünür |
|
|
|
Yavaş kareleri ve donmuş kareleri ayrı ayrı izle
Uygulama başlatılırken veya farklı bir ekrana geçiş yapılırken ilk karenin çiziminin 16 ms.den uzun sürmesi normaldir. Bunun nedeni, uygulamanın görünümleri genişletmesi, ekranı yerleştirmesi ve ilk çizimi sıfırdan yapmasıdır.
Jank'ı önceliklendirme ve çözümlemeyle ilgili en iyi uygulamalar
Uygulamanızda yaşanan jank sorununu çözmeye çalışırken aşağıdaki en iyi uygulamaları aklınızda bulundurun:
- En kolay yeniden oluşturulabilir jank örneklerini belirleyin ve çözün.
- ANR'lere öncelik verin. Yavaş veya donmuş kareler uygulamayı yavaş görünmesine neden olsa da ANR'ler uygulamanın yanıt vermemesine neden olur.
- Yavaş oluşturmayı yeniden üretmek zordur ancak 700 ms. donmuş kareleri kapatarak başlayabilirsiniz. Bu durum, en sık uygulama başlatılırken veya ekranları değiştirirken görülür.
Sorun düzeltme
duraklamayı düzeltmek için hangi karelerin 16 ms. içinde tamamlanmadığını inceleyin ve neyin yanlış olduğunu arayın. Record View#draw
veya Layout
uygulamasının bazı karelerde anormal derecede uzun süre kullanıp kullanmadığını kontrol edin. Bu sorunlar ve diğer sorunlar için Yaygın duraklama kaynakları bölümünü inceleyin.
Düşüşü önlemek için uzun süreli görevleri kullanıcı arayüzü iş parçacığının dışında eşzamansız olarak çalıştırın. Kodunuzun hangi iş parçacığında çalıştığına her zaman dikkat edin ve önemsiz görevleri ana iş parçacığına yayınlarken dikkatli olun.
Uygulamanız için merkezi kaydırma listesi gibi karmaşık ve önemli bir birincil kullanıcı arayüzünüz varsa yavaş oluşturma sürelerini otomatik olarak algılayan ve regresyonları önlemek için testleri sık sık çalıştırabilen yazım araçları testlerini kullanmayı düşünebilirsiniz.
Yaygın olumsuzluk kaynakları
Aşağıdaki bölümlerde, View
sistemini kullanan uygulamalardaki yaygın duraklama kaynakları ve bunları ortadan kaldırmaya yönelik en iyi uygulamalar açıklanmaktadır. Jetpack Compose ile ilgili performans sorunlarını düzeltme hakkında bilgi için Jetpack Compose performansı başlıklı makaleyi inceleyin.
Kaydırılabilir listeler
ListView
ve özellikle RecyclerView
, duraklamaya en müsait olan karmaşık kaydırma listeleri için yaygın olarak kullanılır. Her ikisi de Systrace işaretçileri içerir. Böylece, Systrace'i kullanarak uygulamanızda olumsuz etkiye neden olup olmadıklarını görebilirsiniz. RecyclerView
içindeki iz bölümlerini ve eklediğiniz tüm izleme işaretçilerini almak için -a
<your-package-name>
komut satırı bağımsız değişkenini iletin. Varsa Systrace çıktısında oluşturulan uyarılarla ilgili talimatları uygulayın. Systrace'in içinde, RecyclerView
tarafından takip edilen bölümleri tıklayarak RecyclerView
tarafından yapılan çalışmaların açıklamasını görebilirsiniz.
RecyclerView: informDataSetChanged()
RecyclerView
içindeki her öğenin geri ibaret olduğunu ve dolayısıyla bir karede yeniden düzenlenip yeniden çizildiğini görürseniz küçük güncellemeler için notifyDataSetChanged()
veya setAdapter(Adapter)
ya da swapAdapter(Adapter,
boolean)
çağrılarını almadığınızdan emin olun. Bu yöntemler, tüm liste içeriğinde değişiklikler yapıldığına işaret eder ve Systrace'te RV FullIn Invalid olarak görünür. Bunun yerine, içerik değiştirildiğinde veya eklendiğinde minimum düzeyde güncelleme oluşturmak için SortedList
veya DiffUtil
kullanın.
Örneğin, bir sunucudan haber içeriği listesinin yeni bir sürümünü alan bir uygulamayı düşünün. Bu bilgileri Bağdaştırıcıya gönderdiğinizde, aşağıdaki örnekte gösterildiği gibi notifyDataSetChanged()
yöntemini çağırabilirsiniz:
Kotlin
fun onNewDataArrived(news: List<News>) { myAdapter.news = news myAdapter.notifyDataSetChanged() }
Java
void onNewDataArrived(List<News> news) { myAdapter.setNews(news); myAdapter.notifyDataSetChanged(); }
Bunun olumsuz tarafı, üste bir öğenin eklenmesi gibi önemsiz bir değişiklik olursa RecyclerView
bundan haberdar olmaz. Bu nedenle, önbelleğe alınan öğenin durumunun tamamını bırakması gerektiği için her şeyi yeniden bağlaması gerekir.
Sizin için minimum güncellemeleri hesaplayıp gönderen DiffUtil
kullanmanızı öneririz:
Kotlin
fun onNewDataArrived(news: List<News>) { val oldNews = myAdapter.items val result = DiffUtil.calculateDiff(MyCallback(oldNews, news)) myAdapter.news = news result.dispatchUpdatesTo(myAdapter) }
Java
void onNewDataArrived(List<News> news) { List<News> oldNews = myAdapter.getItems(); DiffResult result = DiffUtil.calculateDiff(new MyCallback(oldNews, news)); myAdapter.setNews(news); result.dispatchUpdatesTo(myAdapter); }
DiffUtil
uygulamasına, listelerinizi nasıl denetleyeceğini bildirmek için MyCallback
Callback
uygulaması olarak tanımlayın.
RecyclerView: İç içe RecyclerViews
Birden fazla RecyclerView
örneğinin, özellikle yatay olarak kaydırılan listelerin dikey bir listesiyle iç içe yerleştirilmesi yaygın bir durumdur. Bunun bir örneği, Play Store ana
sayfasındaki uygulamaların ızgaralarıdır. Bu yaklaşım çok işe yarıyor olabilir ama
birbirinden çok farklı görüşler dolaşıyor.
Sayfayı aşağı ilk kaydırdığınızda şişen çok fazla iç öğenin olduğunu görürseniz
RecyclerView.RecycledViewPool
iç (yatay) RecyclerView
örnekleri arasında paylaşımda bulunup bulunmadığınızı kontrol edebilirsiniz. Varsayılan olarak her RecyclerView
kendi öğe havuzuna sahiptir. Ancak, ekranda aynı anda bir düzine itemViews
olması durumunda, tüm satırlar benzer görünüm türlerini gösteriyorsa itemViews
farklı yatay listeler tarafından paylaşılamadığında bu sorun ortaya çıkar.
Kotlin
class OuterAdapter : RecyclerView.Adapter<OuterAdapter.ViewHolder>() { ... override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { // Inflate inner item, find innerRecyclerView by ID. val innerLLM = LinearLayoutManager(parent.context, LinearLayoutManager.HORIZONTAL, false) innerRv.apply { layoutManager = innerLLM recycledViewPool = sharedPool } return OuterAdapter.ViewHolder(innerRv) } ...
Java
class OuterAdapter extends RecyclerView.Adapter<OuterAdapter.ViewHolder> { RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool(); ... @Override public void onCreateViewHolder(ViewGroup parent, int viewType) { // Inflate inner item, find innerRecyclerView by ID. LinearLayoutManager innerLLM = new LinearLayoutManager(parent.getContext(), LinearLayoutManager.HORIZONTAL); innerRv.setLayoutManager(innerLLM); innerRv.setRecycledViewPool(sharedPool); return new OuterAdapter.ViewHolder(innerRv); } ...
Daha fazla optimizasyon yapmak isterseniz dahili RecyclerView
öğesinin LinearLayoutManager
öğesinde setInitialPrefetchItemCount(int)
'i de çağırabilirsiniz. Örneğin, bir satırda her zaman 3,5 öğe görünüyorsa innerLLM.setInitialItemPrefetchCount(4)
numaralı telefonu arayın. Bu, RecyclerView
'e, ekranda yatay bir satır gelmek üzereyken kullanıcı arayüzü iş parçacığında boş zaman varsa içindeki öğeleri önceden getirmeyi denemesi gerektiğini belirtir.
RecyclerView: Çok fazla para iadesi veya Oluşturma işlemi çok uzun sürüyor
Çoğu durumda, RecyclerView
içindeki önceden getirme özelliği, kullanıcı arayüzü iş parçacığı boştayken işleri önceden yaparak enflasyon maliyetine geçici bir çözüm bulmaya yardımcı olabilir.
RV Ön Getirme etiketli bir bölümde değil de bir kare sırasında şişirme görüyorsanız desteklenen bir cihazda test ettiğinizden ve Destek Kitaplığı'nın son sürümünü kullandığınızdan emin olun.
Önceden getirme, yalnızca Android 5.0 API Düzeyi 21 ve sonraki sürümlerde desteklenir.
Ekranda yeni öğeler gösterilirken sık sık enflasyonun olumsuzluğa neden olduğunu görüyorsanız ihtiyacınızdan daha fazla görüntüleme türünüz olmadığını doğrulayın. Bir RecyclerView
öğesinin içeriğinde ne kadar az görüntüleme türü olursa, ekranda yeni öğe türleri göründüğünde o kadar az enflasyon yapılması gerekir. Mümkünse, uygun olduğu durumlarda görünüm türlerini birleştirin. Türler arasında yalnızca bir simge, renk veya metin parçası değişirse bu değişikliği bağlama anında yapabilir ve aynı zamanda uygulamanızın bellek ayak izini azaltacak şekilde şişirmeyi önleyebilirsiniz.
Görüntüleme türleriniz iyi görünüyorsa enflasyonunuzun maliyetini azaltmayı deneyin.
Gereksiz container ve yapısal görünümleri azaltmak işe yarayabilir. Yapısal görünümleri azaltmaya yardımcı olabilecek ConstraintLayout
ile itemViews
oluşturmayı düşünün.
Performansı daha da optimize etmek istiyorsanız, öğe hiyerarşileriniz basitse ve karmaşık tema oluşturma ile stil özelliklerine ihtiyacınız yoksa kurucuları kendiniz çağırabilirsiniz. Ancak XML'in basitliğini ve özelliklerini kaybetmek için genellikle buna değmez.
RecyclerView: Bağlama işlemi çok uzun sürüyor
Bağlama (yani onBindViewHolder(VH,
int)
) basit olmalı ve en karmaşık öğeler hariç her şey için bir milisaniyeden kısa sürmelidir. Bağdaştırıcınızın dahili öğe verilerinden düz eski Java nesnesi (POJO) öğelerini almalı ve ViewHolder
içindeki görünümlerde çağrı ayarlayıcıları almalıdır. RV OnBindView çok uzun sürüyorsa bağlama kodunuzda minimum düzeyde işlem yaptığınızı doğrulayın.
Bağdaştırıcınızda veri tutmak için temel POJO nesnelerini kullanıyorsanız Veri Bağlama Kitaplığı'nı kullanarak bağlama kodunu onBindViewHolder
bölümüne yazmaktan tamamen kaçınabilirsiniz.
RecyclerView veya ListView: Düzen veya çizim çok uzun sürüyor
Çizim ve düzen ile ilgili sorunlar için Düzen performansı ve Oluşturma performansı bölümlerine bakın.
ListView: Enflasyon
Dikkatli olmazsanız ListView
uygulamasında geri dönüşümü yanlışlıkla devre dışı bırakabilirsiniz. Bir öğe ekranda her gösterildiğinde inflasyon görüyorsanız Adapter.getView()
uygulamanızda keyif verici bir durum bulunduğundan, yeniden bağlandığından ve convertView
parametresini döndürdüğünden emin olun. getView()
uygulamanızın değeri sürekli olarak şişirse uygulamanız ListView
içinde geri dönüşümün avantajlarından yararlanamaz. getView()
yapısı neredeyse her zaman aşağıdaki uygulamaya benzer olmalıdır:
Kotlin
fun getView(position: Int, convertView: View?, parent: ViewGroup): View { return (convertView ?: layoutInflater.inflate(R.layout.my_layout, parent, false)).apply { // Bind content from position to convertView. } }
Java
View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { // Only inflate if no convertView passed. convertView = layoutInflater.inflate(R.layout.my_layout, parent, false) } // Bind content from position to convertView. return convertView; }
Düzen performansı
Systrace, Choreographer#doFrame
Düzen segmentinin çok fazla veya çok sık çalıştığını gösteriyorsa bu, düzen performansı sorunlarıyla karşılaşdığınız anlamına gelir. Uygulamanızın düzen performansı, görünüm hiyerarşisinin hangi bölümünde değişen düzen parametreleri veya girişlerine sahip olduğuna bağlıdır.
Düzen performansı: Maliyet
Segmentler birkaç milisaniyeden uzunsa RelativeLayouts
veya weighted-LinearLayouts
için en kötü iç içe yerleştirme performansından etkileniyor olabilirsiniz. Bu düzenlerin her biri, alt öğeleri için birden fazla ölçüm ve düzen geçişini tetikleyebilir. Bu nedenle, bunların iç içe yerleştirilmesi, iç içe yerleştirme derinliğinde O(n^2)
davranışına yol açabilir.
Hiyerarşinin en düşük yaprak düğümleri hariç tümünde RelativeLayout
özelliğinden veya LinearLayout
öğesinin ağırlık özelliğinden kaçınmayı deneyin. Bunu yapmanın yolları aşağıda açıklanmıştır:
- Yapısal görünümlerinizi yeniden düzenleyin.
- Özel düzen mantığı tanımlayın. Belirli bir örnek için Düzen hiyerarşilerini optimize etme bölümüne bakın. Performans sıkıntısı yaşamadan benzer özellikler sunan
ConstraintLayout
sürümüne geçmeyi deneyebilirsiniz.
Düzen performansı: Sıklık
Düzenin, yeni içerik ekranda göründüğünde (ör. RecyclerView
ürününde yeni bir öğe görünüme kaydırıldığında) gerçekleşmesi beklenir. Her karede kayda değer bir düzen gerçekleşiyorsa düzeni canlandırıyor olabilirsiniz. Bu da muhtemelen karelerin atlanmasına yol açabilir.
Genellikle animasyonlar aşağıdaki gibi View
çizim özelliklerinde çalıştırılmalıdır:
Bu özelliklerin tümünü, dolgu veya marjlar gibi düzen özelliklerinden çok daha ucuza değiştirebilirsiniz. Genellikle, bir invalidate()
öğesini tetikleyen ve ardından sonraki karede draw(Canvas)
gelen bir setter çağırarak bir görünümün çizim özelliklerini değiştirmek de çok daha ucuzdur. Bu işlem, geçersiz kılınan ve genellikle düzenden çok daha ucuz olan görünüm için çizim işlemlerini tekrar kaydeder.
Oluşturma performansı
Android kullanıcı arayüzü iki aşamada çalışır:
- Kullanıcı arayüzü iş parçacığında Record View#draw öğesini, geçersiz kılınan her görünümde
draw(Canvas)
çalıştırır ve özel görünümlere veya kodunuza çağrılar çağırabilir. RenderThread
üzerindeki DrawFrame öğesi, yerelRenderThread
üzerinde çalışır ancak Record View#draw aşamasında oluşturulan çalışmaya dayalı olarak çalışır.
Oluşturma performansı: UI Thread
Record View#draw uzun zaman alıyorsa kullanıcı arayüzü iş parçacığına bit eşlemin boyanması yaygın bir durumdur. Bit eşlem boyama işlemi CPU oluşturmayı kullanır. Dolayısıyla mümkünse bunu yapmaktan kaçının. Sorunun bu olup olmadığını görmek için Android CPU Profiler ile yöntem izlemeyi kullanabilirsiniz.
Bir uygulama, görüntülemeden önce bit eşlemi süslemek istediğinde bit eşlem boyama işlemi genellikle yuvarlatılmış köşeler eklemek gibi bir süsleme olarak yapılır:
Kotlin
val paint = Paint().apply { isAntiAlias = true } Canvas(roundedOutputBitmap).apply { // Draw a round rect to define the shape: drawRoundRect( 0f, 0f, roundedOutputBitmap.width.toFloat(), roundedOutputBitmap.height.toFloat(), 20f, 20f, paint ) paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.MULTIPLY) // Multiply content on top to make it rounded. drawBitmap(sourceBitmap, 0f, 0f, paint) setBitmap(null) // Now roundedOutputBitmap has sourceBitmap inside, but as a circle. }
Java
Canvas bitmapCanvas = new Canvas(roundedOutputBitmap); Paint paint = new Paint(); paint.setAntiAlias(true); // Draw a round rect to define the shape: bitmapCanvas.drawRoundRect(0, 0, roundedOutputBitmap.getWidth(), roundedOutputBitmap.getHeight(), 20, 20, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); // Multiply content on top to make it rounded. bitmapCanvas.drawBitmap(sourceBitmap, 0, 0, paint); bitmapCanvas.setBitmap(null); // Now roundedOutputBitmap has sourceBitmap inside, but as a circle.
Kullanıcı arayüzü iş parçacığında bu tür bir çalışma yapıyorsanız bunun yerine arka plandaki kod çözme iş parçacığında bunu yapabilirsiniz. Önceki örnekte olduğu gibi bazı durumlarda, işi çekim zamanında bile yapabilirsiniz. Dolayısıyla, Drawable
veya View
kodunuz aşağıdaki gibi görünüyorsa:
Kotlin
fun setBitmap(bitmap: Bitmap) { mBitmap = bitmap invalidate() } override fun onDraw(canvas: Canvas) { canvas.drawBitmap(mBitmap, null, paint) }
Java
void setBitmap(Bitmap bitmap) { mBitmap = bitmap; invalidate(); } void onDraw(Canvas canvas) { canvas.drawBitmap(mBitmap, null, paint); }
Bunu aşağıdaki şekilde değiştirebilirsiniz:
Kotlin
fun setBitmap(bitmap: Bitmap) { shaderPaint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) invalidate() } override fun onDraw(canvas: Canvas) { canvas.drawRoundRect(0f, 0f, width, height, 20f, 20f, shaderPaint) }
Java
void setBitmap(Bitmap bitmap) { shaderPaint.setShader( new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP)); invalidate(); } void onDraw(Canvas canvas) { canvas.drawRoundRect(0, 0, width, height, 20, 20, shaderPaint); }
Bit eşlemin üzerine bir renk geçişi çizme ve ColorMatrixColorFilter
ile resim filtreleme gibi arka plan koruması için de bunu yapabilirsiniz. Bit eşlemleri değiştirmenin iki yaygın işlemi daha vardır.
Başka bir nedenle (muhtemelen önbellek olarak kullandığınız) bit eşlem üzerinde çizim yapıyorsanız doğrudan View
veya Drawable
cihazınıza aktarılan donanım hızlandırmalı Canvas
öğesine çizim yapmayı deneyin. Gerekirse karmaşık oluşturma çıkışını önbelleğe alıp GPU oluşturma özelliğinden yararlanmak için LAYER_TYPE_HARDWARE
ile setLayerType()
çağırmayı da düşünebilirsiniz.
Oluşturma performansı: RenderThread
Bazı Canvas
işlemleri düşük maliyetli olsa da RenderThread
üzerinde pahalı hesaplamaları tetikler. Systrace genellikle bunları uyarır.
Büyük Yolların Animasyonu
View
'a iletilen donanım hızlandırmalı Canvas
öğesinde Canvas.drawPath()
çağrıldığında Android, bu yolları önce CPU'da çizer ve GPU'ya yükler.
Büyük yollarınız varsa önbelleğe alınıp verimli bir şekilde çizilebilmeleri için bunları kareler arasında düzenlemekten kaçının.
drawPoints()
,
drawLines()
ve drawRect/Circle/Oval/RoundRect()
, daha fazla çizim çağrısı kullansanız bile daha verimlidir ve kullanımı daha iyidir.
Canvas.clipPath
clipPath(Path)
, pahalı kırpma davranışını tetikler ve genellikle bundan kaçınılmalıdır. Mümkün olduğunda, dikdörtgen olmayan boyutlara kırpmak yerine şekil çizmeyi tercih edin. Daha iyi performans gösterir ve kenar yumuşatmayı destekler. Örneğin, aşağıdaki clipPath
çağrısı farklı şekilde ifade edilebilir:
Kotlin
canvas.apply { save() clipPath(circlePath) drawBitmap(bitmap, 0f, 0f, paint) restore() }
Java
canvas.save(); canvas.clipPath(circlePath); canvas.drawBitmap(bitmap, 0f, 0f, paint); canvas.restore();
Bunun yerine, önceki örneği aşağıdaki gibi ifade edin:
Kotlin
paint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) // At draw time: canvas.drawPath(circlePath, mPaint)
Java
// One time init: paint.setShader(new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP)); // At draw time: canvas.drawPath(circlePath, mPaint);
Bit eşlem yüklemeleri
Android, bit eşlemleri OpenGL dokuları olarak görüntüler ve bir çerçeve içinde ilk kez bir bit eşlem görüntülendiğinde GPU'ya yüklenir. Bunu Systrace'te Doku yükleme(kimlik) genişlik x yükseklik şeklinde görebilirsiniz. Bu işlem, Şekil 2'de gösterildiği gibi birkaç milisaniye sürebilir ancak resmin GPU ile görüntülenmesi gerekir.
Bunlar uzun sürüyorsa öncelikle izdeki genişlik ve yükseklik sayılarını kontrol edin. Görüntülenen bit eşlemin, ekranda gösterildiği alandan çok daha büyük olmadığından emin olun. Böyle bir durumda, yükleme süresini ve belleği boşa harcamış olursunuz. Genellikle bit eşlem yükleme kitaplıkları, uygun boyutlu bit eşlem isteğinde bulunma olanağı sunar.
Android 7.0'da, bit eşlem yükleme kodu (genellikle kitaplıklar tarafından yapılır), ihtiyaç duyulmadan erken yüklemeyi tetiklemek için prepareToDraw()
yöntemini çağırabilir. Bu şekilde, yükleme RenderThread
boşta kaldığında erken gerçekleşir. Bit eşlemeyi bildiğiniz sürece, bunu kod çözdükten sonra veya bit eşlemi bir görünüme bağlarken yapabilirsiniz. İdeal olarak, bit eşlem yükleme kitaplığınız bunu sizin için yapar ancak kendi dosyanızı yönetiyorsanız veya daha yeni cihazlarda yüklemelere basmadığınızdan emin olmak istiyorsanız kendi kodunuzda prepareToDraw()
işlevini çağırabilirsiniz.
İleti dizisi planlama gecikmeleri
İş parçacığı planlayıcı, Android işletim sisteminin sistemde hangi iş parçacıklarının ne zaman ve ne kadar süreyle çalıştırılması gerektiğine karar vermekten sorumlu olan parçasıdır.
Bazen, uygulamanızın UI iş parçacığı engellendiği veya çalışmadığı için jank sorunu oluşur. Systrace, bir iş parçacığının uyku (gri), çalıştırılabilir (mavi: Çalışabilir ancak henüz çalıştırılmak üzere seçilmemiş olması), aktif şekilde çalışıyor (yeşil) veya kesintisiz uyku (kırmızı veya turuncu) olduğunu belirtmek için Şekil 3'te gösterildiği gibi farklı renkler kullanır. Bu, iş parçacığı planlama gecikmelerinin neden olduğu olumsuzluk sorunlarının giderilmesinde son derece yararlıdır.
Android'deki işlemler arası iletişim (IPC) mekanizması olan bağlayıcı çağrıları, uygulamanızın çalışmasında uzun süreli duraklamalara neden olur. Android'in sonraki sürümlerinde bu, UI iş parçacığının çalışmayı durdurmasının en yaygın nedenlerinden biridir. Çözüm genellikle bağlayıcı çağrıları yapan işlevleri çağırmaktan kaçınmaktır. Gerekli değilse değeri önbelleğe alın veya çalışmayı arka plan iş parçacıklarına taşıyın. Kod tabanları büyüdükçe, dikkatli değilseniz düşük düzeyli bazı yöntemler çağırarak yanlışlıkla bir bağlayıcı çağrısı ekleyebilirsiniz. Ancak, bunları izlemeyle bulup düzeltebilirsiniz.
Bağlayıcı işlemleriniz varsa aşağıdaki adb
komutlarını kullanarak bunların çağrı yığınlarını yakalayabilirsiniz:
$ adb shell am trace-ipc start
… use the app - scroll/animate ...
$ adb shell am trace-ipc stop --dump-file /data/local/tmp/ipc-trace.txt
$ adb pull /data/local/tmp/ipc-trace.txt
getRefreshRate()
gibi zararsız görünen çağrılar bazen bağlayıcı işlemlerini tetikleyerek sık sık arandığında büyük sorunlara neden olabilir. Düzenli izleme, bu sorunları ortaya çıktıkça bulmanıza ve düzeltmenize yardımcı olabilir.
Bağlayıcı etkinliğini görmüyorsanız ancak kullanıcı arayüzü iş parçacığınızın çalıştığını yine de göremiyorsanız başka bir iş parçacığından kilit veya başka bir işlem beklemediğinizden emin olun. Genellikle kullanıcı arayüzü iş parçacığının diğer iş parçacıklarından gelen sonuçları beklemesi gerekmez. Diğer ileti dizilerinde bilgi yayınlanmalıdır.
Nesne ayırma ve atık toplama
ART, Android 5.0'da varsayılan çalışma zamanı olarak kullanıma sunulduğundan nesne ayırma ve atık toplama (GC) önemli ölçüde daha az sorun teşkil etse de bu ekstra çalışmayla iş parçacıklarınızı ağırlıklandırmak yine de mümkündür. Saniyede sık sık gerçekleşmeyen nadir bir etkinliğe (ör. bir kullanıcının düğmeye dokunması) göre pay ayırmanın bir sakıncası yoktur ancak her ayırmanın bir maliyeti olduğunu unutmayın. Sık sık çağrılan dar bir döngü içindeyse GC'deki yükü hafifletmek için ayırmadan kaçınmayı düşünün.
Systrace, GC'nin sık çalışıp çalışmadığını gösterir. Android Bellek Profilier ise ayırmaların nereden geldiğini gösterebilir. Mümkün olduğunda, özellikle de dar döngülerde ayırmalardan kaçınırsanız sorun yaşama olasılığınız azalır.
Android'in son sürümlerinde, GC genellikle HeapTaskDaemon adlı bir arka plan iş parçacığında çalışır. Önemli miktarda tahsisat, Şekil 5'te gösterildiği gibi GC'ye daha fazla CPU kaynağı harcandığı anlamına gelebilir.
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken gösterilir
- Uygulamanızı karşılaştırma
- Uygulama performansını ölçmeye genel bakış
- Uygulama optimizasyonu için en iyi uygulamalar