Android rendu et performance - 17 avril 2012
-
Upload
paris-android-user-group -
Category
Technology
-
view
1.407 -
download
0
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