android optimisations greendroid

Post on 09-May-2015

3.739 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

http://www.cyrilmottier.com@cyrilmottier

Cyril Mottier

Optimisations généralessous Android et GreenDroid

Concevoir des applications réactives, fluides et facile d’utilisation

Note généraleMais qui est donc ce personnage ?

Moi - 1/2

Moi - 1/2

• Bloggueur• http://android.cyrilmottier.com

Moi - 1/2

• Bloggueur• http://android.cyrilmottier.com

• Développeur• MetroMap Paris• GreenDroid

Moi - 1/2

• Bloggueur• http://android.cyrilmottier.com

• Développeur• MetroMap Paris• GreenDroid

• Adorateur et prêcheur d’Android• Droidcon UK, ADL Paris, GET, PAUG, etc.

Moi - 2/2

• Auteur• Développez sur Android

chez Digit Books• Co-écrit avec Ludovic Perrier

http://j.mp/hCIJzj

IntroductionPrésentation des contraintes

Sous nos pieds ... - 1/3

• Système adapté aux terminaux contraints• Peu de puissance• Peu de mémoire• Peu de batterie• Peu d’espace d’affichage

Sous nos pieds ... - 2/3

• Machine Virtuelle basique• La fameuse Dalvik VM• Pas de JIT (avant Froyo)

Sous nos pieds ... - 3/3

• Ramasse-miettes (garbage collector - GC) basique• Mark and sweep

• stop-the-world (pré Gingerbread)• Concurrent (post Gingerbread)

Le ramasse-miettes - 1/2

Le ramasse-miettes - 1/2

Le ramasse-miettes - 2/2

Le ramasse-miettes - 2/2

Android aurait-il unproblème de

performance ?

Conclusion - 1/2

Android aurait-il unproblème de

performance ?NON

Conclusion - 1/2

Conclusion - 2/2

Conclusion - 2/2

• GC non contrôlable• Oubliez System.gc() !• Voyez le GC comme une entité indépendante

Conclusion - 2/2

• GC non contrôlable• Oubliez System.gc() !• Voyez le GC comme une entité indépendante

• Pensez différemment ...• Utilisez les types primitifs• Minimisez la création d’objets• Réutilisez les objets

Conclusion - 2/2

• GC non contrôlable• Oubliez System.gc() !• Voyez le GC comme une entité indépendante

• Pensez différemment ...• Utilisez les types primitifs• Minimisez la création d’objets• Réutilisez les objets

Si nécessaire !

Optimisations JavaEtude du langage, de ses facilités et

de ses inconvénients

• Introduit en 1996• Langage haut-niveau

• Langage objet fortement typé• Pas/peu de gestion mémoire• Syntaxe avancée (foreach, etc.)

• Apparente simplicité• Cache des points d’ombre

Le langage Java

Autoboxing

public int factBad(int arg) { if (arg < 0) { throw new ArithmeticException("arg must be a positive integer"); } Integer result = 1; for (int i = 2; i <= arg; i++) { result *= i; } return result;}

Autoboxing

public int factBad(int arg) { if (arg < 0) { throw new ArithmeticException("arg must be a positive integer"); } Integer result = 1; for (int i = 2; i <= arg; i++) { result *= i; } return result;}

public int factBad2(int arg) { if (arg < 0) { throw new ArithmeticException("arg must be a positive integer"); } Integer result = new Integer(1); for (int i = 2; i <= arg; i++) { result = new Integer(result.intValue() * i); } return result.intValue();}

équivaut à

• Utilisez les types primitifs• byte, short, int, long• float, double• boolean, char

Autoboxing

public int factGood2(int arg) { if (arg < 0) { throw new ArithmeticException(); } return (arg == 0) ? 1 : arg * factGood2(arg - 1);}

public int factGood(int arg) { if (arg < 0) { throw new ArithmeticException(); } int result = 1; for (int i = 2; i <= arg; i++) { result *= i; } return result;}

Les types génériques

HashMap<Integer, String> hashMap = new HashMap<Integer, String>();hashMap.put(664, "PAUG");hashMap.put(665, "is");hashMap.put(666, "awesome");

// ...hashMap.get(666);// ...

Les types génériques

HashMap<Integer, String> hashMap = new HashMap<Integer, String>();hashMap.put(664, "PAUG");hashMap.put(665, "is");hashMap.put(666, "awesome");

// ...hashMap.get(666);// ...

HashMap<Integer, String> hashMap = new HashMap<Integer, String>();hashMap.put(new Integer(664), "PAUG");hashMap.put(new Integer(665), "is");hashMap.put(new Integer(666), "awesome");

// ...hashMap.get(new Integer(666));// ...

équivaut à

• Préférez les SparseArrays (android.util) :• SparseBooleanArray• SparseIntArray• SparseArray• LongSparseArray (API privée)

Les types génériques

SparseArray<String> sparseArray = new SparseArray<String>();sparseArray.put(664, "PAUG");sparseArray.put(665, "is");sparseArray.put(666, "awesome");

// ...sparseArray.get(666);// ...

Temporaires contre statiques - 1/2

public boolean intersect(int left, int top, int right, int bottom) { return intersect(new Rect(left, top, right, bottom));}

public abstract boolean intersect(Rect rect);

Temporaires contre statiques - 1/2

public boolean intersect(int left, int top, int right, int bottom) { return intersect(new Rect(left, top, right, bottom));}

public abstract boolean intersect(Rect rect);

private static final Rect sRect = new Rect();

public boolean intersect(int left, int top, int right, int bottom) { sRect.set(left, top, right, bottom); return intersect(sRect);}

public abstract boolean intersect(Rect rect);

Préférez les statiques aux temporaires ...

Temporaires contre statiques - 2/2

• Technique dédiée aux méthodes critiques• onDraw(), onMeasure(), onLayout(), getView(), etc.

• Paint• Rect• Point

• Classes utilitaires• Random

• Méthodes à retour via arguments• Location.distanceBetween( ..., float[] results)

Les arguments variables

public void main() { varargs(1, 2, 3);}

public abstract void varargs(int ... args);

Les arguments variables

public void main() { varargs(1, 2, 3);}

public abstract void varargs(int ... args);

public void main() { varargs(new int[]{1, 2, 3});}

public abstract void varargs(int ... args);

est équivalent à la création d’un tableau ...

Les itérateurs - 1/2

public void iteratorBad(List<T> list) {

for (T obj : list) { // ... }}

Les itérateurs - 1/2

public void iteratorBad(List<T> list) {

for (T obj : list) { // ... }}

public void iteratorBad(List<T> list) {

T obj; for (Iterator<T> i = list.iterator(); i.hasNext(); obj = i.next()) { // ... }}

Revient à créer un Iterator

• Utilisez la syntaxe foreach :• Facile à lire• Optimisée

• Limitez la casse !

Les itérateurs - 2/2

public void iteratorGood(List<T> list) { if (list != null && list.size() != 0) { for (T obj : list) { // ... } }}

Les Strings - 1/3

private static final String SLOGAN = "This" + " " + "conference" + " " + "is" + " " + "awesome";

public String getSlogan() { return SLOGAN;}

Les Strings - 1/3

private static final String SLOGAN = "This" + " " + "conference" + " " + "is" + " " + "awesome";

public String getSlogan() { return SLOGAN;}

private static final String SLOGAN = "This conference is awesome";

public String getSlogan() { return "This conference is awesome";}

est résolu à la compilation par

Les Strings - 2/3

public String concatBad(String[] strings) { String result = null;

for (String s : strings) { result += s; } return result;}

Les Strings - 2/3

public String concatBad(String[] strings) { String result = null;

for (String s : strings) { result += s; } return result;}

public String concatBad(String[] strings) { String result = null;

for (String s : strings) { result = new StringBuilder(result).append(s).toString(); } return result;}

entraine l’instanciation d’un StringBuilder

Les Strings - 3/3

public String concatCorrect(String[] strings) { StringBuilder result = new StringBuilder();

for (String s : strings) { result.append(s); } return result.toString();}

Les Strings - 3/3

public String concatCorrect(String[] strings) { StringBuilder result = new StringBuilder();

for (String s : strings) { result.append(s); } return result.toString();}

private static StringBuilder sStringBuilder = new StringBuilder();

public String concatGood(String[] strings) { sStringBuilder.setLength(0); for (String s : strings) { sStringBuilder.append(s); } return sStringBuilder.toString();}

Ou encore mieux ...

Quelques astuces

• Réutilisez les objets• ViewHolder et convertView avec les ListViews/Adapters• Handler.obtainMessage() / Message.obtain()• Classes dans android.util :

• PoolableManager, Pool, Poolable, Pools, FinitePool, SynchronizedPool

• Evitez la création d’objets• CharArrayBuffer avec les Cursors• SparseArray

Optimisations UIMettez de la puissance et de la fluidité

à vos interfaces graphiques !

Layouts optimisésDéveloppeurs et bûcherons : même combat !

Présentation

Présentation

• Un maître mot : MINIMISATION !

Présentation

• Un maître mot : MINIMISATION !• Moins de vues équivaut à :

• measure() plus rapide• layout() plus rapide• draw() plus rapide

Présentation

• Un maître mot : MINIMISATION !• Moins de vues équivaut à :

• measure() plus rapide• layout() plus rapide• draw() plus rapide

• Trop de vues :• OutOfMemoryException• LayoutInflater.inflate() long

Présentation

• Un maître mot : MINIMISATION !• Moins de vues équivaut à :

• measure() plus rapide• layout() plus rapide• draw() plus rapide

• Trop de vues :• OutOfMemoryException• LayoutInflater.inflate() long

• Préférez la largeur à la profondeur

Le cas du débutant ... - 1/2

• Création d’un nouveau projet• Utilisation du layout « exemple »

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout>

Le cas du débutant ... - 1/2

Le cas du débutant ... - 2/2

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" />

RelativeLayout - 1/2<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="5dp" android:orientation="horizontal">

<ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:src="@drawable/icon" />

<LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="5dp" android:orientation="vertical"> <TextView android:id="@+id/title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:textAppearance="?android:attr/textAppearanceLarge" android:text="@string/title" /> <TextView android:id="@+id/subtitle" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="@string/subtitle" /> </LinearLayout>

</LinearLayout>

RelativeLayout - 1/2

RelativeLayout - 2/2<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="5dp">

<ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:src="@drawable/icon" />

<TextView android:id="@+id/title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:layout_toRightOf="@+id/image" android:textAppearance="?android:attr/textAppearanceLarge" android:text="@string/title" />

<TextView android:id="@+id/subtitle" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/image" android:layout_below="@+id/title" android:textAppearance="?android:attr/textAppearanceMedium" android:text="@string/subtitle" />

</RelativeLayout>

RelativeLayout - 2/2

La balise <merge /> - 1/2

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent">

<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|center_horizontal" android:background="@color/default_background" android:src="@drawable/icon" />

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal" android:background="@color/default_background" android:textAppearance="?android:attr/textAppearanceLarge" android:text="@string/title" />

</FrameLayout>

La balise <merge /> - 1/2

• Permet de contourner les limitations de XML

La balise <merge /> - 2/2

<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android">

<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|center_horizontal" android:background="@color/default_background" android:src="@drawable/icon" />

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal" android:background="@color/default_background" android:textAppearance="?android:attr/textAppearanceLarge" android:text="@string/title" />

</merge>

La balise <merge /> - 2/2

ViewStub - 1/2

<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android">

<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical">

<!-- ... -->

</LinearLayout>

<include layout="@layout/help" android:id="@+id/help" />

</merge>

ViewStub - 1/2

• Evite les créations de vues rarement utilisées• JIT inflation

ViewStub - 2/2

<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android">

<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" />

<ViewStub android:id="@+id/view_stub" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout="@layout/help" android:inflatedId="@+id/help" />

</merge>

ViewStub - 2/2

findViewById(R.id.view_stub).setVisibility(View.VISIBLE);// ouView inflatedView = ((ViewStub) findViewById(R.id.view_stub)).inflate();

Vues personnalisées

public class CustomView extends View {

public CustomView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }

@Override protected void onDraw(Canvas canvas) { // Dessin de la vue }

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Définition de la taille de la vue en fonction des spécifications setMeasuredDimension(100, 100); }}

Layouts personnalisées

public class CustomLayout extends ViewGroup {

public CustomLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Définition de la taille de la vue en fonction des spécifications et // des dimensions des vues filles // child.measure(widthMeasureSpec, heightMeasureSpec) }

@Override protected void onLayout(boolean changed, int l, int t, int r, int d) { // Positionnement et dimensionnement de l'ensemble des vues filles // child.layout(left, top, right, int bottom) }}

TextView et Drawable - 1/2

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="vertical">

<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginBottom="4dp" android:src="@drawable/icon" />

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="@string/not_awesome" />

</LinearLayout>

TextView et Drawable - 1/2

TextView et Drawable - 2/2

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:drawableTop="@drawable/icon" android:drawablePadding="4dp" android:text="@string/awesome" />

Les outils du SDK

• hierarchyviewer• layoutopt• draw9patch• ddms

UI/Main ThreadLibertéééééééééééé (Braveheart)

Présentation

Présentation

• Le système graphique Android est single threaded• Limitez l’impact sur le UI thread !

Présentation

• Le système graphique Android est single threaded• Limitez l’impact sur le UI thread !

• Conséquences nombreuses• Animations saccadées• ANR• Utilisateur mécontent• Application désinstallée / critiquée ...

Solutions

Solutions

• Java !• synchronize / wait() / notify() / notifyAll()

Solutions

• Java !• synchronize / wait() / notify() / notifyAll()

• java.util.concurrent

Solutions

• Java !• synchronize / wait() / notify() / notifyAll()

• java.util.concurrent• Système de message d’Android

• Handler, Message, Looper, HandlerThread

Solutions

• Java !• synchronize / wait() / notify() / notifyAll()

• java.util.concurrent• Système de message d’Android

• Handler, Message, Looper, HandlerThread

• AsyncTasks

Solutions

• Java !• synchronize / wait() / notify() / notifyAll()

• java.util.concurrent• Système de message d’Android

• Handler, Message, Looper, HandlerThread

• AsyncTasks• IntentService

AsyncTask

private class AsyncTaskStrategy implements LongTaskStrategy {

public void executeTask() { (new DumbTask()).execute((Void[]) null); } private class DumbTask extends AsyncTask<Void, Void, Void> {

@Override protected Void doInBackground(Void... params) { executeLongTask(); return null; }

@Override protected void onPostExecute(Void result) { onLongTaskExecuted(); } }}

Handlerprivate class HandlerStrategy implements LongTaskStrategy { private static final int DUMP_MESSAGE = 0x1234; private DumbHandler mHandler;

public void executeTask() { mHandler = new DumbHandler(); new Thread(new Runnable() { public void run() { executeLongTask(); final Message msg = Message.obtain(mHandler, DUMP_MESSAGE); mHandler.sendMessage(msg); } }).start(); }

private class DumbHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == DUMP_MESSAGE) onLongTaskExecuted(); } }

}

Conclusion

Conclusion

• Utile pour les opérations longues/bloquantes• Entrées / sorties (network & filesystem)• Calculs longs• Accès hardware (Camera.open())

Conclusion

• Utile pour les opérations longues/bloquantes• Entrées / sorties (network & filesystem)• Calculs longs• Accès hardware (Camera.open())

• Classes/méthodes d’aide dans l’API• SharedPreferences.Editor.apply()• AsyncQueryHandler pour les requêtes aux

ContentProviders• Filter.filter(CharSequence, FilterListener)

Conclusion

• Utile pour les opérations longues/bloquantes• Entrées / sorties (network & filesystem)• Calculs longs• Accès hardware (Camera.open())

• Classes/méthodes d’aide dans l’API• SharedPreferences.Editor.apply()• AsyncQueryHandler pour les requêtes aux

ContentProviders• Filter.filter(CharSequence, FilterListener)

• Process.setThreadPriority(int)

GreenDroidUne bibliothèque d’aide au développement

Un triste constat

• Ressenti général médiocre• Peu de qualité• Design / ergo antagonistes

• Framework difficile ?• Trop ouvert• Pas d’aide UI/UX

Pourquoi GreenDroid ?

• La naissance de GreenDroid :• Challenge, opensource, factorisation, etc.• Make Android Market a better place

• La philosophie :• Make Android development consistent and easier

UtilisationImport de la bibliothèque GreenDroid à

votre projet

Utilisation

Utilisation

4 étapes

• Cloner le projet GreenDroid (GitHub)

Utilisation

2

3

4

1

• Cloner le projet GreenDroid (GitHub)

Utilisation

2

3

4

1git clone http://github.com/cyrilmottier/GreenDroid.git

• Appliquer GreenDroid à votre projet Android :• Clic droit > Properties• Android > Library > Add• Sélection du dossier GreenDroid > OK

Utilisation

3

4

1

2

• Hériter des thèmes GreenDroid :• @style/Theme.GreenDroid• @style/Theme.GreenDroid.NoTitleBar• Un thème héritant des thèmes précédents

Utilisation

3

4

1

2

• Hériter des thèmes GreenDroid :• @style/Theme.GreenDroid• @style/Theme.GreenDroid.NoTitleBar• Un thème héritant des thèmes précédents

Utilisation

3

4

1

2

<application android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/Theme.GreenDroid"> <!-- ... -->

</application>

• S’assurer que votre application est une GDApplication :• greendroid.app.GDApplication• Votre propre GDApplication

Utilisation

3

4

1

2

• S’assurer que votre application est une GDApplication :• greendroid.app.GDApplication• Votre propre GDApplication

Utilisation

3

4

1

2

<application android:icon="@drawable/icon" android:label="@string/app_name" android:name="greendroid.app.GDApplication" android:theme="@style/Theme.GreenDroid"> <!-- ... -->

</application>

Régles importantes

• Ne modifiez pas GreenDroid !• Utilisation de la notion d’héritage :

• Java pour les classes• XML pour les styles / thèmes

• Pas possible de faire autrement ?• fork• patch• feature request

• N’hésitez pas à patcher / participer !

FonctionnalitésRapide tour d’horizon des possibilités ouvertes

par GreenDroid

ActionBar - 1/2

• Pattern ergonomique :• Affiche le titre de l’écran courant• Donne accès à des actions• Permet le retour à l’écran principal• Présente l’état courant :

• ProgressBar présentant le chargement• Couleur de l’ActionBar fonction du contenu• ...

ActionBar - 2/2

public class ActionBarActivity extends GDActivity {

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setActionBarContentView(R.layout.text); addActionBarItem(Type.Locate, R.id.action_bar_locate); }

@Override public boolean onHandleActionBarItemClick(ActionBarItem item, int position) { switch (item.getItemId()) { case R.id.action_bar_locate:

// Do something break; default: return super.onHandleActionBarItemClick(item, position); } return true; }}

AsyncImageView - 1/2

• ImageView améliorée• Chargement asynchrone d’images

• distantes (http://)• locales (file://)

• Gestion d’un cache• Pré-processing possible

AsyncImageView - 2/2

public class SimpleAsyncImageViewActivity extends GDActivity { private static final String URLS_1 = "https://lh3.googleusercontent.com/_OHO4y8YcQbs/SoWDYIhFrjI/AAAAAAAAKX4/ETS4JGuUYX0/s400/P1080412.JPG"; private static final String URLS_2 = "https://lh6.googleusercontent.com/_OHO4y8YcQbs/So4a6aWih3I/AAAAAAAAKts/hGFcqaHsCuI/s400/P1080809.JPG"; private AsyncImageView mImageView;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setActionBarContentView(R.layout.image); mImageView = (AsyncImageView) findViewById(R.id.image_view); } public void onShowImage1(View v) { mImageView.setUrl(URLS_1); } public void onShowImage2(View v) { mImageView.setUrl(URLS_2); } }

Et bien d’autres ...

• ItemAdapter• QuickActions• ActionBarDrawable• SegmentedBar• etc.

Questions / Réponses

http://www.cyrilmottier.com@cyrilmottier

Cyril Mottier

top related