SlideShare une entreprise Scribd logo
1  sur  157
@android2ee
by(Mathias Seguy== Android2EE){
French AndroidTrainer}
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
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
Pénurie
Vie en collectivité
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
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
Le contexte mémoire sous Android
La mémoire
La mémoire
L'eau c'est la vie !
Pas sur Android
La mémoire c'est la vie !
La mémoire
L'eau c'est la vie !
Pas sur Android
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
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)
Le contexte CPU/GPU sous Android
Le CPU et GPU
Dans la vrai vie
Chaque utilisateur possède un CPU différent (tout pourri)
Dans tes rêves
Le CPU et GPU
Le CPU et GPU
CPU/GPU
==
Videur de Batterie
Le contexte énergie sous Android
L'énergie
L'énergie
Ce que l'on pense/espère avoir
L'énergie
Ce que nos utilisateurs ont vraiment
L'énergie
Votre problème principal
L'énergie
Les causes du gaspillage énergétique
CPU/GPU
Connexion (Http,...)
Drawable/Animations...
Mauvaises pratiques
de code
Services sans fin
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 !!!)
Le contexte collectif sous Android
Vie en collectivité
Le contexte mutualiste sous Android
La vie en collectivité
gmail
twitter
hangout
evernote
messsager
facebook
gmap
linkedIn
runstatic
your app
chrome
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
Complexité
Contraintes
60 chances par seconde toutes les 16ms
60 fps
Aucun traitement dans l'UI Thread,
jamais, aucun.
UI thread
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)
Changer votre vision de la Base de données,
la boite est trop petite.
Persistence
Communication réseau: principale cause de drainage de la batterie.
Réseau
Communication réseau: La big cookies startegy.
Réseau
Ne mettez pas à jour trop souvent, vous n'êtes pas seul !
Don't oversync.
Réseau
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
Plaisir
Usability
(Convivialité & utilisabilité)
L'expérience utilisateur
le plaisir
Combler un besoin utilisateur.
Je veux faire un truc
et je peux!
Avec efficacité.
Quand je le fais,
ça marche!
L'utilisateur doit toujours savoir ce qu'il fait, où il est.
Je sais ce que je fais !
Et quand il le fait, c'est facile, simple et bluffant.
C'est facile et c'est
fluide
Et grâce à vous, il possède des capacités qu'il n'avait pas.
ça me donne des super
pouvoirs!
Ne frustrer pas votre utilisateur, laissez le libre.
Sans authentification,
ni paiement.
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
L'expérience utilisateur
Convivialité
et j'ai utilisé mon téléphone !
J'ai encore de la batterie
à 18h
ou pas...
Je le fais d'un doigt
Spéciale dédicace à la RATP!
Ca marche même hors
ligne
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
ç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 !
L'expérience utilisateur
Utilisabilité
Les animations amènent la compréhension.
Animation
L'information au bon moment.
Notifications
Suivez le guide !
Material Design
https://www.google.com/design/spec/material-design/introduction.html
Soyez Asynchrone !
Toujours Asynchrone !
Asynchrone
Respect
Adaptation
Des contraintes du système.
Respect
De l'utilisateur
Respect
android:contentDescription
public class MyAccessibilityService
extends AccessibilityService
et de ses données.
Respect
et de ses données.
Respect
On va vendre les
données utilisateur.
Respect
Des bonnes pratiques de dèv.
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
Aux écrans
(tailles et densités).
Adaptation
A la puissance des processeurs.
Adaptation
Aux tailles mémoires.
Adaptation
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>
A la bande passante.
Adaptation
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
Aux constructeurs
Adaptation
https://code.google.com/p/android/issues/detail?id=78377
Des habitudes
Des Design Patterns
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 ?
N'allouez pas d'objets.
Mémoire
N'allouez pas d'objets.
Mémoire
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
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
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
N'allouez pas d'objets.
Mémoire
Utilisez la SupportLibrary.
Adaptation
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
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
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
A la compilation: Gradle et Saveur (Flavor)
Adaptation
Gradle
Code
prod
screendensity
processeur
versions
staging/test
Saveur
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é
Les opérations UI Thread coûteuses.
UI Performance
Mesurer et Disposer.
Measure and Layout
Inflation. Initialisation de l'objet
Application.
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.
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.
90
UI Pro Tips
Les styles sont tes meilleurs amis.
!--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.
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();
}
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
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).
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
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...).
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
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...
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.
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.
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);
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}
Authentification
Toujours authentifier vos
utilisateurs
UUID SharedPreferences
SharedPreferences
BackupHelper
Exception
Gérer vos exceptions !
Exception
ManagedException
isManaged
UserId
Error message
Technical data
Functional data
Others stuff
Exception
Gérer vos exceptions !
Exception Manager
ExceptionManager
.manage(...)
Team FeedBackUser FeedBack
SomeWhere in your code galaxy
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
/** * 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
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
Memory Leak
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
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
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
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
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
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){...}
}
}
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.
Analysez
Utilisation mémoire.
Analysez votre
application.
Utilisation réseau.
Utilisation CPU/GPU.
Trace system
(sysTrace).
Analyse des écrans.
n-tiers
MVP ou MVVM
Architecture: Objectif
Simplicité
Tests
SéparationMaintenance
EvolutionResponsabilités
Tests unitaires Tests Intégration Tests Fonctionnels
Robustesse
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
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
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é!
Un peu d'histoire
puis vint le modèle M(VC) swing
Model
Vue&Controller
ModifieMets à jour
Vue Controlleur
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
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
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
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
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
Un peu d'histoire
Pour les tests !!!
Model
Presenter
View
PresenterIntf
ViewIntf
MockPresenter
MockView
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
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
Architecture
Mais pourquoi tout ça ?
Tester !
Séparer les
responsabilités
Réutiliser Simplifier
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 !
Tester !
Séparer les
responsabilités
Réutiliser Simplifier
Architecture
L'important c'est d'arriver à ça
Avec le moins de complexité possible.
Architecture
But wait !
Vous avez pas oublié un truc?
Ce ne sont que des architectures de la couche
VUE !!!
Architecture
Vous avez oublié l'appli !
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
Attention
Mauvaise nomenclature
La malédiction
Services
AndroidServices
SingletonServices
I
N
T
F
Service
AndroidServices
Service Métier Business Traitement Logique
Simplicité SéparationMaintenance
EvolutionResponsabilités Robustesse
Rappel des objectifs
Test
Réutilisabilité Contrat Bouchon/Mock
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
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
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
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
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
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
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
Modèle n-tier
BroadcastReceiver
Communication
Com
D.A.O.
DAO
ExceptionManagerTools
Tester
Tests
JUNIT
Tests
Tests
Transverse
Transverse
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
Modèle n-tier
MockView
View = MVP
Presenter
ViewIntf
1
1
JUNIT
Services
AndroidServices
SingletonServices
Service
I
N
T
F
Tests
Tester
Modèle n-tier
View = MVP
Presenter
View
1
1
Services
AndroidServices
SingletonServices
Service
I
N
T
F
Espresso
AndroidTest SDK
Tests
Tester
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
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
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.
Modèle n-tier
Architecture en
couches
Architecture en
sillots
Service
Vues
COM
DAO
Transverse
Use
case
1
Use
case
2
Use
case
3
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
Aparté sur les Vues
Vues
163
Cohérence
Rangez votre package view !
Soyez cohérent avec votre
application
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
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
Fin de l'aparté
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
Architecture
But wait !
C'est pas un peu compliqué ton truc?
et c'est que le début.
10 Minutes

Contenu connexe

En vedette

Animate Me! if you don't do it for me, do it for Chet - DroidconLondon2015
Animate Me! if you don't do it for me, do it for Chet - DroidconLondon2015Animate Me! if you don't do it for me, do it for Chet - DroidconLondon2015
Animate Me! if you don't do it for me, do it for Chet - DroidconLondon2015Mathias Seguy
 
The 2016 Android Developer Toolbox [NANTES]
The 2016 Android Developer Toolbox [NANTES]The 2016 Android Developer Toolbox [NANTES]
The 2016 Android Developer Toolbox [NANTES]Nilhcem
 
Voyage en monde Android. Trucs et astuces tout au long de la route.
Voyage en monde Android. Trucs et astuces tout au long de la route.Voyage en monde Android. Trucs et astuces tout au long de la route.
Voyage en monde Android. Trucs et astuces tout au long de la route.Mathias Seguy
 
Animate Me, if you don't do it for me do it for chet (DroidCon Paris)
Animate Me, if you don't do it for me do it for chet (DroidCon Paris)Animate Me, if you don't do it for me do it for chet (DroidCon Paris)
Animate Me, if you don't do it for me do it for chet (DroidCon Paris)Mathias Seguy
 
Android2EE training: Tutorials list of Android projects
Android2EE training: Tutorials list of Android projectsAndroid2EE training: Tutorials list of Android projects
Android2EE training: Tutorials list of Android projectsMathias Seguy
 
Google Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification GoogleGoogle Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification GoogleMathias Seguy
 
Eclispe daytoulouse combining the power of eclipse with android_fr_1024_768_s...
Eclispe daytoulouse combining the power of eclipse with android_fr_1024_768_s...Eclispe daytoulouse combining the power of eclipse with android_fr_1024_768_s...
Eclispe daytoulouse combining the power of eclipse with android_fr_1024_768_s...Mathias Seguy
 
Mise en place de l'ActionBarCompat dans vos projets Android.
Mise en place de l'ActionBarCompat dans vos projets Android.Mise en place de l'ActionBarCompat dans vos projets Android.
Mise en place de l'ActionBarCompat dans vos projets Android.Mathias Seguy
 
Animate me, If you don't do it for me do it for Chet :)
Animate me, If you don't do it for me do it for Chet :)Animate me, If you don't do it for me do it for Chet :)
Animate me, If you don't do it for me do it for Chet :)Mathias Seguy
 
Applications Mobiles - Bonnes pratiques de conception et de développement de ...
Applications Mobiles - Bonnes pratiques de conception et de développement de ...Applications Mobiles - Bonnes pratiques de conception et de développement de ...
Applications Mobiles - Bonnes pratiques de conception et de développement de ...BEIJAFLORE
 
In01 - Programmation Android - 01 - introduction
In01 - Programmation Android - 01 - introductionIn01 - Programmation Android - 01 - introduction
In01 - Programmation Android - 01 - introductionYann Caron
 
Chp1 - Introduction au Développement Mobile
Chp1 - Introduction au Développement MobileChp1 - Introduction au Développement Mobile
Chp1 - Introduction au Développement MobileLilia Sfaxi
 
Introduction To Mobile Application Development
Introduction To Mobile Application DevelopmentIntroduction To Mobile Application Development
Introduction To Mobile Application DevelopmentSyed Absar
 
Android Application Development
Android Application DevelopmentAndroid Application Development
Android Application DevelopmentBenny Skogberg
 
Mobile App Development
Mobile App DevelopmentMobile App Development
Mobile App DevelopmentChris Morrell
 

En vedette (20)

Animate Me! if you don't do it for me, do it for Chet - DroidconLondon2015
Animate Me! if you don't do it for me, do it for Chet - DroidconLondon2015Animate Me! if you don't do it for me, do it for Chet - DroidconLondon2015
Animate Me! if you don't do it for me, do it for Chet - DroidconLondon2015
 
The 2016 Android Developer Toolbox [NANTES]
The 2016 Android Developer Toolbox [NANTES]The 2016 Android Developer Toolbox [NANTES]
The 2016 Android Developer Toolbox [NANTES]
 
Voyage en monde Android. Trucs et astuces tout au long de la route.
Voyage en monde Android. Trucs et astuces tout au long de la route.Voyage en monde Android. Trucs et astuces tout au long de la route.
Voyage en monde Android. Trucs et astuces tout au long de la route.
 
Animate Me, if you don't do it for me do it for chet (DroidCon Paris)
Animate Me, if you don't do it for me do it for chet (DroidCon Paris)Animate Me, if you don't do it for me do it for chet (DroidCon Paris)
Animate Me, if you don't do it for me do it for chet (DroidCon Paris)
 
Android2EE training: Tutorials list of Android projects
Android2EE training: Tutorials list of Android projectsAndroid2EE training: Tutorials list of Android projects
Android2EE training: Tutorials list of Android projects
 
Google Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification GoogleGoogle Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification Google
 
Eclispe daytoulouse combining the power of eclipse with android_fr_1024_768_s...
Eclispe daytoulouse combining the power of eclipse with android_fr_1024_768_s...Eclispe daytoulouse combining the power of eclipse with android_fr_1024_768_s...
Eclispe daytoulouse combining the power of eclipse with android_fr_1024_768_s...
 
Bonnes pratiques développement android
Bonnes pratiques développement androidBonnes pratiques développement android
Bonnes pratiques développement android
 
Mise en place de l'ActionBarCompat dans vos projets Android.
Mise en place de l'ActionBarCompat dans vos projets Android.Mise en place de l'ActionBarCompat dans vos projets Android.
Mise en place de l'ActionBarCompat dans vos projets Android.
 
Animate me, If you don't do it for me do it for Chet :)
Animate me, If you don't do it for me do it for Chet :)Animate me, If you don't do it for me do it for Chet :)
Animate me, If you don't do it for me do it for Chet :)
 
Applications Mobiles - Bonnes pratiques de conception et de développement de ...
Applications Mobiles - Bonnes pratiques de conception et de développement de ...Applications Mobiles - Bonnes pratiques de conception et de développement de ...
Applications Mobiles - Bonnes pratiques de conception et de développement de ...
 
In01 - Programmation Android - 01 - introduction
In01 - Programmation Android - 01 - introductionIn01 - Programmation Android - 01 - introduction
In01 - Programmation Android - 01 - introduction
 
Android ppt
Android pptAndroid ppt
Android ppt
 
Chp1 - Introduction au Développement Mobile
Chp1 - Introduction au Développement MobileChp1 - Introduction au Développement Mobile
Chp1 - Introduction au Développement Mobile
 
Introduction To Mobile Application Development
Introduction To Mobile Application DevelopmentIntroduction To Mobile Application Development
Introduction To Mobile Application Development
 
Android Application Development
Android Application DevelopmentAndroid Application Development
Android Application Development
 
Java Tutorial
Java TutorialJava Tutorial
Java Tutorial
 
Business Plan d’une application mobile
Business Plan d’une application mobileBusiness Plan d’une application mobile
Business Plan d’une application mobile
 
Slideshare android
Slideshare androidSlideshare android
Slideshare android
 
Mobile App Development
Mobile App DevelopmentMobile App Development
Mobile App Development
 

Similaire à Architecture et Bonnes pratiques Android #DevoxxFr2016 Part1

The worst practices for Magento
The worst practices for MagentoThe worst practices for Magento
The worst practices for MagentoLe Bot Christophe
 
Cocoaheads Rennes #3 : Bien coder sur iOS
Cocoaheads Rennes #3 : Bien coder sur iOSCocoaheads Rennes #3 : Bien coder sur iOS
Cocoaheads Rennes #3 : Bien coder sur iOSCocoaHeadsRNS
 
Le développement côté utilisateur
Le développement côté utilisateurLe développement côté utilisateur
Le développement côté utilisateurDavid Buros
 
wordcamp biarritz 2.pdf
wordcamp biarritz 2.pdfwordcamp biarritz 2.pdf
wordcamp biarritz 2.pdfbacile1
 
Diapo fête de la science 2011
Diapo fête de la science 2011Diapo fête de la science 2011
Diapo fête de la science 2011cbbpn
 
JavaScript Devoxx France 2013
JavaScript Devoxx France 2013JavaScript Devoxx France 2013
JavaScript Devoxx France 2013Romain Linsolas
 
Lbv Dev Meetup #3
Lbv Dev Meetup #3Lbv Dev Meetup #3
Lbv Dev Meetup #3LbvDev
 
Concevoir une expérience SoLoMo (sociale, locale, mobile): retours d'expérien...
Concevoir une expérience SoLoMo (sociale, locale, mobile): retours d'expérien...Concevoir une expérience SoLoMo (sociale, locale, mobile): retours d'expérien...
Concevoir une expérience SoLoMo (sociale, locale, mobile): retours d'expérien...Microsoft
 
12 bonnes raisons de ne pas contrôler vos applications
12 bonnes raisons de ne pas contrôler vos applications12 bonnes raisons de ne pas contrôler vos applications
12 bonnes raisons de ne pas contrôler vos applicationsSolutions IT et Business
 
Java in docker devoxx ma 2018
Java in docker devoxx ma 2018Java in docker devoxx ma 2018
Java in docker devoxx ma 2018kanedafromparis
 
Télés connectées et développement Web
Télés connectées et développement WebTélés connectées et développement Web
Télés connectées et développement WebJean-Pierre Vincent
 
Du JavaScript propre ? Challenge accepted ! @Devoxx France 2013
Du JavaScript propre ? Challenge accepted ! @Devoxx France 2013Du JavaScript propre ? Challenge accepted ! @Devoxx France 2013
Du JavaScript propre ? Challenge accepted ! @Devoxx France 2013Julien Jakubowski
 
VDLT - Retour DevFest 2023
VDLT - Retour DevFest 2023VDLT - Retour DevFest 2023
VDLT - Retour DevFest 2023SpikeeLabs
 
.NET Microframework: du code, de l&rsquo;électronique, de la robotique
.NET Microframework: du code, de l&rsquo;électronique, de la robotique.NET Microframework: du code, de l&rsquo;électronique, de la robotique
.NET Microframework: du code, de l&rsquo;électronique, de la robotiqueMicrosoft
 
Crash Test Your Idea Meetup Valtech 13/09/2016
Crash Test Your Idea Meetup Valtech 13/09/2016Crash Test Your Idea Meetup Valtech 13/09/2016
Crash Test Your Idea Meetup Valtech 13/09/2016André De Sousa
 
2012 11 Toulibre - Open Hardware
2012 11 Toulibre - Open Hardware2012 11 Toulibre - Open Hardware
2012 11 Toulibre - Open HardwareSylvain Wallez
 
Tirer parti des périphériques mobiles dans une application web : qui a dit qu...
Tirer parti des périphériques mobiles dans une application web : qui a dit qu...Tirer parti des périphériques mobiles dans une application web : qui a dit qu...
Tirer parti des périphériques mobiles dans une application web : qui a dit qu...Christophe Porteneuve
 

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

The worst practices for Magento
The worst practices for MagentoThe worst practices for Magento
The worst practices for Magento
 
Drupal & Mobilité
Drupal & MobilitéDrupal & Mobilité
Drupal & Mobilité
 
Cocoaheads Rennes #3 : Bien coder sur iOS
Cocoaheads Rennes #3 : Bien coder sur iOSCocoaheads Rennes #3 : Bien coder sur iOS
Cocoaheads Rennes #3 : Bien coder sur iOS
 
Le développement côté utilisateur
Le développement côté utilisateurLe développement côté utilisateur
Le développement côté utilisateur
 
wordcamp biarritz 2.pdf
wordcamp biarritz 2.pdfwordcamp biarritz 2.pdf
wordcamp biarritz 2.pdf
 
Atelier WhatsApp et Scan
Atelier WhatsApp et ScanAtelier WhatsApp et Scan
Atelier WhatsApp et Scan
 
Diapo fête de la science 2011
Diapo fête de la science 2011Diapo fête de la science 2011
Diapo fête de la science 2011
 
JavaScript Devoxx France 2013
JavaScript Devoxx France 2013JavaScript Devoxx France 2013
JavaScript Devoxx France 2013
 
Lbv Dev Meetup #3
Lbv Dev Meetup #3Lbv Dev Meetup #3
Lbv Dev Meetup #3
 
Concevoir une expérience SoLoMo (sociale, locale, mobile): retours d'expérien...
Concevoir une expérience SoLoMo (sociale, locale, mobile): retours d'expérien...Concevoir une expérience SoLoMo (sociale, locale, mobile): retours d'expérien...
Concevoir une expérience SoLoMo (sociale, locale, mobile): retours d'expérien...
 
12 bonnes raisons de ne pas contrôler vos applications
12 bonnes raisons de ne pas contrôler vos applications12 bonnes raisons de ne pas contrôler vos applications
12 bonnes raisons de ne pas contrôler vos applications
 
Java in docker devoxx ma 2018
Java in docker devoxx ma 2018Java in docker devoxx ma 2018
Java in docker devoxx ma 2018
 
Télés connectées et développement Web
Télés connectées et développement WebTélés connectées et développement Web
Télés connectées et développement Web
 
Du JavaScript propre ? Challenge accepted ! @Devoxx France 2013
Du JavaScript propre ? Challenge accepted ! @Devoxx France 2013Du JavaScript propre ? Challenge accepted ! @Devoxx France 2013
Du JavaScript propre ? Challenge accepted ! @Devoxx France 2013
 
VDLT - Retour DevFest 2023
VDLT - Retour DevFest 2023VDLT - Retour DevFest 2023
VDLT - Retour DevFest 2023
 
.NET Microframework: du code, de l&rsquo;électronique, de la robotique
.NET Microframework: du code, de l&rsquo;électronique, de la robotique.NET Microframework: du code, de l&rsquo;électronique, de la robotique
.NET Microframework: du code, de l&rsquo;électronique, de la robotique
 
Crash Test Your Idea Meetup Valtech 13/09/2016
Crash Test Your Idea Meetup Valtech 13/09/2016Crash Test Your Idea Meetup Valtech 13/09/2016
Crash Test Your Idea Meetup Valtech 13/09/2016
 
Php 100k
Php 100kPhp 100k
Php 100k
 
2012 11 Toulibre - Open Hardware
2012 11 Toulibre - Open Hardware2012 11 Toulibre - Open Hardware
2012 11 Toulibre - Open Hardware
 
Tirer parti des périphériques mobiles dans une application web : qui a dit qu...
Tirer parti des périphériques mobiles dans une application web : qui a dit qu...Tirer parti des périphériques mobiles dans une application web : qui a dit qu...
Tirer parti des périphériques mobiles dans une application web : qui a dit qu...
 

Architecture et Bonnes pratiques Android #DevoxxFr2016 Part1

  • 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. 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
  • 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. 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. Le contexte mémoire sous Android La mémoire
  • 8. La mémoire L'eau c'est la vie ! Pas sur Android
  • 9. La mémoire c'est la vie ! La mémoire L'eau c'est la vie ! Pas sur Android
  • 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. 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. Le contexte CPU/GPU sous Android Le CPU et GPU
  • 13. Dans la vrai vie Chaque utilisateur possède un CPU différent (tout pourri) Dans tes rêves Le CPU et GPU
  • 14. Le CPU et GPU CPU/GPU == Videur de Batterie
  • 15. Le contexte énergie sous Android L'énergie
  • 16. L'énergie Ce que l'on pense/espère avoir
  • 17. L'énergie Ce que nos utilisateurs ont vraiment
  • 19. L'énergie Les causes du gaspillage énergétique CPU/GPU Connexion (Http,...) Drawable/Animations... Mauvaises pratiques de code Services sans fin
  • 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. Le contexte collectif sous Android Vie en collectivité
  • 22. Le contexte mutualiste sous Android La vie en collectivité gmail twitter hangout evernote messsager facebook gmap linkedIn runstatic your app chrome
  • 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
  • 25. 60 chances par seconde toutes les 16ms 60 fps
  • 26. Aucun traitement dans l'UI Thread, jamais, aucun. UI thread
  • 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. Changer votre vision de la Base de données, la boite est trop petite. Persistence
  • 29. Communication réseau: principale cause de drainage de la batterie. Réseau
  • 30. Communication réseau: La big cookies startegy. Réseau
  • 31. Ne mettez pas à jour trop souvent, vous n'êtes pas seul ! Don't oversync. Réseau
  • 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
  • 35. Combler un besoin utilisateur. Je veux faire un truc et je peux!
  • 36. Avec efficacité. Quand je le fais, ça marche!
  • 37. L'utilisateur doit toujours savoir ce qu'il fait, où il est. Je sais ce que je fais !
  • 38. Et quand il le fait, c'est facile, simple et bluffant. C'est facile et c'est fluide
  • 39. Et grâce à vous, il possède des capacités qu'il n'avait pas. ça me donne des super pouvoirs!
  • 40. Ne frustrer pas votre utilisateur, laissez le libre. Sans authentification, ni paiement.
  • 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
  • 43. et j'ai utilisé mon téléphone ! J'ai encore de la batterie à 18h
  • 44. ou pas... Je le fais d'un doigt
  • 45. Spéciale dédicace à la RATP! Ca marche même hors ligne
  • 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. ç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 !
  • 49. Les animations amènent la compréhension. Animation
  • 50. L'information au bon moment. Notifications
  • 51. Suivez le guide ! Material Design https://www.google.com/design/spec/material-design/introduction.html
  • 52. Soyez Asynchrone ! Toujours Asynchrone ! Asynchrone
  • 54. Des contraintes du système. Respect
  • 55. De l'utilisateur Respect android:contentDescription public class MyAccessibilityService extends AccessibilityService
  • 56. et de ses données. Respect
  • 57. et de ses données. Respect On va vendre les données utilisateur.
  • 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. Aux écrans (tailles et densités). Adaptation
  • 61. A la puissance des processeurs. Adaptation
  • 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. A la bande passante. Adaptation
  • 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
  • 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 ?
  • 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. 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. 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
  • 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. 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. 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. A la compilation: Gradle et Saveur (Flavor) Adaptation Gradle Code prod screendensity processeur versions staging/test Saveur
  • 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. Les opérations UI Thread coûteuses. UI Performance Mesurer et Disposer. Measure and Layout Inflation. Initialisation de l'objet Application.
  • 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. 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. 90 UI Pro Tips Les styles sont tes meilleurs amis.
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Authentification Toujours authentifier vos utilisateurs UUID SharedPreferences SharedPreferences BackupHelper
  • 98. Exception Gérer vos exceptions ! Exception ManagedException isManaged UserId Error message Technical data Functional data Others stuff
  • 99. Exception Gérer vos exceptions ! Exception Manager ExceptionManager .manage(...) Team FeedBackUser FeedBack SomeWhere in your code galaxy
  • 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. /** * 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. 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
  • 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. 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. 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. 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. 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. 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. 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. Analysez Utilisation mémoire. Analysez votre application. Utilisation réseau. Utilisation CPU/GPU. Trace system (sysTrace). Analyse des écrans.
  • 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. 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. 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. Un peu d'histoire puis vint le modèle M(VC) swing Model Vue&Controller ModifieMets à jour Vue Controlleur
  • 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. 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. 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. 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. 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. Un peu d'histoire Pour les tests !!! Model Presenter View PresenterIntf ViewIntf MockPresenter MockView
  • 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. 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. Architecture Mais pourquoi tout ça ? Tester ! Séparer les responsabilités Réutiliser Simplifier
  • 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. Tester ! Séparer les responsabilités Réutiliser Simplifier Architecture L'important c'est d'arriver à ça Avec le moins de complexité possible.
  • 129. Architecture But wait ! Vous avez pas oublié un truc? Ce ne sont que des architectures de la couche VUE !!!
  • 133. Simplicité SéparationMaintenance EvolutionResponsabilités Robustesse Rappel des objectifs Test Réutilisabilité Contrat Bouchon/Mock
  • 143. Modèle n-tier MockView View = MVP Presenter ViewIntf 1 1 JUNIT Services AndroidServices SingletonServices Service I N T F Tests Tester
  • 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. 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
  • 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. 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
  • 151. Vues 163 Cohérence Rangez votre package view ! Soyez cohérent avec votre application
  • 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. 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
  • 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. Architecture But wait ! C'est pas un peu compliqué ton truc? et c'est que le début.

Notes de l'éditeur

  1. desactive ton telephone fais un selfie
  2. Le contexte = pénurie et vie en collectivité d'enfants 5 minutes
  3. bref chet haase stuff
  4. Une question simple Une réponse complexe qui dépend du contexte
  5. Une question simple Une réponse complexe qui dépend du contexte
  6. Il ne faut jamais ignoré le contexte et celui ci peut être multiple (a plusieurs niveaux)
  7. 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
  8. 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
  9. 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
  10. 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)
  11. 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...
  12. 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).
  13. 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).
  14. 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).
  15. Votre problème principal car c'est celui de votre utilisateur
  16. Qui parmis vous possède un chargeur ? un chargeur portable ?
  17. Votre problème principal car c'est celui de votre utilisateur
  18. Les bonnes pratiques de code et d'architecture
  19. C'est pour ça que vous êtes là non ? évitez tout ça :)
  20. Votre problème principal car c'est celui de votre utilisateur
  21. 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.
  22. 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...)
  23. 5 minutes
  24. 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...)
  25. 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
  26. 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
  27. 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?
  28. Dessin de RetoMeier GoogleIo2012 Utilisation de la big cookies startegy (on y reviendra)
  29. Dessin de RetoMeier GoogleIo2012 Utilisation de la big cookies startegy (on y reviendra)
  30. 10 minutes
  31. Laissez les gens rencontrer votre application. Le schema de Damien Mabin était super DroidConGreece
  32. 10 minutes
  33. Connaitre, comprendre, accepter le monde Android et ses contraintes, évoquées slides précédents
  34. 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é ?
  35. L'histoire du startup we, premier reflexe, on va vendre nos données utilisateurs Protéger les données de vos utilisateurs
  36. L'histoire du startup we, premier reflexe, on va vendre nos données utilisateurs Protéger les données de vos utilisateurs
  37. drawable-xhdpi layout-small
  38. 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
  39. Ca va beaucoup mieux avec la SupportLibrary mais c'est pas encore ça Les animations par exemple
  40. Vous n'êtes pas à l'abris des bugs spécifiques
  41. 30 minutes
  42. Il y a les miennes et celles de chet
  43. 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
  44. 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)
  45. 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
  46. 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)
  47. 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
  48. 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
  49. 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...
  50. 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.
  51. 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
  52. 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.
  53. 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
  54. 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
  55. 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
  56. 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
  57. GCM manager pour les synchronisation cliente GCM pour que le serveur previenne le client
  58. 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.
  59. 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é
  60. Donnez les PoolExecutor que tu utilises (1 pour la BD, 4 pour le reste cancelable, 4 pour le non cancelable)
  61. Les classes internes non statiques conservent une référence vers leur classe englobante.
  62. Les classes internes non statiques conservent une référence vers leur classe englobante.
  63. Les classes internes non statiques conservent une référence vers leur classe englobante.
  64. 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é
  65. 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é
  66. 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.
  67. systrace= voir combine de temps on passe dans une méthode Analyse des écrans: Animations, View hierarchy, Pixel perfect, overdraw,
  68. 20 minutes
  69. Explication de chaque modèle via un schéma pour chacun
  70. 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
  71. La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  72. Et on a inventé le MVC : decouple la vue, de l'interaction, de la representation.
  73. La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  74. La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  75. On a relié View et Controller, c'est là qu'on a mis les listener dans les vues
  76. 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
  77. La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  78. La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  79. La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  80. La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  81. La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
  82. Grace a gradle et aux saveurs on peut automatiser les confs poru executer les tests
  83. Et aussi il y a du DataBinding
  84. J'ai vu des flux tweeter où les gars s'engueulaient pedant 500 tweets sur le sujet (mais tu fais du MVPM toi, ... blazbla)
  85. 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)
  86. 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
  87. On sépare le code, on le ventile, on l'aère
  88. A scindé en plein de partie, c'est globalement compliqué, mais localement simple
  89. Chaque couche est responsable d'une partie precise de l'application: Algorithme, HTTP, DAO, Vues...
  90. Car chaque couche/composant possède un contrat très précis décrit par son interface
  91. Car modulaire
  92. Car modulaire
  93. Car ils ne dependent de personne
  94. JUnit et bouchons
  95. Les tests apportent la sécurité pour le projet
  96. Les tests apportent la sécurité pour le projet
  97. 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.
  98. 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.