Kavramlar ve Jetpack Compose uygulaması
Android 3.0'dan (API düzeyi 11) itibaren Android 2D oluşturma işlem hattı donanım hızlandırmayı destekler. Bu, View'ın tuvalinde gerçekleştirilen tüm çizim işlemlerinin GPU'yu kullandığı anlamına gelir.
Donanım hızlandırmayı etkinleştirmek için daha fazla kaynak gerektiğinden uygulamanız daha fazla RAM kullanır.
Hedef API düzeyiniz >=14 ise donanım hızlandırma varsayılan olarak etkindir ancak açıkça da etkinleştirilebilir. Uygulamanız yalnızca standart görünümler ve Drawable kullanıyorsa bu ayarı genel olarak etkinleştirmek olumsuz çizim efektlerine neden olmaz. Ancak donanım hızlandırma, 2D çizim işlemlerinin tümünde desteklenmediğinden bu özelliği etkinleştirmek bazı özel görünümlerinizi veya çizim çağrılarınızı etkileyebilir. Sorunlar genellikle görünmez öğeler, istisnalar veya yanlış oluşturulmuş pikseller olarak ortaya çıkar. Bu sorunu gidermek için Android, donanım hızlandırmayı birden fazla düzeyde etkinleştirme veya devre dışı bırakma seçeneği sunar. Donanım hızlandırmayı kontrol etme başlıklı makaleyi inceleyin.
Uygulamanız özel çizim yapıyorsa sorunları bulmak için donanım hızlandırma özelliği açık olan gerçek donanım cihazlarda uygulamanızı test edin. Çizim işlemleri desteği bölümünde, donanım hızlandırmayla ilgili bilinen sorunlar ve bu sorunları giderme yöntemleri açıklanmaktadır.
Ayrıca OpenGL with the Framework APIs (Çerçeve API'leriyle OpenGL) ve Renderscript başlıklı makalelere de göz atın.
Donanım hızlandırmayı kontrol etme
Donanım hızlandırmayı aşağıdaki düzeylerde kontrol edebilirsiniz:
- Uygulama
- Etkinlik
- Pencere
- Göster
Uygulama düzeyi
Android manifest dosyanızda, uygulamanızın tamamında donanım hızlandırmayı etkinleştirmek için <application> etiketine aşağıdaki özelliği ekleyin:
<application android:hardwareAccelerated="true" ...>
Etkinlik düzeyi
Uygulamanız, donanım hızlandırma özelliği genel olarak etkinleştirildiğinde düzgün çalışmıyorsa bu özelliği tek tek etkinlikler için de kontrol edebilirsiniz. Donanım hızlandırmayı etkinlik düzeyinde etkinleştirmek veya devre dışı bırakmak için <activity> öğesinde android:hardwareAccelerated özelliğini kullanabilirsiniz. Aşağıdaki örnek, uygulamanın tamamında donanım hızlandırmayı etkinleştirir ancak bir etkinlikte devre dışı bırakır:
<application android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
Pencere seviyesi
Daha da hassas bir denetime ihtiyacınız varsa aşağıdaki kodu kullanarak belirli bir pencere için donanım hızlandırmayı etkinleştirebilirsiniz:
Kotlin
window.setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED )
Java
getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
Görünüm düzeyi
Aşağıdaki kodu kullanarak, çalışma zamanında tek bir görünüm için donanım hızlandırmayı devre dışı bırakabilirsiniz:
Kotlin
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
Java
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Bir görünümün donanım hızlandırmalı olup olmadığını belirleme
Bir uygulamanın, özellikle özel görünümler gibi öğeler için şu anda donanım hızlandırmalı olup olmadığını bilmesi bazen yararlı olur. Bu özellik, uygulamanızda çok sayıda özel çizim yapılıyorsa ve tüm işlemler yeni oluşturma ardışık düzeni tarafından düzgün şekilde desteklenmiyorsa özellikle yararlıdır.
Uygulamanın donanım hızlandırmalı olup olmadığını kontrol etmenin iki farklı yolu vardır:
View.isHardwareAccelerated,View, donanım hızlandırmalı bir pencereye bağlıysatruedeğerini döndürür.Canvas.isHardwareAccelerated,Canvasdonanım hızlandırmalıysatruedeğerini döndürür.
Bu kontrolü çizim kodunuzda yapmanız gerekiyorsa mümkün olduğunda View.isHardwareAccelerated yerine Canvas.isHardwareAccelerated kullanın. Görünüm, donanım hızlandırmalı bir pencereye eklendiğinde donanım hızlandırmalı olmayan bir tuval kullanılarak çizilmeye devam edebilir. Örneğin, bir görünümü önbelleğe alma amacıyla bit eşleme olarak çizdiğinizde bu durum oluşur.
Android çizim modelleri
Donanım hızlandırma etkinleştirildiğinde Android çerçevesi, uygulamanızı ekranda oluşturmak için görüntüleme listelerini kullanan yeni bir çizim modeli kullanır. Görüntüleme listelerini ve uygulamanızı nasıl etkileyebileceklerini tam olarak anlamak için Android'in donanım hızlandırması olmadan görünümleri nasıl çizdiğini de anlamak faydalıdır. Aşağıdaki bölümlerde yazılıma dayalı ve donanım hızlandırmalı çizim modelleri açıklanmaktadır.
Yazılıma dayalı çizim modeli
Yazılım çizim modelinde görünümler aşağıdaki iki adımda çizilir:
- Hiyerarşiyi geçersiz kılma
- Hiyerarşiyi çizme
Bir uygulamanın kullanıcı arayüzünün bir bölümünü güncellemesi gerektiğinde, içeriği değişen herhangi bir görünümde invalidate() (veya varyantlarından biri) çağrılır. Geçersiz kılma mesajları, yeniden çizilmesi gereken ekran bölgelerini (kirli bölge) hesaplamak için görünüm hiyerarşisinde yukarı doğru yayılır. Android sistemi daha sonra hiyerarşideki, kirli bölgeyle kesişen tüm görünümleri çizer. Maalesef bu çizim modelinin iki dezavantajı vardır:
İlk olarak, bu model her çizim geçişinde çok fazla kodun yürütülmesini gerektirir. Örneğin, uygulamanız bir düğmede
invalidateçağırıyorsa ve bu düğme başka bir görünümün üzerinde yer alıyorsa Android sistemi, değişmemiş olmasına rağmen görünümü yeniden çizer.İkinci sorun ise çizim modelinin uygulamanızdaki hataları gizleyebilmesidir. Android sistemi, görünümler kirli bölgeyle kesiştiğinde yeniden çizdiğinden, içeriğini değiştirdiğiniz bir görünüm,
invalidateçağrılmamış olsa bile yeniden çizilebilir. Bu durumda, doğru davranışı elde etmek için başka bir görünümün geçersiz kılınmasına güveniyorsunuz. Bu davranış, uygulamanızı her değiştirdiğinizde değişebilir. Bu nedenle, görünümün çizim kodunu etkileyen verileri veya durumu her değiştirdiğinizde özel görünümlerinizdeinvalidateişlevini çağırmanız gerekir.
Donanım hızlandırmalı çizim modeli
Android sistemi, ekran güncellemeleri istemek ve görünümleri oluşturmak için invalidate ve draw kullanmaya devam eder ancak gerçek çizimi farklı şekilde işler.
Android sistemi, çizim komutlarını hemen yürütmek yerine bunları görünüm hiyerarşisinin çizim kodunun çıktısını içeren görüntüleme listelerine kaydeder. Bir diğer optimizasyon ise Android sisteminin yalnızca invalidate çağrısıyla kirli olarak işaretlenen görünümler için görüntüleme listelerini kaydetmesi ve güncellemesi gerektiğidir. Geçersiz kılınmamış görünümler, daha önce kaydedilmiş görüntüleme listesi yeniden yayınlanarak yeniden çizilebilir. Yeni çizim modeli üç aşamadan oluşur:
Hiyerarşiyi geçersiz kılma
Görüntüleme listelerini kaydetme ve güncelleme
Görüntüleme listelerini çizme
Bu modelde, draw yönteminin yürütülmesi için kirli bölgeyle kesişen bir görünüme güvenemezsiniz. Android sisteminin bir görünümün görüntüleme listesini kaydetmesini sağlamak için invalidate yöntemini çağırmanız gerekir. Bunu yapmayı unutursanız görünüm değiştirildikten sonra bile aynı görünür.
Görüntüleme listelerini kullanmak, animasyon performansına da fayda sağlar. Çünkü alfa veya döndürme gibi belirli özelliklerin ayarlanması, hedeflenen görünümün geçersiz kılınmasını gerektirmez (bu işlem otomatik olarak yapılır). Bu optimizasyon, görüntüleme listeleri içeren görünümler (uygulamanızın donanım hızlandırmalı olduğu tüm görünümler) için de geçerlidir. Örneğin, Button üzerinde ListView içeren bir LinearLayout olduğunu varsayalım. LinearLayout için görüntüleme listesi şu şekildedir:
DrawDisplayList(ListView)DrawDisplayList(Button)
Şimdi ListView opaklığını değiştirmek istediğinizi varsayalım. ListView üzerinde setAlpha(0.5f) çağrıldıktan sonra, görüntüleme listesi artık şunları içerir:
SaveLayerAlpha(0.5)DrawDisplayList(ListView)RestoreDrawDisplayList(Button)
ListView öğesinin karmaşık çizim kodu yürütülmedi. Bunun yerine, sistem yalnızca çok daha basit olan LinearLayout öğesinin görüntüleme listesini güncelledi.
Donanım hızlandırmanın etkinleştirilmediği bir uygulamada hem listenin hem de üst öğesinin çizim kodu tekrar yürütülür.
Çizim işlemleri için destek
Donanım hızlandırmalı olduğunda 2D oluşturma ardışık düzeni, en sık kullanılan Canvas çizim işlemlerinin yanı sıra daha az kullanılan birçok işlemi de destekler. Android ile birlikte gelen uygulamaları, varsayılan widget'ları ve düzenleri, yansımalar ve döşenmiş dokular gibi yaygın gelişmiş görsel efektleri oluşturmak için kullanılan tüm çizim işlemleri desteklenir.
Aşağıdaki tabloda, API düzeylerindeki çeşitli işlemlerin destek düzeyi açıklanmaktadır:
| Desteklenen ilk API düzeyi | ||||
| Tuval | ||||
| drawBitmapMesh() (renkler dizisi) | 18 | |||
| drawPicture() | 23 | |||
| drawPosText() | 16 | |||
| drawTextOnPath() | 16 | |||
| drawVertices() | 29 | |||
| setDrawFilter() | 16 | |||
| clipPath() | 18 | |||
| clipRegion() | 18 | |||
| clipRect(Region.Op.XOR) | 18 | |||
| clipRect(Region.Op.Difference) | 18 | |||
| clipRect(Region.Op.ReverseDifference) | 18 | |||
| Döndürme/perspektif ile clipRect() | 18 | |||
| Boyama yapın | ||||
| setAntiAlias() (metin için) | 18 | |||
| setAntiAlias() (çizgiler için) | 16 | |||
| setFilterBitmap() | 17 | |||
| setLinearText() | ✗ | |||
| setMaskFilter() | ✗ | |||
| setPathEffect() (çizgiler için) | 28 | |||
| setShadowLayer() (metin dışında) | 28 | |||
| setStrokeCap() (çizgiler için) | 18 | |||
| setStrokeCap() (noktalar için) | 19 | |||
| setSubpixelText() | 28 | |||
| Xfermode | ||||
| PorterDuff.Mode.DARKEN (framebuffer) | 28 | |||
| PorterDuff.Mode.LIGHTEN (framebuffer) | 28 | |||
| PorterDuff.Mode.OVERLAY (framebuffer) | 28 | |||
| Shader | ||||
| ComposeShader içinde ComposeShader | 28 | |||
| ComposeShader içinde aynı türdeki gölgelendiriciler | 28 | |||
| ComposeShader'da yerel matris | 18 | |||
Tuval ölçeklendirme
Donanımla hızlandırılmış 2D oluşturma ardışık düzeni, ölçeklenmemiş çizimi desteklemek için ilk olarak oluşturuldu. Bazı çizim işlemleri, daha yüksek ölçek değerlerinde kaliteyi önemli ölçüde düşürüyordu. Bu işlemler, GPU tarafından dönüştürülen 1.0 ölçeğinde çizilmiş dokular olarak uygulanır. API düzeyi 28'den itibaren tüm çizim işlemleri sorunsuz bir şekilde ölçeklendirilebilir.
Aşağıdaki tabloda, büyük ölçekleri doğru şekilde işlemek için uygulamanın ne zaman değiştirildiği gösterilmektedir:
| Ölçeklendirilecek çizim işlemi | Desteklenen ilk API düzeyi |
| drawText() | 18 |
| drawPosText() | 28 |
| drawTextOnPath() | 28 |
| Basit Şekiller | 17 |
| Karmaşık Şekiller | 28 |
| drawPath() | 28 |
| Gölge katmanı | 28 |
Uygulamanız bu eksik özelliklerden veya sınırlamalardan herhangi birinden etkileniyorsa setLayerType(View.LAYER_TYPE_SOFTWARE, null) işlevini çağırarak donanım hızlandırmayı yalnızca uygulamanızın etkilenen kısmı için devre dışı bırakabilirsiniz.
Bu şekilde, donanım hızlandırmadan başka her yerde yararlanmaya devam edebilirsiniz.
Uygulamanızda donanım hızlandırmayı farklı seviyelerde nasıl etkinleştireceğiniz ve devre dışı bırakacağınız hakkında daha fazla bilgi için Donanım hızlandırmayı kontrol etme başlıklı makaleyi inceleyin.
Katmanları görüntüleme
Android'in tüm sürümlerinde görünümler, görünümün çizim önbelleğini veya Canvas.saveLayer kullanarak ekran dışı arabelleklerde oluşturulabiliyordu. Ekran dışı arabelleklerin veya katmanların çeşitli kullanım alanları vardır. Bu katmanları, karmaşık görünümleri canlandırırken daha iyi performans elde etmek veya kompozisyon efektleri uygulamak için kullanabilirsiniz. Örneğin, bir görünümü geçici olarak katmanda oluşturmak ve ardından opaklık faktörüyle ekranda tekrar birleştirmek için Canvas.saveLayer kullanarak karartma efektleri uygulayabilirsiniz.
Android 3.0'dan (API düzeyi 11) itibaren, View.setLayerType yöntemiyle katmanları nasıl ve ne zaman kullanacağınız konusunda daha fazla kontrole sahip olursunuz. Bu API iki parametre alır: kullanmak istediğiniz katmanın türü ve katmanın nasıl birleştirilmesi gerektiğini açıklayan isteğe bağlı bir Paint nesnesi. Bir katmana renk filtreleri, özel karıştırma modları veya opaklık uygulamak için Paint parametresini kullanabilirsiniz. Bir görünümde üç katman türünden biri kullanılabilir:
LAYER_TYPE_NONE: Görünüm normal şekilde oluşturulur ve ekran dışı bir arabellek tarafından desteklenmez. Bu, varsayılan davranıştır.LAYER_TYPE_HARDWARE: Uygulama donanım hızlandırmalıysa görünüm, donanım dokusunda donanımda oluşturulur. Uygulama donanım hızlandırmalı değilse bu katman türü,LAYER_TYPE_SOFTWAREile aynı şekilde davranır.LAYER_TYPE_SOFTWARE: Görünüm, yazılımda bit eşleme olarak oluşturulur.
Kullandığınız katman türü hedefinize bağlıdır:
Performans: Bir görünümü donanım dokusuna dönüştürmek için donanım katmanı türü kullanın. Bir görünüm katmana dönüştürüldüğünde, görünüm
invalidateişlevini çağırana kadar çizim kodunun yürütülmesi gerekmez. Alfa animasyonları gibi bazı animasyonlar doğrudan katmana uygulanabilir. Bu, GPU için çok verimli bir yöntemdir.Görsel efektler: Bir görünüme özel görsel işlemler uygulamak için donanım veya yazılım katmanı türü ve
Paintkullanın. Örneğin,ColorMatrixColorFilterkullanarak siyah beyaz bir görünüm çizebilirsiniz.Uyumluluk: Bir görünümün yazılımda oluşturulmasını zorlamak için yazılım katmanı türünü kullanın. Donanım hızlandırmalı bir görünümde (örneğin, uygulamanızın tamamı donanım hızlandırmalıysa) oluşturma sorunları varsa bu, donanım oluşturma işlem hattının sınırlamalarını aşmanın kolay bir yoludur.
Katmanları ve animasyonları görüntüleme
Donanım katmanları, uygulamanız donanım hızlandırmalı olduğunda daha hızlı ve sorunsuz animasyonlar sunabilir. Çok sayıda çizim işlemi gerçekleştiren karmaşık görünümleri canlandırırken animasyonu saniyede 60 kare hızında çalıştırmak her zaman mümkün olmayabilir. Bu sorun, görünümü donanım dokusuna işlemek için donanım katmanları kullanılarak giderilebilir. Donanım dokusu daha sonra görünümü canlandırmak için kullanılabilir. Böylece, görünümün canlandırılırken sürekli olarak kendini yeniden çizmesi gerekmez. Görünümün özellikleri değiştirilmediği (invalidate çağrılır) veya invalidate manuel olarak çağrılmadığı sürece görünüm yeniden çizilmez. Uygulamanızda animasyon çalıştırıyorsanız ve istediğiniz gibi sorunsuz sonuçlar elde edemiyorsanız animasyonlu görünümlerinizde donanım katmanlarını etkinleştirmeyi deneyin.
Bir görünüm donanım katmanıyla desteklendiğinde özelliklerinden bazıları, katmanın ekranda oluşturulma şekliyle işlenir. Bu özelliklerin ayarlanması, görünümün geçersiz kılınmasını ve yeniden çizilmesini gerektirmediğinden verimli olacaktır. Aşağıda, katmanın birleştirilme şeklini etkileyen özelliklerin listesi verilmiştir. Bu özelliklerden herhangi biri için ayarlayıcıyı çağırmak, optimum geçersiz kılma ve hedeflenen görünümün yeniden çizilmemesiyle sonuçlanır:
alpha: Katmanın opaklığını değiştirir.x,y,translationX,translationY: Katmanın konumunu değiştirir.scaleX,scaleY: Katmanın boyutunu değiştirir.rotation,rotationX,rotationY: Katmanın 3D uzaydaki yönünü değiştirir.pivotX,pivotY: Katmanın dönüşüm kaynağını değiştirir.
Bu özellikler, bir görünümü ObjectAnimator ile animasyon yaparken kullanılan adlardır. Bu özelliklere erişmek istiyorsanız uygun ayarlayıcıyı veya alıcıyı çağırın. Örneğin, alfa özelliğini değiştirmek için setAlpha işlevini çağırın. Aşağıdaki kod snippet'inde, 3D'de bir görünümü Y ekseni etrafında döndürmenin en verimli yolu gösterilmektedir:
Kotlin
view.setLayerType(View.LAYER_TYPE_HARDWARE, null) ObjectAnimator.ofFloat(view, "rotationY", 180f).start()
Java
view.setLayerType(View.LAYER_TYPE_HARDWARE, null); ObjectAnimator.ofFloat(view, "rotationY", 180).start();
Donanım katmanları video belleğini tükettiğinden, bunları yalnızca animasyon süresince etkinleştirmeniz ve animasyon tamamlandıktan sonra devre dışı bırakmanız önemle tavsiye edilir. Bunu animasyon dinleyicilerini kullanarak yapabilirsiniz:
Kotlin
view.setLayerType(View.LAYER_TYPE_HARDWARE, null) ObjectAnimator.ofFloat(view, "rotationY", 180f).apply { addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { view.setLayerType(View.LAYER_TYPE_NONE, null) } }) start() }
Java
view.setLayerType(View.LAYER_TYPE_HARDWARE, null); ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.setLayerType(View.LAYER_TYPE_NONE, null); } }); animator.start();
Mülk animasyonu hakkında daha fazla bilgi için Mülk animasyonu başlıklı makaleyi inceleyin.
İpuçları ve püf noktaları
Donanım hızlandırmalı 2D grafiklere geçiş performansı anında artırabilir ancak yine de aşağıdaki önerileri uygulayarak uygulamanızı GPU'yu etkili bir şekilde kullanacak şekilde tasarlamanız gerekir:
- Uygulamanızdaki görünüm sayısını azaltın
- Sistemin çizmesi gereken görünüm sayısı arttıkça sistem yavaşlar. Bu durum, yazılım oluşturma işlem hattı için de geçerlidir. Görünümleri azaltmak, kullanıcı arayüzünüzü optimize etmenin en kolay yollarından biridir.
- Fazla çizimden kaçının
- Üst üste çok fazla katman çizmeyin. Üzerindeki diğer opak görünümler tarafından tamamen gizlenen görünümleri kaldırın. Üst üste harmanlanmış birkaç katman çizmeniz gerekiyorsa bunları tek bir katmanda birleştirebilirsiniz. Mevcut donanımlarda, kare başına ekranda piksel sayısının 2,5 katından fazla çizim yapmamak iyi bir kuraldır (bit eşlemdeki şeffaf pikseller sayılır).
- Çizim yöntemlerinde oluşturma nesneleri oluşturmayın
- Sık yapılan bir hata, her oluşturma yöntemi çağrıldığında yeni bir
Paintveya yeni birPatholuşturmaktır. Bu, çöp toplayıcının daha sık çalışmasına neden olur ve donanım işlem hattındaki önbellekleri ve optimizasyonları atlar. - Şekilleri çok sık değiştirmeyin
- Örneğin, karmaşık şekiller, yollar ve daireler doku maskeleri kullanılarak oluşturulur. Her yol oluşturduğunuzda veya değiştirdiğinizde donanım ardışık düzeni yeni bir maske oluşturur. Bu işlem maliyetli olabilir.
- Bit eşlemleri çok sık değiştirmeyin
- Bir bit eşlemin içeriğini her değiştirdiğinizde, bir sonraki çiziminizde GPU dokusu olarak tekrar yüklenir.
- Alfa sürümünü dikkatli kullanma
setAlpha,AlphaAnimationveyaObjectAnimatorkullanarak bir görünümü yarı saydam hale getirdiğinizde, bu görünüm ekran dışı bir arabellekte oluşturulur. Bu da gerekli doldurma hızını iki katına çıkarır. Çok büyük görünümlerde alfa uygularken görünümün katman türünüLAYER_TYPE_HARDWAREolarak ayarlamayı düşünebilirsiniz.