Slide 1

Slide 1 text

AndroidAnnotations Développer efficacement pour Android Romain Piel développeur Android chez MPme 1830 MER 24 AVR 2013

Slide 2

Slide 2 text

Qui suis-je ? • Passionné d’Android depuis 2010 • Développeur de nombreuses applications mobiles • Post Office UK, Kiss Kube, Robin for App.net... • Responsable du produit Android chez MPme, Londres

Slide 3

Slide 3 text

Ce soir Android, ce qui alourdit le code Le régime AndroidAnnotations Android et les annotations

Slide 4

Slide 4 text

Android, ce qui alourdit le code

Slide 5

Slide 5 text

Android, ce qui alourdit le code Les actions sur les éléments de l’UI TextView myTextView = (TextView)findViewById(R.id.someId); ((Button)findViewById(R.id.someOtherId)).onClick(new OnClickListener() { @Override public void onClick(View v) { handler.postDelayed(new Runnable() { @Override public void run() { Toast.make("Ah que coucou!", Toast.LENGTH_SHORT).show(); } }, 500); } });

Slide 6

Slide 6 text

Android, ce qui alourdit le code Les AsyncTask class MakeCoffeeTask extends AsyncTask { @Override protected Coffee doInBackground(String... params) { String brand = params[0]; Boolean withSugar = params[1]; Coffee coffee = makeCoffee(coffeeBrand, withSugar); return coffee; } @Override protected void onPostExecute(Coffee result) { drinkCoffee(result); } }

Slide 7

Slide 7 text

Android, ce qui alourdit le code Et bien d’autres petits détails... @Override public void onSaveInstanceState(Bundle bundle) { super.onSaveInstanceState(bundle); bundle.putSerializable("items", items); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null && savedInstanceState.containsKey("items")) { items = ((ArrayList ) savedInstanceState.getSerializable("items")); } } ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);

Slide 8

Slide 8 text

Conséquences Votre code Vous

Slide 9

Slide 9 text

“Le rapport entre le temps consacré a la lecture du code et son écriture est bien plus de 10 à 1. Faciliter la lecture, c'est faciliter l'écriture.” Robert C. Martin

Slide 10

Slide 10 text

Le régime AndroidAnnotations Par Pierre-Yves Ricau et 18 autres contributeurs https://github.com/excilys/androidannotations

Slide 11

Slide 11 text

@EActivity(R.layout.translate) public class TranslateActivity extends Activity { @ViewById EditText textInput; @ViewById(R.id.myTextView) TextView result; @AnimationRes Animation fadeIn; @Click void doTranslate() { translateInBackground(textInput.getText().toString()); } @Background void translateInBackground(String textToTranslate) { String translatedText = callGoogleTranslate(textToTranslate); showResult(translatedText); } @UiThread void showResult(String translatedText) { result.setText(translatedText); result.startAnimation(fadeIn); } } Injection de vue Objectif : Simplifier la vie du développeur Injection de ressource Événements Threads Layout @EActivity(R.layout.translate) public class TranslateActivity extends Activity { @ViewById EditText textInput; @ViewById(R.id.myTextView) TextView result; @AnimationRes Animation fadeIn; @Click void doTranslate() { translateInBackground(textInput.getText().toSt } @Background void translateInBackground(String textToTranslate) String translatedText = callGoogleTranslate(te showResult(translatedText); } @UiThread void showResult(String translatedText) { result.setText(translatedText); result.startAnimation(fadeIn); } }

Slide 12

Slide 12 text

@EActivity(R.layout.translate) public class TranslateActivity extends Activity { @ViewById EditText textInput; @ViewById(R.id.myTextView) TextView result; @AnimationRes Animation fadeIn; @Click void doTranslate() { translateInBackground(textInput.getText().toS } @Background void translateInBackground(String textToTranslate) String translatedText = callGoogleTranslate(t showResult(translatedText); } @UiThread void showResult(String translatedText) { result.setText(translatedText); result.startAnimation(fadeIn); Injection de vue Injection de ressource Événements Threads Layout @EActivity(R.layout.translate) public class TranslateActivity extends Activity { @ViewById EditText textInput; @ViewById(R.id.myTextView) TextView result; @AnimationRes Animation fadeIn; @Click void doTranslate() { translateInBackground(textInput.getText().toSt } @Background void translateInBackground(String textToTranslate) String translatedText = callGoogleTranslate(te showResult(translatedText); } @UiThread void showResult(String translatedText) { result.setText(translatedText); result.startAnimation(fadeIn); } }

Slide 13

Slide 13 text

O En tout, plus de 80 annotations: • Amélioration de composants • Activity, Service, etc... • Injections • Vues, ressources, dépendances... • Gestion des événements • Simple threading Bonne intégration avec d’autres librairies Objectif : Simplifier la vie du développeur

Slide 14

Slide 14 text

Annotations de base @EActivity(R.layout.translate) public final class TranslateActivity { } Les activity sont annotées @EActivity Des annotations existent pour tous les composants classiques : @EService @EReceiver @EApplication @EFragment @EProvider ...

Slide 15

Slide 15 text

Annotations de base Ces classes annotées permettent : • D’améliorer les composants de base • Injection de layout • Fullscreen activities, etc... • D’ajouter d’autres annotations sur les attributs et les méthodes

Slide 16

Slide 16 text

Annotations internes - Injections • Injection de vues • Injection de ressources • Injection de composants systèmes @ViewById(R.id.myTextView) TextView result; @AnimationRes Animation fadeIn; @SystemService NotificationManager notificationManager;

Slide 17

Slide 17 text

Annotations internes - Injections Les attributs annotés ne peuvent pas être utilisés directement dans le constructeur ou le onCreate() @AfterInject : les attributs non-vues sont injectés @AfterViews : les vues sont instanciées @AfterInject public void doSomethingAfterInjection() { } @AfterViews public void updateTextWithDate() { result.setText("Date: " + new Date()); }

Slide 18

Slide 18 text

Annotations internes - Gestion des threads Annotez vos méthodes avec @Background et @UiThread Bye AsyncTask! @Background void someBackgroundWork(String aParam, long anotherParam) { } @UiThread void doInUiThread(String result) { }

Slide 19

Slide 19 text

Objets personnalisés Aussi possible d’annoter des classes personnalisées avec @EBean : Cette classe peut alors être injectée : @EBean public class MyClass { } @EActivity public class MyActivity extends Activity { @Bean MyClass myObject; }

Slide 20

Slide 20 text

Pas mal... Mais comment ça marche ton truc?

Slide 21

Slide 21 text

Mais comment ça marche ton truc? Java 6 Annotation Processor Génération de code à la compilation Chaque classe annotée aura une sous- classe générée : public final class TranslateActivity_ extends TranslateActivity { ! // Ici sera généré tout le code pas jojo que l'on ne veut pas écrire }

Slide 22

Slide 22 text

Mais comment ça marche ton truc? Configuration de l’IDE (eclipse) : 1.Ajouter dans /libs androidannotations-X.X.X-api.jar 2.Ajouter ce jar au build path 3.Ajouter dans un autre dossier /compile-libs le jar androidannotations-X.X.X.jar 4.Dans les project properties : Java Compiler > Annotation Processing Enable annotation processing Java Compiler > Annotation Processing > Factory Path Ajouter le processeur androidannotations-X.X.X.jar

Slide 23

Slide 23 text

Pièges classiques startActivity(new Intent(this, TranslateActivity_.class)); @AfterViews public void updateTextWithDate() { result.setText("Date: " + new Date()); }

Slide 24

Slide 24 text

Problèmes de performances? Aucun Le travail se fait à la compilation. Le runtime n’est donc pas affecté.

Slide 25

Slide 25 text

Conséquences Votre code Vous

Slide 26

Slide 26 text

Retour d’expérience • Au départ assez difficile de se mettre dans la logique AndroidAnnotations. • Puis très vite addictif... • Un code beaucoup plus lisible car plus light • Entraîne à modulariser le plus possible son code • Debugging : aucune différence. Le code généré est accessible

Slide 27

Slide 27 text

Retour d’expérience Certaines limites : • Généricité non supportée • Très mauvais d’intégrer de l’héritage entre deux classes annotées • Classes internes ne peuvent pas être annotées

Slide 28

Slide 28 text

Android et les annotations 1. Les copains d’AndroidAnnotations 2. Les autres

Slide 29

Slide 29 text

Roboguice Construit à partir de Google Guice Injection de vues et de tout type d’objets. + Injection plus puissante que AndroidAnnotations - Les injections se font au runtime ‣ Moins bonnes performances - Activités doivent hériter de RoboActivity ‣ Problème pour utiliser d’autres librairies

Slide 30

Slide 30 text

Dagger Librairie d’injection de dépendances similaire à Google Guice. Comme Roboguice, offre plus d’options sur les injections de dépendances. Les injections se font aussi au runtime. Pas orienté Android mais très light. A fast dependency injector for Android and Java.

Slide 31

Slide 31 text

Butter Knife Librairie très simple pour injecter des vues. Très similaire à l’injection de vue d’AndroidAnnotations Fonctionne au runtime class ExampleActivity extends Activity { ! @InjectView(R.id.title) TextView title; ! @InjectView(R.id.subtitle) TextView subtitle; ! @InjectView(R.id.footer) TextView footer; ! @Override public void onCreate(Bundle savedInstanceState) { ! ! super.onCreate(savedInstanceState); ! ! setContentView(R.layout.simple_activity); ! ! Views.inject(this); ! } }

Slide 32

Slide 32 text

Android et les annotations 1. Les copains d’AndroidAnnotations 2. Les autres

Slide 33

Slide 33 text

Project lombok Librairie Java basée sur les annotations pour simplifier au maximum le langage : @AllArgsConstructor public class MyClass { ! @Getter @Setter private int age = 10; ! @Setter(AccessLevel.PROTECTED) private String name; }

Slide 34

Slide 34 text

Otto Librairie Java basé sur des annotations proposant un bus d’événements : Otto An enhanced Guava-based event bus with emphasis on Android support. Bus Post Subscribe bus.post(new AnswerAvailableEvent(42)); @Subscribe public void answerAvailable(AnswerAvailableEvent event) { }

Slide 35

Slide 35 text

Retrofit Librairie Java pour implémenter rapidement un client REST. public class Client { private static final String API_URL = "https://api.github.com"; class Contributor { String login; int contributions; } interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List contributors( @Name("owner") String owner, @Name("repo") String repo ); } }

Slide 36

Slide 36 text

Roboelectric Librairie orientée Android permettant de faire rapidement des tests unitaires. @RunWith(RobolectricTestRunner.class) public class HomeActivityTest { ! private HomeActivity activity; private ImageView pivotalLogo; @Before public void setUp() throws Exception { activity = new HomeActivity(); activity.onCreate(null); pivotalLogo = (ImageView) activity.findViewById(R.id.pivotal_logo); } @Test public void shouldHaveALogo() throws Exception { assertThat(pivotalLogo.getVisibility(), equalTo(View.VISIBLE)); assertThat(shadowOf(pivotalLogo).getResourceId(), equalTo(R.drawable.pivotallabs_logo)); } }

Slide 37

Slide 37 text

Conclusion Annotations pour un code • Lisible • Maintenable • Réutilisable Keep it simple

Slide 38

Slide 38 text

Des questions?