Yavaş oluşturma

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.

Systrace örneği
Şekil 1. Systrace örneği.

Şekil 1'deki Systrace örneği, jank'ı tanımlamak için aşağıdaki bilgileri içerir:

  1. 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.
  2. 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.
  3. 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
  • RecyclerView kaydırma aniden çalışıyor
  • Karmaşık animasyonlar içeren ekranlarda, animasyonun düzgün şekilde oluşturulmadığı
  • Uygulama başlatılırken
  • Bir ekrandan farklı bir yere geçme (ör. ekran geçişi)
  • Etkinliğiniz ön plandayken uygulamanız bir giriş etkinliğine veya BroadcastReceiver öğesine (ör. tuşa basma veya ekrana dokunma etkinlikleri) beş saniye içinde yanıt vermedi.
  • Ön planda etkinliğiniz olmasa da BroadcastReceiver cihazınız uzun bir süre içinde yürütmeyi tamamlamamıştı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, yerel RenderThread ü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.

Bir uygulama, bir kare içinde
  büyük bir bit eşlem yükleyerek önemli ölçüde zaman
Şekil 2. Bir uygulama, büyük bir bit eşlem yüklemek için bir kare içinde oldukça fazla zaman harcıyordur. Boyutunu küçültün veya prepareToDraw() ile kodunu çözerken erken tetikleyin.

İ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.

Kullanıcı arayüzü iş parçacığının uykuda olduğu bir dönemi vurgular
Şekil 3. Kullanıcı arayüzü iş parçacığının uykuda olduğu bir dönemin vurgusu.

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.

Bir karavan kaçakındaki bağlayıcı işlemleri nedeniyle uyuyan kullanıcı arayüzü iş parçacığını gösterir. Bağlama mantığınızı odaklı tutun ve bağlayıcı çağrılarını izleyip kaldırmak için trace-ipc kullanın.
Şekil 4. Kullanıcı arayüzü iş parçacığı, bir karavan kaçakındaki bağlayıcı işlemleri nedeniyle uyuyor. Bağlama mantığınızı basit tutun ve bağlayıcı çağrılarını bulup kaldırmak için trace-ipc kullanın.

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.

HeapTaskDaemon&#39;da 94 ms&#39;lik bir GC gösterir
Şekil 5. HeapTaskDaemon iş parçacığında 94 ms GC.

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.