Android rendu et performance - 17 avril 2012

Post on 24-May-2015

1.407 views 0 download

Transcript of Android rendu et performance - 17 avril 2012

Lisse comme du beurreRomain Guy, Google

@romainguyhttp://www.curious-creature.org/+

Lisse comme du beurreRomain Guy, Google

@romainguyhttp://www.curious-creature.org/+

Lisse comme du beurreRomain Guy, Google

@romainguyhttp://www.curious-creature.org/+

Android,����������� ������������������  rendu����������� ������������������  et����������� ������������������  performance

Qui suis-je ? Pourquoi moi ?

• Android depuis 2007• Tech-lead du UI toolkit• Je parle beaucoup• J’ai le micro

Ordre du jour

• UI thread• Views• Animations• Rendu• Mémoire

UI thread

• Ne le bloquez pas !– Évitez les I/O, lourds calculs, etc.

• Mais manipulez la UI depuis ce thread– View.post(Runnable)

– Handler

1: StrictMode.setThreadPolicy(2: new StrictMode.ThreadPolicy.Builder()3: .detectAll()4: .penaltyDeath()5: .build());

StrictMode

1: StrictMode.setThreadPolicy(2: new StrictMode.ThreadPolicy.Builder()3: .detectAll()4: .penaltyDeath()5: .build());

StrictMode

Tâches de fond

1: new Thread(new Runnable() {2: public void run() {3: // Opération4: }5: }).start();

AsyncTask

1: LoadPhotos task =2: new LoadPhotos().execute("romainguy");

1: class LoadPhotos extends AsyncTask<String, Bitmap, PhotoList> { 2: protected PhotoList doInBackground(String... params) { 3: PhotoList photos = Picasa.loadPhotos(params[0]); 4: for (int i = 0; i < photos.getCount(); i++) { 5: publishProgress(photos.get(i).loadSmallBitmap()); 6: } 7: return photos; 8: } 9: 10: protected void onProgressUpdate(Bitmap... values) {11: addPhotoToGrid(values[0]);12: }13: 14: protected void onPostExecute(PhotoList result) {15: setPhotoList(result);16: }17: }

Solution parfaite ?

1: ToggleButton t = (ToggleButton) findViewById(R.id.switchOnOff); 2: t.setOnCheckedChangeListener(new OnCheckedChangeListener() { 3: public void onCheckedChanged(CompoundButton b, boolean isChecked) { 4: new Thread(new Runnable() { 5: public void run() { 6: prefs.edit().putBoolean("switch", isChecked)).commit(); 7: } 8: }).start(); 9: }10: });

Solution parfaite ?

1: ToggleButton t = (ToggleButton) findViewById(R.id.switchOnOff); 2: t.setOnCheckedChangeListener(new OnCheckedChangeListener() { 3: public void onCheckedChanged(CompoundButton b, boolean isChecked) { 4: new Thread(new Runnable() { 5: public void run() { 6: prefs.edit().putBoolean("switch", isChecked)).commit(); 7: } 8: }).start(); 9: }10: });

Ordre d’exécution

UI Thread Thread 1 Thread 2

Switch on

Switch off

Ordre d’exécution

• Processeurs 2/4 coeurs communs• AsyncTask

– Sérialisée depuis Android 3.0

– executeOnExecutor()

Une View est coûteuse

Tem

ps

Nombre d’attributs XML

Inflation

TextView = 800 octets

Poids d’une structure sans les données

Layouts

• Coûts supplémentaires d’exécution • La mise en page est coûteuse– RelativeLayout

– android:layout_weight

• N’appelez pas requestLayout() souvent

Aplatissez vos layouts

<ViewStub />

class MyView extends View

new

new

Utilisez les outils !

traceview, hierarchyviewer, DDMS & lint

View.setLayerType(int type, Paint p)

DrawPatch

Button

Display

Save

ClipRect

Translate

DrawText

Restore

draw()DisplayList

DrawPatch

Button

Display

Save

ClipRect

Translate

DrawText

Restore

draw()DisplayList Layer

draw()

3 sortes de layers

view.setLayerType(View.LAYER_TYPE_NONE, null)

Layer software

view.setLayerType(View.LAYER_TYPE_SOFTWARE, null)

Layer hardware

view.setLayerType(View.LAYER_TYPE_HARDWARE, null)

Dessin d’une ListViewDessin d’une ListViewDessin d’une ListView

Temps en ms

Layer hardware DisplayList Software

0.009 2.1 10.3

Propriétés pour transformer les vues

Opacité Position Taille Rotation Origine

alpha x scaleX rotation pivotX

y scaleY rotationX pivotY

translationX rotationY

translationY

Propriétés de transformation

• Ne changent pas la vue• Changent comment elle se dessine• Propices à de nombreuses optimisations

Optimisation des propriétés

ViewRoot

ViewGroup

View A View B

DisplayList

Layer B DisplayList A

Optimisation des propriétés

ViewRoot

ViewGroup

View A View B

setRotationY(45)

DisplayList

Layer B DisplayList A

Optimisation des propriétés

ViewRoot

ViewGroup

View A View B

setRotationY(45)

invalidate()

DisplayList

Layer B DisplayList A

Optimisation des propriétés

ViewRoot

ViewGroup

View A View B

setRotationY(45)

invalidate()

DisplayList

Layer B DisplayList A

mark dirty

Optimisation des propriétés

ViewRoot

ViewGroup

View A View B

setRotationY(45)

invalidate()

DisplayList

Layer B DisplayList A

mark dirty

Dure réalité

• Les layers ne sont pas gratuits– Coût mémoire important

– Créer ou rafraîchir un layer prend du temps

• Utilisez les temporairement– Vues complexes

– Pour la durée d’une animation

“Property animations”

Nouvelles animations

• Animez une propriété– Objet cible

– Nom de la propriété

– Paramètres de valeurs

• Marche avec toute paire get/set

1: ObjectAnimator.ofFloat(view, "alpha", 0);2: 3: ObjectAnimator.ofFloat(colorDrawable,4: "color", Color.WHITE, Color.BLACK);5: 6: ObjectAnimator.ofFloat(view, View.X, 200);

1: view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 2: ObjectAnimator.ofFloat( 3: view, View.ROTATION_Y, 180).start();

1: view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 2: ObjectAnimator animator = ObjectAnimator.ofFloat( 3: view, View.ROTATION_Y, 180); 4: 5: animator.addListener(new AnimatorListenerAdapter() { 6: @Override 7: public void onAnimationEnd(Animator animation) { 8: view.setLayerType(View.LAYER_TYPE_NONE, null); 9: }10: });11: 12: animator.start();

Mais il y a mieux !

ViewPropertyAnimator

• Plus facile• Plus rapide• Pas de JNI ni de reflection• Optimise les invalidate()

1: view.animate().alpha(0);2: 3: view.animate().translationX(500);4: 5: view.animate().alpha(0).translationX(500);

view.animate().rotationY(180).withLayer();

view.animate().rotationY(180).withLayer();

Ce t te API pourrai t ou ne

pourrait pas se retrouver dans

une future version d’Android

qui ne pourrait elle-même peut

être pas exister. Mais ça serait

bien.

Mémoire

$ adb shell dumpsys meminfo <process>

1 of 2

** MEMINFO in pid 1523 [com.example.android.sample] ** Shared Private Heap Heap Heap Pss Dirty Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ Native 3260 944 3220 9960 6359 20 Dalvik 6952 15612 6344 21319 16224 5095 Cursor 0 0 0 Ashmem 0 0 0 Other dev 12583 660 1096 .so mmap 1149 1812 352 .jar mmap 0 0 0 .apk mmap 114 0 0 .ttf mmap 7 0 0 .dex mmap 807 0 0 Other mmap 44 8 28 Unknown 1439 356 1424 TOTAL 26355 19392 12464 31279 22583 5115

1 of 2

** MEMINFO in pid 1523 [com.example.android.sample] ** Shared Private Heap Heap Heap Pss Dirty Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ Native 3260 944 3220 9960 6359 20 Dalvik 6952 15612 6344 21319 16224 5095 Cursor 0 0 0 Ashmem 0 0 0 Other dev 12583 660 1096 .so mmap 1149 1812 352 .jar mmap 0 0 0 .apk mmap 114 0 0 .ttf mmap 7 0 0 .dex mmap 807 0 0 Other mmap 44 8 28 Unknown 1439 356 1424 TOTAL 26355 19392 12464 31279 22583 5115

1 of 2

** MEMINFO in pid 1523 [com.example.android.sample] ** Shared Private Heap Heap Heap Pss Dirty Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ Native 3260 944 3220 9960 6359 20 Dalvik 6952 15612 6344 21319 16224 5095 Cursor 0 0 0 Ashmem 0 0 0 Other dev 12583 660 1096 .so mmap 1149 1812 352 .jar mmap 0 0 0 .apk mmap 114 0 0 .ttf mmap 7 0 0 .dex mmap 807 0 0 Other mmap 44 8 28 Unknown 1439 356 1424 TOTAL 26355 19392 12464 31279 22583 5115

1 of 2

** MEMINFO in pid 1523 [com.example.android.sample] ** Shared Private Heap Heap Heap Pss Dirty Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ Native 3260 944 3220 9960 6359 20 Dalvik 6952 15612 6344 21319 16224 5095 Cursor 0 0 0 Ashmem 0 0 0 Other dev 12583 660 1096 .so mmap 1149 1812 352 .jar mmap 0 0 0 .apk mmap 114 0 0 .ttf mmap 7 0 0 .dex mmap 807 0 0 Other mmap 44 8 28 Unknown 1439 356 1424 TOTAL 26355 19392 12464 31279 22583 5115

1 of 2

** MEMINFO in pid 1523 [com.example.android.sample] ** Shared Private Heap Heap Heap Pss Dirty Dirty Size Alloc Free ------ ------ ------ ------ ------ ------ Native 3260 944 3220 9960 6359 20 Dalvik 6952 15612 6344 21319 16224 5095 Cursor 0 0 0 Ashmem 0 0 0 Other dev 12583 660 1096 .so mmap 1149 1812 352 .jar mmap 0 0 0 .apk mmap 114 0 0 .ttf mmap 7 0 0 .dex mmap 807 0 0 Other mmap 44 8 28 Unknown 1439 356 1424 TOTAL 26355 19392 12464 31279 22583 5115

2 of 2

Objects Views: 45 ViewRootImpl: 1 AppContexts: 2 Activities: 1 Assets: 2 AssetManagers: 2 Local Binders: 13 Proxy Binders: 14 Death Recipients: 0 OpenSSL Sockets: 0 SQL heap: 0 MEMORY_USED: 0 PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0 Asset Allocations zip:/data/app/com.example.android.sample-2.apk:/resources.arsc: 2K

2 of 2

Objects Views: 45 ViewRootImpl: 1 AppContexts: 2 Activities: 1 Assets: 2 AssetManagers: 2 Local Binders: 13 Proxy Binders: 14 Death Recipients: 0 OpenSSL Sockets: 0 SQL heap: 0 MEMORY_USED: 0 PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0 Asset Allocations zip:/data/app/com.example.android.sample-2.apk:/resources.arsc: 2K

2 of 2

Objects Views: 45 ViewRootImpl: 1 AppContexts: 2 Activities: 1 Assets: 2 AssetManagers: 2 Local Binders: 13 Proxy Binders: 14 Death Recipients: 0 OpenSSL Sockets: 0 SQL heap: 0 MEMORY_USED: 0 PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0 Asset Allocations zip:/data/app/com.example.android.sample-2.apk:/resources.arsc: 2K

$ adb shell dumpsys gfxinfo <process>

Caches:Current memory usage / total memory usage (bytes): TextureCache 1166964 / 25165824 LayerCache 0 / 16777216 GradientCache 0 / 524288 PathCache 0 / 4194304 CircleShapeCache 0 / 1048576 OvalShapeCache 0 / 1048576 RoundRectShapeCache 0 / 1048576 RectShapeCache 0 / 1048576 ArcShapeCache 0 / 1048576 TextDropShadowCache 0 / 2097152 FontRenderer 0 262144 / 262144 FontRenderer 1 262144 / 262144 FontRenderer 2 262144 / 262144Other: FboCache 1 / 16 PatchCache 22 / 512Total memory usage: 1953396 bytes, 1.86 MB

View hierarchy: android.view.ViewRootImpl@40b82f70: 45 views, 4.97 kB (display lists)

Total ViewRootImpl: 1Total Views: 45Total DisplayList: 4.97 kB

Caches:Current memory usage / total memory usage (bytes): TextureCache 1166964 / 25165824 LayerCache 0 / 16777216 GradientCache 0 / 524288 PathCache 0 / 4194304 CircleShapeCache 0 / 1048576 OvalShapeCache 0 / 1048576 RoundRectShapeCache 0 / 1048576 RectShapeCache 0 / 1048576 ArcShapeCache 0 / 1048576 TextDropShadowCache 0 / 2097152 FontRenderer 0 262144 / 262144 FontRenderer 1 262144 / 262144 FontRenderer 2 262144 / 262144Other: FboCache 1 / 16 PatchCache 22 / 512Total memory usage: 1953396 bytes, 1.86 MB

View hierarchy: android.view.ViewRootImpl@40b82f70: 45 views, 4.97 kB (display lists)

Total ViewRootImpl: 1Total Views: 45Total DisplayList: 4.97 kB

Caches:Current memory usage / total memory usage (bytes): TextureCache 1166964 / 25165824 LayerCache 0 / 16777216 GradientCache 0 / 524288 PathCache 0 / 4194304 CircleShapeCache 0 / 1048576 OvalShapeCache 0 / 1048576 RoundRectShapeCache 0 / 1048576 RectShapeCache 0 / 1048576 ArcShapeCache 0 / 1048576 TextDropShadowCache 0 / 2097152 FontRenderer 0 262144 / 262144 FontRenderer 1 262144 / 262144 FontRenderer 2 262144 / 262144Other: FboCache 1 / 16 PatchCache 22 / 512Total memory usage: 1953396 bytes, 1.86 MB

View hierarchy: android.view.ViewRootImpl@40b82f70: 45 views, 4.97 kB (display lists)

Total ViewRootImpl: 1Total Views: 45Total DisplayList: 4.97 kB

Rendu accéléré

• Certains opérations sont gratuites• Certains opérations sont peu coûteuses• Certains opérations sont très coûteuses

Très coûteux

• View.setAlpha() ou Canvas.saveLayer()– Crée un buffer temporaire

– Coûte du temps

– Coûte de la mémoire

Gratuit

• View.setAlpha()– Avec un layer

– LAYER_TYPE_SOFTWARE

– LAYER_TYPE_HARDWARE

Très coûteux

• Créer un layer– Allocation mémoire

– Rendu, surtout layers software

• Faites-le en avance– View.buildLayer()

• Évitez les invalidate()– Rafraîchit le layer

Peu coûteux

• Un layer se dessine VITE• Effets visuels presque gratuits

– Fondu (opacité avec alpha)

– Filtres de couleurs

Coûteux

• Modifier une Paint souvent

• Modifier un Bitmap souvent

• Modifier un Path souvent

• Créer un nouvel objet encore plus coûteux

Gratuit

• Transformations– Translation

– Rotation 2D et 3D

– Mise à l’échelle

• Filtrage de texture– Paint.setFilterBitmap(true)

• Avec ou sans layer

Optimiser pour le GPU

• Redessiner seulement les parties utiles– N’utiliez pas View.invalidate()

– Utilisez View.invalidate(l, t, r, b)

• Permet de traverser l’arbre de la UI rapidement

Optimiser pour le GPU

• Groupez les primitives– Pas bien : boucle de Canvas.drawLine()

– Bien : Canvas.drawLines()

• Évitez les changements d’état– Minimisez les appels à save() & restore()

Optimiser pour le GPU

• Groupez les commandes dans cet ordre– Par type

– Par Paint

– Par Bitmap

1: c.drawText(...); 2: c.drawBitmap(...); 3: c.drawLine(...); 4: c.drawText(...); 5: c.drawBitmap(...); 6: c.drawLine(...); 7: c.drawText(...); 8: c.drawBitmap(...); 9: c.drawLine(...);

Pas bien

1: c.drawText(...); 2: c.drawBitmap(...); 3: c.drawLine(...); 4: c.drawText(...); 5: c.drawBitmap(...); 6: c.drawLine(...); 7: c.drawText(...); 8: c.drawBitmap(...); 9: c.drawLine(...);

Pas bien

1: c.drawText(...); 2: c.drawText(...); 3: c.drawText(...); 4: c.drawBitmap(...); 5: c.drawBitmap(...); 6: c.drawBitmap(...); 7: c.drawLine(...); 8: c.drawLine(...); 9: c.drawLine(...);

Bien

1: c.drawText(...); 2: c.drawText(...); 3: c.drawText(...); 4: c.drawBitmap(...); 5: c.drawBitmap(...); 6: c.drawBitmap(...); 7: c.drawLines(...);

Mieux

Pour plus d’informations

• Google I|O 2011– Android Accelerated Rendering

– http://youtu.be/v9S5EO7CLjo

• Parleys.com– Various Devoxx talks for the past 4 years

• Android Developers– http://d.android.com

Questions et (peut-être) réponses