Ce diaporama a bien été signalé.
Le téléchargement de votre SlideShare est en cours. ×

Architecture et Bonnes pratiques Android #DevoxxFr2016 Part1

Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Chargement dans…3
×

Consultez-les par la suite

1 sur 157 Publicité

Architecture et Bonnes pratiques Android #DevoxxFr2016 Part1

Depuis maintenant 7 ans que je développe sous Android, ma principale préoccupation a toujours été l'architecture.

Et si nous prenions quelques heures pour en discuter ?
Je vous propose une vision globale et synthétique s'appuyant sur des exemples concrets, sur les principes et conseils de référence des équipes Google et sur des modèles d'architecture (MVP/n-tiers) et leur mises en place.
Au programme:
Le contexte Android,
L'objectif utilisateur,
La responsabilité du développeur,
Les bonnes pratiques (celles de Chet Haase, Romain Guy et les miennes),
Les principes d'architecture (n-tiers, MVP et MVVM),
Leur application sur Android (services, threads, Application ...),
Le déploiement continue,
Les librairies incontournables du moment,
Un exemple concret d'utilisation d'un service REST (up et download)
et bien sûr un projet github est associé à cette conférence pour que le code soit avec vous !

Depuis maintenant 7 ans que je développe sous Android, ma principale préoccupation a toujours été l'architecture.

Et si nous prenions quelques heures pour en discuter ?
Je vous propose une vision globale et synthétique s'appuyant sur des exemples concrets, sur les principes et conseils de référence des équipes Google et sur des modèles d'architecture (MVP/n-tiers) et leur mises en place.
Au programme:
Le contexte Android,
L'objectif utilisateur,
La responsabilité du développeur,
Les bonnes pratiques (celles de Chet Haase, Romain Guy et les miennes),
Les principes d'architecture (n-tiers, MVP et MVVM),
Leur application sur Android (services, threads, Application ...),
Le déploiement continue,
Les librairies incontournables du moment,
Un exemple concret d'utilisation d'un service REST (up et download)
et bien sûr un projet github est associé à cette conférence pour que le code soit avec vous !

Publicité
Publicité

Plus De Contenu Connexe

Les utilisateurs ont également aimé (20)

Similaire à Architecture et Bonnes pratiques Android #DevoxxFr2016 Part1 (20)

Publicité

Architecture et Bonnes pratiques Android #DevoxxFr2016 Part1

  1. 1. @android2ee by(Mathias Seguy== Android2EE){ French AndroidTrainer}
  2. 2. Android2EE est référencé en tant qu’organisme de formation, vous pouvez faire prendre en charge tout ou partie du montant de cette formation par votre OPCA. Cette formation Initiation avancée à Android est éligible au titre du DIF et CIF. Lieu : Paris, Toulouse, Lyon Durée : 5 jours Prix : 2980 € 2
  3. 3. Lieu : Paris Date : 10 - 13 Mai 2016 Durée : 4 jours 3 Lieu : Lyon Date : 23 - 27 Mai 2016 Durée : 5 jours Lieu : Paris Date : 06 - 10 Juin 2016 Durée : 5 jours
  4. 4. Pénurie Vie en collectivité
  5. 5. Le contexte est tout Papa, on peut atteindre Mach1 ? La réponse dépend du contexte ! DataCenter Desktop Tablette Smartphone You're here Light1, facile. Mach1, facile. Euh,... Qui me parle ? Allégorie de la puissance
  6. 6. Le contexte est tout Papa, tu me sers à boire de l'eau, s'il te plait ? Water water.drink() giveWater() water.drink() null Le contexteLe contexte NPE
  7. 7. Le contexte mémoire sous Android La mémoire
  8. 8. La mémoire L'eau c'est la vie ! Pas sur Android
  9. 9. La mémoire c'est la vie ! La mémoire L'eau c'est la vie ! Pas sur Android
  10. 10. La mémoire c'est la vie ! La mémoire Mais Vous développez avec les meilleurs devices de la planète !!! Performance Base de données locale et fichiers Persistance multi-processus
  11. 11. La mémoire dans la vrai vie de vos utilisateurs ! La mémoire HelloWorld.apk == 1 Mo faibles perfs pas d'espace Appareil Standard : 8Go (4GO courant)
  12. 12. Le contexte CPU/GPU sous Android Le CPU et GPU
  13. 13. Dans la vrai vie Chaque utilisateur possède un CPU différent (tout pourri) Dans tes rêves Le CPU et GPU
  14. 14. Le CPU et GPU CPU/GPU == Videur de Batterie
  15. 15. Le contexte énergie sous Android L'énergie
  16. 16. L'énergie Ce que l'on pense/espère avoir
  17. 17. L'énergie Ce que nos utilisateurs ont vraiment
  18. 18. L'énergie Votre problème principal
  19. 19. L'énergie Les causes du gaspillage énergétique CPU/GPU Connexion (Http,...) Drawable/Animations... Mauvaises pratiques de code Services sans fin
  20. 20. L'énergie Les causes du gaspillage énergétique Mauvaises pratiques de code Putain de javaïstes (Android c'est pas du Java sur des serveurs clusterisés !!!)
  21. 21. Le contexte collectif sous Android Vie en collectivité
  22. 22. Le contexte mutualiste sous Android La vie en collectivité gmail twitter hangout evernote messsager facebook gmap linkedIn runstatic your app chrome
  23. 23. C'est le vivre tous ensemble qui est compliqué (pensez, aussi, aux sanitaires du camping municipal de cet été) La tragédie des biens communs
  24. 24. Complexité Contraintes
  25. 25. 60 chances par seconde toutes les 16ms 60 fps
  26. 26. Aucun traitement dans l'UI Thread, jamais, aucun. UI thread
  27. 27. Lui bosse, mais toi, tu bouges plus, t'es en mode freeze. GarbageCollector 10-20 ms avec Dalvik 2-3ms avec ART (mais il n'est pas ton ennemi)
  28. 28. Changer votre vision de la Base de données, la boite est trop petite. Persistence
  29. 29. Communication réseau: principale cause de drainage de la batterie. Réseau
  30. 30. Communication réseau: La big cookies startegy. Réseau
  31. 31. Ne mettez pas à jour trop souvent, vous n'êtes pas seul ! Don't oversync. Réseau
  32. 32. Dans un monde complexe Synthèse Mémoire Puissance Energie et de responsabilité collectiveUn contexte de pénuries 60fps UI Thread intouchable GC sans espace énergivore
  33. 33. Plaisir Usability (Convivialité & utilisabilité)
  34. 34. L'expérience utilisateur le plaisir
  35. 35. Combler un besoin utilisateur. Je veux faire un truc et je peux!
  36. 36. Avec efficacité. Quand je le fais, ça marche!
  37. 37. L'utilisateur doit toujours savoir ce qu'il fait, où il est. Je sais ce que je fais !
  38. 38. Et quand il le fait, c'est facile, simple et bluffant. C'est facile et c'est fluide
  39. 39. Et grâce à vous, il possède des capacités qu'il n'avait pas. ça me donne des super pouvoirs!
  40. 40. Ne frustrer pas votre utilisateur, laissez le libre. Sans authentification, ni paiement.
  41. 41. Je veux faire un truc, je peux le faire, et quand je le fais, ça marche ! Je sais ce que je fais. C'est facile et c'est fluide. Ca me donne des super pouvoirs, sans authentification, ni paiement. Le plaisir! Combler un besoin utilisateur avec efficacité. L'utilisateur doit toujours savoir ce qu'il fait, où il est. Et quand il le fait, c'est facile, simple et bluffant. Et grâce à vous, il possède des capacités qu'il n'avait pas. Ne frustrer pas votre utilisateur, laissez le libre. Utilisateur Développeur
  42. 42. L'expérience utilisateur Convivialité
  43. 43. et j'ai utilisé mon téléphone ! J'ai encore de la batterie à 18h
  44. 44. ou pas... Je le fais d'un doigt
  45. 45. Spéciale dédicace à la RATP! Ca marche même hors ligne
  46. 46. sans télécharger pendant des heures des données, instancier quarante classes ou faire des tonnes de requêtes en base! Ca démarre instantanément
  47. 47. ça va pas me lâcher au moment où j'en ai besoin! Et je peux avoir confiance Spéciale dédicace à la RATP ! Again !
  48. 48. L'expérience utilisateur Utilisabilité
  49. 49. Les animations amènent la compréhension. Animation
  50. 50. L'information au bon moment. Notifications
  51. 51. Suivez le guide ! Material Design https://www.google.com/design/spec/material-design/introduction.html
  52. 52. Soyez Asynchrone ! Toujours Asynchrone ! Asynchrone
  53. 53. Respect Adaptation
  54. 54. Des contraintes du système. Respect
  55. 55. De l'utilisateur Respect android:contentDescription public class MyAccessibilityService extends AccessibilityService
  56. 56. et de ses données. Respect
  57. 57. et de ses données. Respect On va vendre les données utilisateur.
  58. 58. Respect Des bonnes pratiques de dèv.
  59. 59. Des bonnes pratiques d'ergonomie. Respect http://android-developers.blogspot.fr/2015/05/android-design-support-library.html https://www.google.com/design/spec/material-design/introduction.html
  60. 60. Aux écrans (tailles et densités). Adaptation
  61. 61. A la puissance des processeurs. Adaptation
  62. 62. Aux tailles mémoires. Adaptation
  63. 63. A la batterie (à ce qu'il en reste surtout). Adaptation <receiver android:name=".BatteryLevelReceiver"> <intent-filter> <action android:name="android.intent.action.ACTION_BATTERY_LOW"/> <action android:name="android.intent.action.ACTION_BATTERY_OKAY"/> </intent-filter> </receiver>
  64. 64. A la bande passante. Adaptation
  65. 65. Aux versions Adaptation Android 4.1 JellyBean 27 Juin 2012 Android 4.4 KitKat 3 Septembre 2013 Android 2.3 Gingerbread 06 Décembre 2010 Android 3.0 HoneyComb 10 Mai 2011 Android 4.0 IceCreamSandwich 19 Novembre 2011 Android 5.0 Lollipop 3 Novembre 2014 Android 6.0 MarshMallow 5 Octobre 2015
  66. 66. Aux constructeurs Adaptation https://code.google.com/p/android/issues/detail?id=78377
  67. 67. Des habitudes Des Design Patterns
  68. 68. 74 Pour réaliser mon objectif utilisateur Pour assumer mes responsabilités de développeur Pour m'adapter aux contraintes du système Pourquoi des bonnes pratiques ?
  69. 69. N'allouez pas d'objets. Mémoire
  70. 70. N'allouez pas d'objets. Mémoire
  71. 71. N'allouez pas d'objets. Mémoire N'allouez pas d'objets dans les boucles et les méthodes appelées souvent (onDraw). Mettez les objets en cache (pour éviter de les réallouer) Utiliser des pools d'objets au besoin Utiliser des variables de classes temporaires (plutôt que des variables de méthodes) Utiliser ArrayMap/SimpleArray Map/SparseArray au lieux de HashMap Définissez la taille de vos ArrayList
  72. 72. N'allouez pas d'objets. Mémoire Pas d'itérateurs, il faut for(int i=0; i<malist.size(); i++) Oubliez que les enums existent Que des librairies spécifiquement écrites pour Android public static Objet obj; peut générer des fuites mémoires Instanciez les objets à la demande LazyLoading Préférez les paramètres IO à vos méthodes que la création d'un objet
  73. 73. N'allouez pas d'objets. Mémoire Répondez présent au CallBack onTrimMemory et désallouez Tuez vos Services dès que possible LargeHeap ? non c'est pas pour toi
  74. 74. N'allouez pas d'objets. Mémoire
  75. 75. Utilisez la SupportLibrary. Adaptation
  76. 76. Interface, Factory et implémentations: La programmation par contrat. Adaptation if(postHoneyComb) if(postJellyBean) if(postKitKat) Factory Implémentation Interface Client resvaluesversions.xml <resources> <bool name="postHoneyComb">false</bool> <bool name="postJellyBean">false</bool> <bool name="postKitKat">false</bool> </resources> resvalues-v11versions.xml <resources> <bool name="postHoneyComb">true</bool> <bool name="postJellyBean">false</bool> <bool name="postKitKat">false</bool> </resources> resvalues-v16versions.xml <resources> <bool name="postHoneyComb">true</bool> <bool name="postJellyBean">true</bool> <bool name="postKitKat">false</bool> </resources> connait Demande renvoie l'implémentation adaptée au contexte
  77. 77. Interface, Factory et implémentations: La programmation par contrat. Adaptation Factory Interface Client if(postHoneyComb) if(postJellyBean) if(postKitKat) Versions if(wifi) if(3G) if(noconnectivity) connectivité if(ginger) if(ics) if(lollipop) Animations
  78. 78. Interface, Factory et implémentations: La programmation par contrat. Adaptation Factory Interface Client if(batteryLow) if(batteryOk) if(charging) Batterie if(lowRam) if(casualRam) Faible appareil if(nexus) if(htc) if(samsung) Constructeur
  79. 79. A la compilation: Gradle et Saveur (Flavor) Adaptation Gradle Code prod screendensity processeur versions staging/test Saveur
  80. 80. UI Performance Pas de lecture/écriture disque Pas d'appel réseau ni de communication en général Pas de gros calcul pas de traitement pas de complexité
  81. 81. Les opérations UI Thread coûteuses. UI Performance Mesurer et Disposer. Measure and Layout Inflation. Initialisation de l'objet Application.
  82. 82. Les opérations UI Thread coûteuses. UI Performance Mesurer et Disposer. Inflation. Initialisation de l'objet Application. Complexité de la hiérarchie des vues. RelativeLayout.
  83. 83. Pas d'animation durant les opérations coûteuses. UI Performance Les CustomViews permettent de simplifier le measure et layout. Démarrage Rapide : Utilisez la Starting Window avec le bon thème. Le ViewStub est ton ami. Le LinearLayout est ton meilleur ami. Choisissez bien vos Layouts en fonction de votre besoin. Simplifiez vos IHM, aplatissez vos layouts.
  84. 84. 90 UI Pro Tips Les styles sont tes meilleurs amis.
  85. 85. !--Style for TextView--> <!-- ************************--> <style name=“mainTxv“ parent="@android:style/TextAppearance"> <item name=“android:textSize“>12sp</item> <item name=“android:textStyle“>bold</item> <item name=“android:background“>"@color/background"</item> <item name=“android:paddingLeft“>3dip</item> <item name=“android:paddingRight“>3dip</item> <item name=“android:textColor“> "@color/foreground" </item> <item name=“android:layout_width“>wrap_content</item> <item name=“android:layout_height“>wrap_content</item> </style> <style name=“mainEdt“ parent="mainTxv"> <item name=“android:textSize“>12sp</item> <item name=“android:textStyle“>bold</item> </style> 91 MyBaseTheme Theme Theme.Holo MyThemeColorChart RefColors RefDimensions Style TxvStyle TxvTitleStyle EdtStyle BtnStyle And so on TxvWarningStyle TxvErrorStyle BtnTitleStyle BtnWarningStyle <TextView style=“@style/ TxvStyle“ android:text=“@string/hello“ /> UI Pro Tips Les styles sont tes meilleurs amis.
  86. 86. UI Pro Tips Globalement cohérent: affectez votre style aux composants natifs. Custom Toast et Custom AlertDialog <?xml version="1.0" encoding="utf-8"?> <resources> <style name="Theme.Light.JCertif" parent="android:style/Theme.Holo.Light"> <item name="android:dropDownItemStyle">@style/Widget.DropDownItemLight</item> </style> <style name="Theme.Light.JCertif.preHC" parent="android:style/Theme.NoTitleBar"> <item name="android:dropDownItemStyle">@style/Widget.DropDownItemLight</item> </style> <style name="Widget.DropDownItemLight" parent="@android:style/Widget.DropDownItem"> <item name="android:textColor">@color/deep_blue</item> <item name="android:textSize">@dimen/textSize</item> <item name="android:textStyle">italic</item> </style> </resources> http://romannurik.github.io/AndroidAssetStudio/ http://blog.stylingandroid.com/archives/1267 https://github.com/android/platform_frameworks_base/blob/master/core/res/res/values/themes.xml https://github.com/android/platform_frameworks_base/blob/master/core/res/res/values/styles.xml http://www.androidviews.net/category/dev-tools/ http://android-ui-utils.googlecode.com/hg/asset-studio/dist/index.html private void myMakeText() { LayoutInflater inflater = getLayoutInflater(); View layout = getLayoutInflater().inflate(R.layout.toast_layout, ); Toast toast = new Toast(this); toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0); toast.setDuration(Toast.LENGTH_LONG); toast.setView(layout); toast.show(); }
  87. 87. Redirigez vos layouts Gravez dans le marbre le nombre de fragments affichés 93 •reslayout activity_a_two_panes_layout.xml (le vrai layout) activity_a_one_panes_layout.xml (le vrai layout) Dans ActivityA setContentView(R.layout.main_activity) • resvalues  layout_redirection.xml <resources> <item name="main_activity" type="layout">@layout/activity_one_panes</item> <bool name="twoPane">false</bool> </resources> •resvalues-land || resvalues-large || reslayout-xlarge layout_redirection.xml <resources> <item name="main_activity" type="layout">@layout/activity_two_panes</item> <bool name="twoPane">true</bool> </resources> getRessource().getBoolean(R.bool.twoPane) UI Pro Tips
  88. 88. Attention à l'overDraw. UI Pro Tips Utiliser la Starting Windows thèmée (vous pouvez mettre une image). Utiliser le Context approprié. Les vues ne doivent pas être connues des threads (autre que l'UI).
  89. 89. Ajoutez votre connectivité (Wifi,..,edge) à vos requêtes Ajoutez la version des données à vos urls /myservlet_v1.2 MyOnlyObject in Json+GZip Ajoutez un TimeStamp à vos données dans vos bases de données (local & global) Requêtez vos données en vous appuyant sur les timestamp Utilisez json + gzip (ou mieux: des dataBuffer) C'est le serveur qui effectue le Prefetch des données (basé sur votre connectivité) TimeStamp TimeStamp /myservlet_v1.2?timespan=11215982/myservlet_v1.2?timespan=11215982&net=wifi Utilisez la Big Cookies stratégie. Réseau
  90. 90. Réseau Utiliser GCM NetworkManager (JobScheduler) Ne sur-synchroniser pas ! Vous n'êtes pas seul. Utilisez GCM. Vérifiez le réseau avant de faire un appel. N'oubliez pas les zones a faible débit (Afrique, Aveyron...).
  91. 91. Tu n'es pas un Javaïste Parcelable c'est super.... pour les intents. Pas de stockage disque !!! Utilisez les structures Android : SparseArray, ArrayMap,... Oubliez que la sérialisation existe. Utilisez les SharedPref Préférez les types primitifs et attention à l'auto-boxing
  92. 92. Persistance Jamais de chemin en dur, jamais, seulement du relatif. N'utilisez pas SQLite pour de la persistance simple Une base de données peut posséder plusieurs Tables...
  93. 93. Objectif Interne Fichiers privés, protégés, propres à l'application. Détruits lors de la désinstallation. Cache Fichiers temporaires propres à l'application. Détruits lors de la désinstallation. External Fichiers privés et publics, non-protégés, propres à l'application. Détruits lors de la désinstallation. Public External Fichiers publics, non-protégés. Non détruits lors de la désinstallation. Persistance Pour les fichiers : Connaître son objectif.
  94. 94. Méthode pour récupérer le dossier racine Interne File getFilesDir() Cache File getCacheDir() External File getExternalStorage(String type); Public External File getExternalStoragePublicDirectory(String type); Persistance Utilisez les classes File, FileInputStream et FileOutputStream. ou BufferSink et BufferSource... Quand on Connaît son objectif, tout devient facile.
  95. 95. Persistance Vérifiez la présence du fichier avant d'écrire ou de lire... //Then read your file File filePicture = new File(subFolder, fileNameStr); if(filePicture.exist()){ //Then open an InputStream on it your file FileInputStream fis = new FileInputStream(filePicture);
  96. 96. Persistance Vérifiez l'état de l'ExternalDirectory avant d'éssayer de lire/écrire. String externalStorageState = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(externalStorageState)) { // We can read and write the media} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(externalStorageState)) { // We can only read the media}
  97. 97. Authentification Toujours authentifier vos utilisateurs UUID SharedPreferences SharedPreferences BackupHelper
  98. 98. Exception Gérer vos exceptions ! Exception ManagedException isManaged UserId Error message Technical data Functional data Others stuff
  99. 99. Exception Gérer vos exceptions ! Exception Manager ExceptionManager .manage(...) Team FeedBackUser FeedBack SomeWhere in your code galaxy
  100. 100. Thread Centralisez leur gestion. La gestion des Threads est cruciale. Faîtes attention aux fuites mémoires. Utilisez un ServiceExecutor Utilisez PLUSIEURS ServiceExecutor MyApplication ServiceExecutor
  101. 101. /** * The ThreadPool Executor for caching background */ ExecutorService executor = Executors.newFixedThreadPool(6, null); /** * Launch a Runnable*/ public synchronized void cacheBackGround() { executor.submit(mCacheBackgroundRunnnable); } Thread ExecutorService utilise un ThreadPoolExecutor
  102. 102. if (executor != null) { executor.shutdown(); // Disable new tasks from being submitted try {//as long as your threads hasn't finished while (!executor.isTerminated()) { // Wait a while for existing tasks to terminate if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { // Cancel currently executing tasks executor.shutdownNow(); Log.e("MyApp","Probably a memory leak here"); } } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted executor.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); Log.e("MyApp","Probably a memory leak here too"); }}} Thread N'oubliez pas de les détruire
  103. 103. Memory Leak
  104. 104. Memory Leak Fuite mémoire courante : Un Thread pointant vers l'IHM Thread I.H.M Thread de traitement Activity Handler sendMessage() message Thread I.H.M Thread de traitement Activity sendMessage() message Handler OnDestroy() OnCreate() Thread de traitement initiale sendMessage() Activity fantôme Handler fantôme Utilisez WeakReference
  105. 105. Fuite mémoire courante : Un Thread pointant vers l'IHM Accorder le cycle de vie de la thread avec celui de l'activité Thread I.H.M Thread de traitement Activity Handler sendMessage() Thread I.H.M Thread de traitement Activity sendMessage() message message OnDestroy() OnCreate() Handler Memory Leak
  106. 106. Fuite mémoire courante : Un Thread pointant vers l'IHM Conserver la Thread Thread I.H.M Thread de traitement Activity Handler sendMessage() Thread I.H.M Activity message message Thread de traitement Handler sendMessage() OnDestroy() OnCreate() !!!Attention ne pas utiliser en l’état !!! !!! Fuite mémoire !!! Memory Leak
  107. 107. Fuite mémoire courante : Non static inner class Utilisez les static inner class public class MainActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); exampleOne(); } private void exampleOne() { new Thread() { public void run() { while (true) {SystemClock.sleep(1000); } }}.start(); }} Non Static Inner Class public class MainActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new MyThread().start(); } private static class MyThread extends Thread { public void run() { while (true) {SystemClock.sleep(1000);} }} } Memory Leak
  108. 108. Fuite mémoire courante : private static avec une référence implicite vers la classe englobante Ne pas le faire public class MainActivity extends Activity { private static Drawable sBackground; protected void onCreate(Bundle state) { super.onCreate(state); TextView label = new TextView(this); if (sBackground == null) { sBackground = getDrawable(R.drawable.largebitmap); } label.setBackgroundDrawable(sBackground); setContentView(label); }} Mem Leak Memory Leak
  109. 109. public class Cst { public static int MaConstante=1; } public class DamnedService extends Service { private void somewhere(int val){ if(val==Cst.MaConstante){...} } } Fuite mémoire courante : public static dans un objet lourd Extraire les constantes dans une classe dédiée Mem Leak Memory Leak public class MainActivity extends Activity { public static int MaConstante=1; } public class DamnedService extends Service { private void somewhere(int val){ if(val==MainActivity.MaConstante){...} } }
  110. 110. Architecture Activity est l'entrée principale de votre application. Don't fuck around with the system ! Un Service permet de dire au système "Je fais une longue opération, considère moi comme une Activité visible sans IHM." BroadcastReceiver indique au système qu'il est intéressé pour recevoir telle ou telle information.
  111. 111. Analysez Utilisation mémoire. Analysez votre application. Utilisation réseau. Utilisation CPU/GPU. Trace system (sysTrace). Analyse des écrans.
  112. 112. n-tiers MVP ou MVVM
  113. 113. Architecture: Objectif Simplicité Tests SéparationMaintenance EvolutionResponsabilités Tests unitaires Tests Intégration Tests Fonctionnels Robustesse
  114. 114. Un peu d'histoire public class HistoryBattleActivity extends AppCompatActivity { private static final String TAG = "HistoryBattleActivity"; private static final int CONTEXT_CURRENT=110274; private static final int CONTEXT_HISTORY=131274; /*********************************************************** * Attributes **********************************************************/ BattleFragment battleFragment; /** * Current context History/current */ int currentContext=CONTEXT_CURRENT; /*********************************************************** * Managing RoundTrip animation (VectorDrawable1 to VectorDrawable 2 and back again ********************************************************** /** * The LevelList that contains only two AnimatedVectorDrawable, * the ones used to go from on to the other */ LevelListDrawable backupRoundTrip; /** * The current AnimatedVector diaplsyed by the RoundTrip */ AnimatedVectorDrawable contextDrawable; /** * To know is the animation have been already launched */ boolean backupRoundTripFirstLaunched=true; /** * Historical battles */ ArrayList<Long> battlesId=null; /*********************************************************** * Attributes for the ViewPager **********************************************************/ /** * The TabLayout itself :) */ TabLayout tabLayout; /** * The page Adapter : Manage the list of views (in fact here, it's fragments) * And send them to the ViewPager */ private MyPagerAdapter pagerAdapter; /** * The ViewPager is a ViewGroup that manage the swipe from left to right to left * Like a listView with a gesture listener... */ private ViewPager viewPager; /*********************************************************** * Managing the Life cycle **********************************************************/ **********************************************************/ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e(TAG, "onCreate() called"); setContentView(R.layout.activity_history); //find the Toolbar Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); //use it as your action bar setSupportActionBar(toolbar); getSupportActionBar().setSubtitle(getString(R.string.history_fragment_subtitle)); getSupportActionBar().setTitle(getString(R.string.history_fragment_title)); tabLayout = (TabLayout) findViewById(R.id.tabLayout); //Define its gravity and its mode tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); //Define the color to use (depending on the state a different color should be disaplyed) //Works only if done before adding tabs tabLayout.setTabTextColors(getResources().getColorStateList(R.color.tab_selector_color)); //instanciate the PageAdapter pagerAdapter=new MyPagerAdapter(this,true); //Find the viewPager viewPager = (ViewPager) super.findViewById(R.id.viewpager); // Affectation de l'adapter au ViewPager viewPager.setAdapter(pagerAdapter); viewPager.setClipToPadding(true); //Add animation when the page are swiped //this instanciation only works with honeyComb and more //if you want it all version use AnimatorProxy of the nineoldAndroid lib //@see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer //TODO uncomment those lines and the opengl bug disappears // if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){ // viewPager.setPageTransformer(true, new MyPageTransformer()); // } //AND CLUE TABLAYOUT AND VIEWPAGER tabLayout.setupWithViewPager(viewPager); } @Override protected void onStart() { super.onStart(); //track entrance Log.e(TAG, "onStart() has been called"); EventBus.getDefault().register(this); } @Override protected void onResume() { super.onResume(); //track entrance Log.e(TAG, "onResume() has been called"); } } @Override protected void onStop() { super.onStop(); //track entrance Log.e(TAG, "onStop() has been called"); EventBus.getDefault().unregister(this); } @Override public void onBackPressed() { if(((MyApplication)getApplication()).isCigaretPanelOpen){ //do nothing the fragment will just change its state battleFragment.onBack(); }else{ super.onBackPressed(); } } @Subscribe(threadMode = ThreadMode.MAIN)//EventBus public void updateUI(FullUpdateEvent event) { //TODO update properly Log.e(TAG, "fullUpdate() called with: " + "notUsed = [" + event + "]"); //rebuild every thing:$ pagerAdapter.notifyRebuildAll(); if(event.isSwitchActivity()){ switchContext(); }else{ tabLayout.setupWithViewPager(viewPager); } } /*********************************************************** * Managing menu **********************************************************/ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater=getMenuInflater(); inflater.inflate(R.menu.history_menu, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.menu_switch_context: switchContext(); break; } return super.onOptionsItemSelected(item); } /*********************************************************** * Managing backup button round trip **********************************************************/ /** * Switch context from history to current (and vis versa) * Launch the animation on the currentAnimatedVectorDrawable */ private void switchContext(){ Intent startNewContext=new Intent(this, CurrentBattleActivity.class); startNewContext.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(startNewContext); } } Au début, il y avait la classe dieu (God Class) On a une petite évol ! On va tester l'évol... Bref, on avait fait de la m***e
  115. 115. Un peu d'histoire public class HistoryBattleActivity extends AppCompatActivity { private static final String TAG = "HistoryBattleActivity"; private static final int CONTEXT_CURRENT=110274; private static final int CONTEXT_HISTORY=131274; /*********************************************************** * Attributes **********************************************************/ BattleFragment battleFragment; /** * Current context History/current */ int currentContext=CONTEXT_CURRENT; /*********************************************************** * Managing RoundTrip animation (VectorDrawable1 to VectorDrawable 2 and back again ********************************************************** /** * The LevelList that contains only two AnimatedVectorDrawable, * the ones used to go from on to the other */ LevelListDrawable backupRoundTrip; /** * The current AnimatedVector diaplsyed by the RoundTrip */ AnimatedVectorDrawable contextDrawable; /** * To know is the animation have been already launched */ boolean backupRoundTripFirstLaunched=true; /** * Historical battles */ ArrayList<Long> battlesId=null; /*********************************************************** * Attributes for the ViewPager **********************************************************/ /** * The TabLayout itself :) */ TabLayout tabLayout; /** * The page Adapter : Manage the list of views (in fact here, it's fragments) * And send them to the ViewPager */ private MyPagerAdapter pagerAdapter; /** * The ViewPager is a ViewGroup that manage the swipe from left to right to left * Like a listView with a gesture listener... */ private ViewPager viewPager; /*********************************************************** * Managing the Life cycle **********************************************************/ **********************************************************/ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e(TAG, "onCreate() called"); setContentView(R.layout.activity_history); //find the Toolbar Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); //use it as your action bar setSupportActionBar(toolbar); getSupportActionBar().setSubtitle(getString(R.string.history_fragment_subtitle)); getSupportActionBar().setTitle(getString(R.string.history_fragment_title)); tabLayout = (TabLayout) findViewById(R.id.tabLayout); //Define its gravity and its mode tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); //Define the color to use (depending on the state a different color should be disaplyed) //Works only if done before adding tabs tabLayout.setTabTextColors(getResources().getColorStateList(R.color.tab_selector_color)); //instanciate the PageAdapter pagerAdapter=new MyPagerAdapter(this,true); //Find the viewPager viewPager = (ViewPager) super.findViewById(R.id.viewpager); // Affectation de l'adapter au ViewPager viewPager.setAdapter(pagerAdapter); viewPager.setClipToPadding(true); //Add animation when the page are swiped //this instanciation only works with honeyComb and more //if you want it all version use AnimatorProxy of the nineoldAndroid lib //@see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer //TODO uncomment those lines and the opengl bug disappears // if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){ // viewPager.setPageTransformer(true, new MyPageTransformer()); // } //AND CLUE TABLAYOUT AND VIEWPAGER tabLayout.setupWithViewPager(viewPager); } @Override protected void onStart() { super.onStart(); //track entrance Log.e(TAG, "onStart() has been called"); EventBus.getDefault().register(this); } @Override protected void onResume() { super.onResume(); //track entrance Log.e(TAG, "onResume() has been called"); } } @Override protected void onStop() { super.onStop(); //track entrance Log.e(TAG, "onStop() has been called"); EventBus.getDefault().unregister(this); } @Override public void onBackPressed() { if(((MyApplication)getApplication()).isCigaretPanelOpen){ //do nothing the fragment will just change its state battleFragment.onBack(); }else{ super.onBackPressed(); } } @Subscribe(threadMode = ThreadMode.MAIN)//EventBus public void updateUI(FullUpdateEvent event) { //TODO update properly Log.e(TAG, "fullUpdate() called with: " + "notUsed = [" + event + "]"); //rebuild every thing:$ pagerAdapter.notifyRebuildAll(); if(event.isSwitchActivity()){ switchContext(); }else{ tabLayout.setupWithViewPager(viewPager); } } /*********************************************************** * Managing menu **********************************************************/ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater=getMenuInflater(); inflater.inflate(R.menu.history_menu, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.menu_switch_context: switchContext(); break; } return super.onOptionsItemSelected(item); } /*********************************************************** * Managing backup button round trip **********************************************************/ /** * Switch context from history to current (and vis versa) * Launch the animation on the currentAnimatedVectorDrawable */ private void switchContext(){ Intent startNewContext=new Intent(this, CurrentBattleActivity.class); startNewContext.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(startNewContext); } } On va découpler la vue du reste de l'application
  116. 116. Un peu d'histoire public class HistoryBattleActivity extends AppCompatActivity { private static final String TAG = "HistoryBattleActivity"; private static final int CONTEXT_CURRENT=110274; private static final int CONTEXT_HISTORY=131274; /*********************************************************** * Attributes **********************************************************/ BattleFragment battleFragment; /** * Current context History/current */ int currentContext=CONTEXT_CURRENT; /*********************************************************** * Managing RoundTrip animation (VectorDrawable1 to VectorDrawable 2 and back again ********************************************************** /** * The LevelList that contains only two AnimatedVectorDrawable, * the ones used to go from on to the other */ LevelListDrawable backupRoundTrip; /** * The current AnimatedVector diaplsyed by the RoundTrip */ AnimatedVectorDrawable contextDrawable; /** * To know is the animation have been already launched */ boolean backupRoundTripFirstLaunched=true; /** * Historical battles */ ArrayList<Long> battlesId=null; /*********************************************************** * Attributes for the ViewPager **********************************************************/ /** * The TabLayout itself :) */ TabLayout tabLayout; /** * The page Adapter : Manage the list of views (in fact here, it's fragments) * And send them to the ViewPager */ private MyPagerAdapter pagerAdapter; /** * The ViewPager is a ViewGroup that manage the swipe from left to right to left * Like a listView with a gesture listener... */ private ViewPager viewPager; /*********************************************************** * Managing the Life cycle **********************************************************/ **********************************************************/ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e(TAG, "onCreate() called"); setContentView(R.layout.activity_history); //find the Toolbar Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); //use it as your action bar setSupportActionBar(toolbar); getSupportActionBar().setSubtitle(getString(R.string.history_fragment_subtitle)); getSupportActionBar().setTitle(getString(R.string.history_fragment_title)); tabLayout = (TabLayout) findViewById(R.id.tabLayout); //Define its gravity and its mode tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); //Define the color to use (depending on the state a different color should be disaplyed) //Works only if done before adding tabs tabLayout.setTabTextColors(getResources().getColorStateList(R.color.tab_selector_color)); //instanciate the PageAdapter pagerAdapter=new MyPagerAdapter(this,true); //Find the viewPager viewPager = (ViewPager) super.findViewById(R.id.viewpager); // Affectation de l'adapter au ViewPager viewPager.setAdapter(pagerAdapter); viewPager.setClipToPadding(true); //Add animation when the page are swiped //this instanciation only works with honeyComb and more //if you want it all version use AnimatorProxy of the nineoldAndroid lib //@see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer //TODO uncomment those lines and the opengl bug disappears // if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){ // viewPager.setPageTransformer(true, new MyPageTransformer()); // } //AND CLUE TABLAYOUT AND VIEWPAGER tabLayout.setupWithViewPager(viewPager); } @Override protected void onStart() { super.onStart(); //track entrance Log.e(TAG, "onStart() has been called"); EventBus.getDefault().register(this); } @Override protected void onResume() { super.onResume(); //track entrance Log.e(TAG, "onResume() has been called"); } } @Override protected void onStop() { super.onStop(); //track entrance Log.e(TAG, "onStop() has been called"); EventBus.getDefault().unregister(this); } @Override public void onBackPressed() { if(((MyApplication)getApplication()).isCigaretPanelOpen){ //do nothing the fragment will just change its state battleFragment.onBack(); }else{ super.onBackPressed(); } } @Subscribe(threadMode = ThreadMode.MAIN)//EventBus public void updateUI(FullUpdateEvent event) { //TODO update properly Log.e(TAG, "fullUpdate() called with: " + "notUsed = [" + event + "]"); //rebuild every thing:$ pagerAdapter.notifyRebuildAll(); if(event.isSwitchActivity()){ switchContext(); }else{ tabLayout.setupWithViewPager(viewPager); } } /*********************************************************** * Managing menu **********************************************************/ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater=getMenuInflater(); inflater.inflate(R.menu.history_menu, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.menu_switch_context: switchContext(); break; } return super.onOptionsItemSelected(item); } /*********************************************************** * Managing backup button round trip **********************************************************/ /** * Switch context from history to current (and vis versa) * Launch the animation on the currentAnimatedVectorDrawable */ private void switchContext(){ Intent startNewContext=new Intent(this, CurrentBattleActivity.class); startNewContext.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(startNewContext); } } On va découpler la vue du reste de l'application Model Vue Controlleur Modifie Préviens Mets à jour Pour fêter ça, on a dansé la danse de la victoire Le modèle MVC était né!
  117. 117. Un peu d'histoire puis vint le modèle M(VC) swing Model Vue&Controller ModifieMets à jour Vue Controlleur
  118. 118. Gère les données affichées Affiche les données interagit avec l'utilisateur Un peu d'histoire le modèle M(VC) swing Model Vue&Controller 1 1
  119. 119. Un peu d'histoire le modèle M(VC) swing Gère les données affichées Model Affiche les données interagit avec l'utilisateur Vue&Controller 1 1
  120. 120. Un peu d'histoire Apparition du MVP Gère les données affichées Model Affiche les données interagit avec l'utilisateur Vue&Controller Presenter La logique Business Et les données Model View
  121. 121. Un peu d'histoire Apparition du MVP Gère les données affichées Presenter Affiche les données interagit avec l'utilisateur View La logique Business Et les données Model 1 1 ModifieMets à jour Dialogue
  122. 122. Un peu d'histoire Apparition de la programmation par contrat (Interfaces) Gère les données affichées Presenter Affiche les données interagit avec l'utilisateur View La logique Business Et les données Model 1 0 PresenterIntf ViewIntf 0 1
  123. 123. Un peu d'histoire Pour les tests !!! Model Presenter View PresenterIntf ViewIntf MockPresenter MockView
  124. 124. Saveur=TestPresenter Un peu d'histoire Avec Gradle, c'est le bonheur des tests. Saveur=TestVueSaveur=PROD Model Presenter View 0 1 MockPresenter MockViewView Presenter 0 1 0 1 MockModel
  125. 125. Un peu d'histoire Apparition du MVVM Gère les données affichées Presenter Affiche les données interagit avec l'utilisateur View La logique Business Et les données Model 1 1 ModifieMets à jour Dialogue ViewModel 1 n
  126. 126. Architecture Mais pourquoi tout ça ? Tester ! Séparer les responsabilités Réutiliser Simplifier
  127. 127. Architecture But wait ! Première remarque. MVC MVP MVVM FLUX On s'en fout !!! C'est pas ça l'important !!! C'est pareil à trois poils de *** prêt !
  128. 128. Tester ! Séparer les responsabilités Réutiliser Simplifier Architecture L'important c'est d'arriver à ça Avec le moins de complexité possible.
  129. 129. Architecture But wait ! Vous avez pas oublié un truc? Ce ne sont que des architectures de la couche VUE !!!
  130. 130. Architecture Vous avez oublié l'appli !
  131. 131. Modèle n-tier Application Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows starts starts knows View = MVP Transverse I N T F Service I N T F Communication Com D.A.O. I N T F DAO knows
  132. 132. Attention Mauvaise nomenclature La malédiction Services AndroidServices SingletonServices I N T F Service AndroidServices Service Métier Business Traitement Logique
  133. 133. Simplicité SéparationMaintenance EvolutionResponsabilités Robustesse Rappel des objectifs Test Réutilisabilité Contrat Bouchon/Mock
  134. 134. Séparation Modèle n-tier Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows knows starts starts knows View = MVP Service Communication Com D.A.O. DAO Transverse Application I N T F I N T F I N T F
  135. 135. Simplicité Modèle n-tier Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows knows starts starts knows View = MVP Service Communication Com D.A.O. DAO Transverse Application I N T F I N T F I N T F
  136. 136. Responsabilités Modèle n-tier Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows knows starts starts knows View = MVP Service Communication Com D.A.O. DAO Transverse Application I N T F I N T F I N T F
  137. 137. Contrat Modèle n-tier Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows knows starts starts knows View = MVP Service Communication Com D.A.O. DAO Transverse Application I N T F I N T F I N T F
  138. 138. Réutilisabilité Modèle n-tier Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows knows starts starts knows View = MVP Service Communication Com D.A.O. DAO Transverse Application I N T F I N T F I N T F
  139. 139. Modularité Modèle n-tier Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows knows starts starts knows View = MVP Service Communication Com D.A.O. DAO Transverse Application I N T F I N T F I N T F
  140. 140. Evolution Modèle n-tier Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows knows starts starts knows View = MVP Service Communication Com D.A.O. DAO Transverse Application I N T F I N T F I N T F
  141. 141. Modèle n-tier BroadcastReceiver Communication Com D.A.O. DAO ExceptionManagerTools Tester Tests JUNIT Tests Tests Transverse Transverse
  142. 142. Modèle n-tier Services AndroidServices SingletonServices Service knows Communication Com I N T F knows D.A.O. DAO I N T F Tests JUNIT Tester
  143. 143. Modèle n-tier MockView View = MVP Presenter ViewIntf 1 1 JUNIT Services AndroidServices SingletonServices Service I N T F Tests Tester
  144. 144. Modèle n-tier View = MVP Presenter View 1 1 Services AndroidServices SingletonServices Service I N T F Espresso AndroidTest SDK Tests Tester
  145. 145. Testabilité complète Modèle n-tier Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows knows starts starts knows View = MVP Service Communication Com D.A.O. DAO Transverse Application
  146. 146. Maintenabilité Robustesse Modèle n-tier Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows knows starts starts knows View = MVP Service Communication Com D.A.O. DAO Transverse Application
  147. 147. Modèle n-tier Service Vues COM DAO Transverse Use case 1 Use case 2 Use case 3 Architecture en couches Architecture en sillots On s'en fout !!! On fait les deux. Naturellement.
  148. 148. Modèle n-tier Architecture en couches Architecture en sillots Service Vues COM DAO Transverse Use case 1 Use case 2 Use case 3
  149. 149. Modèle n-tier Architecture de la vraie vie Service Vues COM DAO Transverse Use case 1 Use case 2 Use case 3 On commence par un use case et après on réutilise dès qu'on peut
  150. 150. Aparté sur les Vues
  151. 151. Vues 163 Cohérence Rangez votre package view ! Soyez cohérent avec votre application
  152. 152. Vues 164 Rangez votre package view ! Un sous package par fragment Un sous package par composant graphique complexe de l'activité Au root du package, l'activité et son presenter Un package par vue
  153. 153. Limiter les dépendances 165 Couche IHM HumanViewParentIntf HumanPresParentIntf FamilyView FamilyPresenter 1 1 HumanView HumanPresenter 1 1 use use Vues Vue contenue et réutilisable Utilisez des interfaces Couche IHM HumanViewParentIntf HumanPresParentIntf FamilyActivity FamilyPresenter 1 1 HumanFragment HumanPresenter 1 1 use use
  154. 154. Fin de l'aparté
  155. 155. MVP + N-Tier mettre en place le principe de séparation des responsabilités Etre testable unitairement simplement permettre de s'adapter aux changements du framework Synthèse Sans impact, bref d'être indépendant de l'implémentation et de pouvoir la modifier et la tester permettre de faire évoluer les UI, la BD,... permettre de changer de librairies
  156. 156. Architecture But wait ! C'est pas un peu compliqué ton truc? et c'est que le début.
  157. 157. 10 Minutes

Notes de l'éditeur

  • desactive ton telephone
    fais un selfie
  • Le contexte = pénurie et vie en collectivité d'enfants
    5 minutes
  • bref chet haase stuff
  • Une question simple
    Une réponse complexe qui dépend du contexte
  • Une question simple
    Une réponse complexe qui dépend du contexte
  • Il ne faut jamais ignoré le contexte et celui ci peut être multiple (a plusieurs niveaux)
  • Memory c'est les perfs: plus on alloue, plus on désalloue, plus on fait marcher le GC & plus on stress le CPU
    Memory: c'est aussi plus tu en prends moins tu en laisse aux autres
    Memory: les images
    => entraine des bonnes pratiques de rédaction de code spécifique à Android
  • Memory c'est les perfs: plus on alloue, plus on désalloue, plus on fait marcher le GC & plus on stress le CPU
    Memory: c'est aussi plus tu en prends moins tu en laisse aux autres
    Memory: les images
    => entraine des bonnes pratiques de rédaction de code spécifique à Android
  • Memory c'est les perfs: plus on alloue, plus on désalloue, plus on fait marcher le GC & plus on stress le CPU
    Memory: c'est aussi plus tu en prends moins tu en laisse aux autres
    Memory: les images
    => entraine des bonnes pratiques de rédaction de code spécifique à Android
  • Memory c'est les perfs: plus on alloue, plus on désalloue, plus on fait marcher le GC & plus on stress le CPU
    Memory: c'est aussi plus tu en prends moins tu en laisse aux autres
    Memory: les images
    => entraine des bonnes pratiques de rédaction de code spécifique à Android
    Multi processus utilise la mémoire (si ils en utilise ou en garde) c'est le drame (LRU cach strategy tout ça)
  • Et pire la réalité pour nos utilisateur, ils ont des appareils pourris ! Il faut tester sur des appareils pourris aussi
    et dans ces 4Go il faut y mettre le système, facebook, les photo de l'utilisateur et ses jeux, ses applis, ses chansons...
  • 1)We have the slower CPU of the world and our users slowest
    2)Stressing the CPU drains the battery. System anwser: down clocking the CPU (except if you check the device, runs animations permanently,
    3)GPU is the same (Bitmaps loading).
  • 1)We have the slower CPU of the world and our users slowest
    2)Stressing the CPU drains the battery. System anwser: down clocking the CPU (except if you check the device, runs animations permanently,
    3)GPU is the same (Bitmaps loading).
  • 1)We have the slower CPU of the world and our users slowest
    2)Stressing the CPU drains the battery. System anwser: down clocking the CPU (except if you check the device, runs animations permanently,... => you get the CPU at its max speed => drain the battery)
    3)GPU is the same (Bitmaps loading).
  • Votre problème principal car c'est celui de votre utilisateur
  • Qui parmis vous possède un chargeur ? un chargeur portable ?
  • Votre problème principal car c'est celui de votre utilisateur
  • Les bonnes pratiques de code et d'architecture
  • C'est pour ça que vous êtes là non ? évitez tout ça :)
  • Votre problème principal car c'est celui de votre utilisateur
  • La vie en collectivité: Dès que l'un en fait trop cela impacte les autres,
    Dès que l'un tue la batterie pour son propre confort, c'est l'appareil qui parait pourri
    Dès que l'un prend trop de ressources, ce sont les autres qui en manquent
    Si vous en avez trop pris, votre vie dans le LRUCach sera limité, vous ne resterez pas longtemps => votre app prendra du temps à se re-lancer
    Ne te penses pas plus important que ne l'est.
  • Chacun agit celon son propre besoin, ses propres responsabilités, aux mieux pour lui mais pas au mieux pour tous.
    Aucune app n'est responsable de l'apparente pietre qualité du device, si on les analyse, rien n'en sortira.
    C'est la somme des agissements de chacun, c'est chaque détail, qui ferra qu'à la fin de la journée le device sera épuisé
    (exemple l'over sync de toutes les applications si il y en a 100, à 14h plus de batterie...)
  • 5 minutes
  • fluide => toutes les apps cohabitent
    Les super pouvoirs== cadeau des dieux grecs (photo du monde, communication à des distances de dingues, voir, écouter, apprendre, être en lien avec des gens lointains...)
  • 60 fois par seconde une fenetre s'ouvre pour redessiner l'écran, si vous n'êtes pas là, pas pret, vous ratez la fenetre, la prochaine est dans 16 ms...
    C'est une de vous plus grosses contrainte
  • Freeze every body to work
    10-20 ms facile avec des super fast device (Dalvik)
    2-3mps avec ART
    More object (=> avoid allocating) more complex more long
    Et surtout tu rates ta fenetre des 16ms pour etre redessiner, imagine ce que ca donne durant une animation.
    Pour les ListView c'était super important

  • Moi, ça m'est arrivé souvent d'avoir le not enough space
    Est-ce que quelqu'un parmis vous à déjà implémenté et utilisé ce code?
  • Dessin de RetoMeier GoogleIo2012
    Utilisation de la big cookies startegy (on y reviendra)
  • Dessin de RetoMeier GoogleIo2012
    Utilisation de la big cookies startegy (on y reviendra)
  • 10 minutes
  • Laissez les gens rencontrer votre application. Le schema de Damien Mabin était super DroidConGreece
  • 10 minutes
  • Connaitre, comprendre, accepter le monde Android et ses contraintes, évoquées slides précédents
  • Tu es au service de l'utilisateur, on ne l'oblige pas, on lui parle avec cordialité, on ne le frustre pas tout ça tout ça
    et on le respecte. On est tous d'accord?
    Du coup qui a déjà codé ces balises ?
    Vous avez déjà codé pour être compliant avec l'accessibilité ?
  • L'histoire du startup we, premier reflexe, on va vendre nos données utilisateurs
    Protéger les données de vos utilisateurs
  • L'histoire du startup we, premier reflexe, on va vendre nos données utilisateurs
    Protéger les données de vos utilisateurs
  • drawable-xhdpi
    layout-small
  • Souvenez vous dans le désert
    Il y aussi Google et facebook qui partent faire des tests dans des zones inaccessibles
    Il y a aussi enoirmenet d'adaptation à ce facteur dans les différents
  • Ca va beaucoup mieux avec la SupportLibrary mais c'est pas encore ça
    Les animations par exemple
  • Vous n'êtes pas à l'abris des bugs spécifiques
  • 30 minutes
  • Il y a les miennes et celles de chet
  • Parce que sans de bonnes pratiques de code vous n'arriverez pas à atteindre ces objectifs
    C'est dans les détails que le diable se cache et c'est pour ça que c'est bonne pratique sont importantes
  • Votre utilisation de la mémoire est le point le plus important pour voir a quel point l'application se comporte bien.
    (taille de l'empreinte mémoire ainsi que sa gestion (et donc le passage de GC)
  • Il y a des endroits de code (getView, onDraw) où il ne faut ni déclarer ni allouer d'objets
    Mettre les objets en cach permet de ne pas les réallouer n fois
    Au besoin la mise en place d'un Pool d'objets peut-être la bonne solution
    Mettre des variables temporaires en variable de classe (static) [ListView ArrayAdapter.getView]
    Les HashMap ne sont pas a utiliser
    Définissez la taille des ArrayList si vous pouvez, evite la réallocation en doublant l'espace mem+la recopie des données

  • Pas d'itérateurs ca créé un objet (et alors ?) du coup for(Object o:toto){for(AnotherObject ao:o.truc())} c'est allouer un autre objet dans un objet
    Les énumérations requierent plus d'espace pour implémenter de simples constantes primitives, il vaut mieux des constantes directement. Ca prend trop de place, il ne faut surtout pas dire que c'est une bonne pratique, car tout avec des enums et c'est la mort mémoire
    Les librairies, c'est clair.
    public static c'est Romain Guy au Paug
    C'est une philosophie sous Android de n'initialiser les objet qu'a la demande pour éviter des temps de demarrage digne d'eclipse.
    Cela signifie qu'il est mieux parfois de passer en paramètre le retour de la méthode Rect getRect() =>getRect(Rect return)



  • Les services: l'exemple du scruteur de changement de connnectivité réseau au lieux de la mise en place d'un broadcastreceiver
    onTrimMemory (api level 14) vous appele quand on vous demande liberez de la memoire le systeme n'en peut plus.=> exemple de Bitmap dans le LruCach
  • Les services: l'exemple du scruteur de changement de connnectivité réseau au lieux de la mise en place d'un broadcastreceiver
    onTrimMemory (api level 14) vous appele quand on vous demande liberez de la memoire le systeme n'en peut plus.=> exemple de Bitmap dans le LruCach
  • Très utile pour produire différents apk avec un if else à la compilation:
    Pour test/prod
    Différente densité == économie de taille d'apk avec les images
    Différentes version pour une gestion spécifique (post hc et pre hc
    Différents processeurs (intel, amd...
  • Measure and Layout : happen in the ui threa d and it's time consuming (depends on view complexity)
    Inflation: same
    Init App: rallonge le temps de lancement (dans oncreate) c'est dans l'uithread
    Drawing the layout is a two pass process:
    measuring pass - implemented in the measure(int, int) method and is a top-down traversal of the view hierarchy. Every view stores its measurements.
    layout pass - implemented in the layout(int, int, int, int) method is also a top-down traversal of the view hierarchy. During this phase each layout manager is responsible for positioning all of its children. It uses the sizes computed in the measure pass.
    Note
     The measure and layout step always happen together.
    Note
     Layout managers can run the measure pass several times. For example LinearLayout supports the weight attribute which distributes the remaining empty space among views and RelativeLayout measures child views several times to solve constraints given in the layout file.
    A view or activity can retrigger the measure and layout pass with a call to the requestLayout() method.
  • Complexité augmente le nombre de passage (complexité dépend de la profondeur)
    RelativeLayout jamais en début de fichier !! double le nombre de passe necessaire
  • Pas d'animations, soit on les fait avant et on inflate/change les view, soit on le fait après.
    Préferrez les animations qui ne change pas le layout (Layout.param non,non, non, mais TranslationX, ... oui oui oui)
    Remplacer
    CutomView: Exemple de facebook dans ses listes, et de manière générale on fige les dimensions plus vite avec un passage unique.
    La starting Window: Utilise le theme de l'application pour afficher une fenêtre de démarrage.
  • Ils te permettent de simplifier tes layouts (dont l'objectif est le placement des composants)
    Ils te permettent de centraliser les caractéristiques graphiques, de les organiser et de les classer
    Ils te permettent ^d'être cohérent globlement et de facilement évoluer
  • Ils te permettent de simplifier tes layouts (dont l'objectif est le placement des composants)
    Ils te permettent de centraliser les caractéristiques graphiques, de les organiser et de les classer
    Ils te permettent ^d'être cohérent globlement et de facilement évoluer
  • Ils te permettent de simplifier tes layouts (dont l'objectif est le placement des composants)
    Ils te permettent de centraliser les caractéristiques graphiques, de les organiser et de les classer
    Ils te permettent ^d'être cohérent globalement et de facilement évoluer
  • OverDraw: Redessiner plusieurs fois un même pixel (Debug GPU overDraw)
    Pour la starting window c'est le blog de cyril sur le démarage des apps et sur l'overdraw
    Pour la context, on ne peut pas utiliser le context app pour créer du GUI car il lui manque le thème/ idem pour la récupération de ressources
    Les vues ne sont pas connues des Threads, sinon fuite mémoire potentielle
  • GCM manager pour les synchronisation cliente
    GCM pour que le serveur previenne le client
  • Parcelable pas d'enregistrement sur disque: Le mieux c'est Json + zip
    Sérialisation elle stocke trop d'informations pour assurer une persistance disque ad vitam eternae, trop lente, à proscrire.
  • Ici MaConstante ne sera jamais GC, vu qu'elle implicitement retient l'instance de la classe qui l'a créé la première fois, c'est mort, fuite mémoire de la taille d'une activité
  • Donnez les PoolExecutor que tu utilises (1 pour la BD, 4 pour le reste cancelable, 4 pour le non cancelable)
  • Les classes internes non statiques conservent une référence vers leur classe englobante.
  • Les classes internes non statiques conservent une référence vers leur classe englobante.
  • Les classes internes non statiques conservent une référence vers leur classe englobante.
  • Drawable pointe implicitement vers lActivité qui l'a instancié, donc le mettre en statique pour sauver le temps de chargmenet du bitmap lors de la rotation est une erreur horrible. car on retient toujours la première instance de l'activité
  • Ici MaConstante ne sera jamais GC, vu qu'elle implicitement retient l'instance de la classe qui l'a créé la première fois, c'est mort, fuite mémoire de la taille d'une activité
  • Don't fuck around== Chaque brique du système possède une semantique très precise, il faut l'utiliser en coherence avec cette semantique.

    Service is how an application indicates to the operating system that it needs to perform a longer-running operation outside of the normal Activity UI flow. If you do not need either of these behaviors (Bound/Start), you should not use a Service.
    For example, if you have background work to do that does not need to keep itself running (such as downloading content for your UI that you could resume later when the user returns to the UI) you should use local threading primitives such as AsyncTask, Loader, HandlerThread, etc. Using a Service is much more resource-intensive (as your service needs to be tracked as part of the global system state)
    Le mot service prête a confusion !!

    Utiliser un broadcast pour écvouter un event plutot qu'un service qui tourney sans arret.
  • systrace= voir combine de temps on passe dans une méthode
    Analyse des écrans: Animations, View hierarchy, Pixel perfect, overdraw,
  • 20 minutes
  • Explication de chaque modèle via un schéma pour chacun
  • La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau... 100 000 lignes de code, aucun tests
  • La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  • Et on a inventé le MVC : decouple la vue, de l'interaction, de la representation.
  • La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  • La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  • On a relié View et Controller, c'est là qu'on a mis les listener dans les vues
  • La liaison est 1-1, on sait alors que c'est du pipo pour l'rdi que c'est fait pour les humains et pour les tests
  • La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  • La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  • La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  • La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  • La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  • Grace a gradle et aux saveurs on peut automatiser les confs poru executer les tests
  • Et aussi il y a du DataBinding
  • J'ai vu des flux tweeter où les gars s'engueulaient pedant 500 tweets sur le sujet (mais tu fais du MVPM toi, ... blazbla)
  • Principe de separation des couches, des responsabilités
    Isolation des composants = Interfaces
    Chaque bloc est indépendant des autres
    L'importance des flêches de dependence. Qui connait qui.?
    n-tier== n niveau logique (tier=niveau logique)
  • Les services métiers ne sont pas des services Android
    ce sont les traitements métiers, la couche business/métier
    Les services Android c'est une toute autre notion
  • On sépare le code, on le ventile, on l'aère
  • A scindé en plein de partie, c'est globalement compliqué, mais localement simple
  • Chaque couche est responsable d'une partie precise de l'application: Algorithme, HTTP, DAO, Vues...
  • Car chaque couche/composant possède un contrat très précis décrit par son interface
  • Car modulaire
  • Car modulaire
  • Car ils ne dependent de personne
  • JUnit et bouchons
  • Les tests apportent la sécurité pour le projet
  • Les tests apportent la sécurité pour le projet
  • La couche IHM contient l’ensemble des écrans et se scinde en plusieurs sous-packages.
    Une application hiérarchise ses vues, cette hiérarchie doit se retrouver dans vos packages.
  • La couche IHM contient l’ensemble des écrans et se scinde en plusieurs sous-packages.
    Une application hiérarchise ses vues, cette hiérarchie doit se retrouver dans vos packages.

×