Skip to content

Commit 02f1ebc

Browse files
committed
Merge remote-tracking branch 'johan/master'
2 parents 3c354b9 + e1aa7eb commit 02f1ebc

File tree

2 files changed

+368
-0
lines changed

2 files changed

+368
-0
lines changed
Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
---
2+
layout: post
3+
title: AndroidAnnotations
4+
author: johanpoirier
5+
tags: [android, androidannotations, framework, annotations]
6+
---
7+
8+
Dans le monde des [frameworks pour le développement Android](http://androidannotations.org/), je vous présente [AndroidAnnotations](http://androidannotations.org/) (mon coup de coeur). Comme son nom l’indique, ce framework apporte un bon nombre d'annotations qui nous permettent d'éliminer beaucoup de code boilerplate. Il simplifie le code et améliore sa lisibilité. Nous allons voir comment :
9+
10+
<p class="center">
11+
<img src="/public/img/2012-06-06-androidannotations/androidannotations.png" border="0" />
12+
</p>
13+
14+
15+
## Le principe
16+
17+
AA fonctionne par génération de code à la compilation (JAPT) en créant des classes suffixées d'un **_**. Une activity **MyActivity** devient donc **MyActivity_** et doit être déclarée telle quelle dans le AndroidManifest.xml. Ce qui peut paraître au premier abord un gros point négatif apporte un avantage non négligeable : pas d'injection au runtime, le code généré ressemble beaucoup à du code Android "classique".
18+
19+
20+
## Les annotations
21+
22+
Nous allons voir les principales annoations regroupées par thème.
23+
La [liste](https://github.com/excilys/androidannotations/wiki/AvailableAnnotations) complète est disponible sur la [documentation](https://github.com/excilys/androidannotations/wiki) du projet sur Github.
24+
25+
26+
### Pour les composants
27+
28+
#### @EActivity
29+
30+
{% highlight java %}
31+
@EActivity(R.layout.my_bookings)
32+
public class MyBookings extends Activity {
33+
...
34+
}
35+
{% endhighlight %}
36+
37+
Il s'agit de l'annotation principale. Elle permet la génèration de l'actvité **MyBookings_** dans laquelle la méthode onCreate est généré automatiquement et où le layout est défini avec la valeur contenue dans l'annotation. On peut dès maintenant utiliser quasiment toutes les autres annotations dans cette activité.
38+
39+
#### @EService, @EProvider, @EReceiver, EFragment...
40+
41+
La même chose pour les Service, IntentService, ContentProvider, BroadcastReceiver et autres fragments.
42+
43+
{% highlight java %}
44+
@EService
45+
public class MyService extends IntentService {
46+
...
47+
}
48+
{% endhighlight %}
49+
50+
#### @EView et @EViewGroup
51+
52+
L'utilisation de ces 2 annotations nous facilite la création et l'utilisation de widgets personnalisés.
53+
54+
@EView permet de redéfinir un bouton par exemple :
55+
56+
{% highlight java %}
57+
@EView
58+
public class MyButton extends Button {
59+
60+
@StringRes
61+
String someStringResource;
62+
63+
public MyButton(Context context, AttributeSet attrs) {
64+
super(context, attrs);
65+
}
66+
}
67+
{% endhighlight %}
68+
69+
@EViewGroup permet de définir un composant complet composé de plusieurs widgets dont des @EView :
70+
{% highlight java %}
71+
@EViewGroup(R.layout.title_with_subtitle)
72+
public class TitleWithSubtitle extends RelativeLayout {
73+
74+
@ViewById
75+
protected TextView title, subtitle;
76+
77+
public TitleWithSubtitle(Context context, AttributeSet attrs) {
78+
super(context, attrs);
79+
}
80+
81+
public void setTexts(String titleText, String subTitleText) {
82+
title.setText(titleText);
83+
subtitle.setText(subTitleText);
84+
}
85+
}
86+
{% endhighlight %}
87+
88+
Il faut comme toujours bien penser à utiliser le nom des classes générées avec le **_** dans les layout.
89+
90+
#### @EBean
91+
92+
Une simple classe peut bénéficier d'AA grâce à cette annotation. Cela nous permet de pouvoir faire de l'injection partout où cela est nécessaire :
93+
94+
{% highlight java %}
95+
@EBean(scope = Scope.Singleton)
96+
public class HotelService {
97+
98+
@RootContext
99+
Context context;
100+
101+
@Bean
102+
HotelDao hotelDao;
103+
104+
public HotelService() {
105+
106+
}
107+
}
108+
{% endhighlight %}
109+
110+
Dans l'exemple ci-dessus, on injecte HotelDao (qui lui-même a été déclaré en @EBean) dans le @EBean HotelService qui par ailleurs a été défini en Singleton.
111+
Ceci nous offre des perspectives très intéressantes car il nous permet de faire de l'injection de dépendances dans notre application sans risque de diminuer les performances (car aucune injection au runtime). Nous verrons plus bas un exemple plus évolué avec le couplage à ORMLite.
112+
113+
114+
### L'injection
115+
116+
On peut quasiment tout injecter dans nos classes annotées, des vues, des ressources ou encore des services systèmes.
117+
118+
#### @ViewById, @StringRes, @DrawableRes, ...
119+
120+
{% highlight java %}
121+
@EActivity(R.layout.my_bookings)
122+
public class MyBookings extends SearchableActivity {
123+
124+
@Pref
125+
BookingPrefs_ prefs;
126+
127+
@Bean
128+
UserService userService;
129+
130+
@ViewById(R.id.buttonHotels)
131+
Button hotelsButton;
132+
133+
...
134+
}
135+
{% endhighlight %}
136+
137+
Dans l'exemple, on voit une belle illustration de ces annotations.
138+
139+
- le @ViewById nous débarasse enfin des findViewById(R.id.buttonHotels)
140+
- le DrawableRes charge n'importe quel ressource présente dans nos répertoire /drawable/
141+
142+
#### @SystemService
143+
144+
Injection de systèmes services d'Android comme le NotificationManager, le LocationManager ou encore le ConnectivityManager.
145+
146+
#### @RootContext
147+
148+
L'accès au contexte est essentiel dans une application Android, et l'annotation @RootContext permet de fournir le contexte de l'application là où aucun autre n'est directement disponible.
149+
150+
#### @Extra
151+
152+
Permet de récupérer les valeurs passées dans un Intent. Encore une fois, ça simplifie la vie :
153+
154+
{% highlight java %}
155+
@Extra(C.EXTRA_HOTEL_KEY)
156+
Hotel hotel;
157+
{% endhighlight %}
158+
159+
#### @AfterViews, @AfterInject
160+
161+
- @AfterViews annote une méthode pour indiquer qu'elle doit être appelée après que les vues aient été récupéré (via les @ViewId). Très pratique quand on doit manipuler ces vues avant l'affichage. La méthode annotée est souvent utilisée à la place de onResume().
162+
163+
- @AfterInject annote également une méthode mais est appelée après l'injection dans la classe annotée par un @EBean
164+
165+
### La gestion des évènements
166+
167+
#### @Click, @LongClick et @Touch
168+
169+
Ces annoations nous débarasse des listener d'events. Plus besoin d'implémenter d'interfaces, il suffit d'annoter une méthode @Click :
170+
171+
{% highlight java %}
172+
@Click(R.id.buttonHotels)
173+
public void buttonHotelsClick() {
174+
startActivity(new Intent(this, HotelsList_.class));
175+
}
176+
{% endhighlight %}
177+
178+
Simple, non ?
179+
180+
#### @OptionsMenu et @OptionItem
181+
182+
Idem pour la gestion des options de la touche menu. Le @OptionsMenu déclare le layout du menu pour l'activité et les @OptionItem gère l'évènement de l'option sélectionnée :
183+
184+
{% highlight java %}
185+
@EActivity(R.layout.my_bookings)
186+
@OptionsMenu(R.menu.bookings)
187+
public class MyBookings extends Activity {
188+
189+
@OptionsItem(R.id.menu_search)
190+
public void searchOption() {
191+
onSearchRequested();
192+
}
193+
194+
@OptionsItem(R.id.menu_bench)
195+
public void benchmarkOption() {
196+
benchmark();
197+
}
198+
199+
...
200+
}
201+
{% endhighlight %}
202+
203+
### Les threads
204+
205+
#### @Background
206+
207+
Fini les AsyncTask, le @Background simplifie à l'extrême les tâches de fond !
208+
Cette annotation s'applique à une méthode qui, une fois appelée, s'exécute comme une AsyncTask. A la fin d'une telle méthode, vous pouvez appeler une autre méthode annotée par @UiThread pour agir sur l'interface utilisateur :
209+
210+
#### @UiThread
211+
212+
{% highlight java %}
213+
@Background
214+
public void benchmark() {
215+
// benchmark some stuff
216+
displayToast("Benchmark done !");
217+
}
218+
219+
@UiThread
220+
public void displayToast(String text) {
221+
Toast.makeText(this, text, Toast.LENGTH_LONG).show();
222+
}
223+
{% endhighlight %}
224+
225+
226+
### Bonus
227+
228+
#### @SharedPref et @Pref
229+
230+
Cette annotation permet d'utiliser les SharedPreferences de façon typesafe.
231+
Il suffit de déclarer une interface :
232+
233+
{% highlight java %}
234+
@SharedPref(value=Scope.UNIQUE)
235+
public interface BookingPrefs {
236+
237+
@DefaultLong(-1L)
238+
long loggedUserId();
239+
}
240+
{% endhighlight %}
241+
242+
Puis tout simplement dons une activité :
243+
244+
{% highlight java %}
245+
@EActivity(R.layout.my_bookings)
246+
public class MyBookings extends Activity {
247+
248+
@Pref
249+
BookingPrefs_ prefs;
250+
251+
@Override
252+
protected void onResume() {
253+
if (prefs.loggedUserId().get() == -1L) {
254+
// show login activity
255+
startActivityForResult(new Intent(this, Login_.class), LOGIN_ACTIVITY_CODE);
256+
} else {
257+
displayBookings();
258+
}
259+
}
260+
}
261+
{% endhighlight %}
262+
263+
#### @Rest
264+
265+
[@Rest](https://github.com/excilys/androidannotations/wiki/Rest%20API) permet de déclarer une interface d'accès à une API Rest. Il est très courant d'appeler une API Rest dans nos applications, cette annotation se charge de générer toute l'implémentation nécessaire. Par contre, cela nécessite l'utilisation de RestTemplate du framework [Spring for Android](http://www.springsource.org/spring-android).
266+
267+
{% highlight java %}
268+
@Rest("http://pullrequest.org/booking/api")
269+
public interface BookingRestApi {
270+
271+
@Get("/hotels")
272+
@Accept(MediaType.APPLICATION_JSON)
273+
HotelList getHotels();
274+
275+
@Get("/hotel/{id}")
276+
@Accept(MediaType.APPLICATION_JSON)
277+
Hotel getHotel(long id);
278+
279+
void setRestTemplate(RestTemplate restTemplate);
280+
}
281+
{% endhighlight %}
282+
283+
Le @Rest précise l'url de l'API. Ensuite, cela ressemble beaucoup à du JAX-RS.
284+
285+
Petite précision pour faire du JSON ou du XML, il faut ajouter un marshaller comme Jackson ou GSon et le préciser à l'appel des méthodes REST :
286+
287+
{% highlight java %}
288+
// add GsonMessageConverter to resttemplate
289+
RestTemplate restTemplate = new RestTemplate();
290+
restTemplate.getMessageConverters().add(new GsonHttpMessageConverter());
291+
bookingRestApi.setRestTemplate(restTemplate);
292+
293+
// call to API
294+
HotelList hotels = bookingRestApi.getHotels();
295+
{% endhighlight %}
296+
297+
## Intégration à d'autres frameworks
298+
299+
### RoboGuice
300+
301+
AA s'intègre à RoboGuice 1.1.1 (pas de support de la 2.0 encore) à l'aide de l'annotation [@RoboGuice](https://github.com/excilys/androidannotations/wiki/RoboGuiceIntegration).
302+
Je préfère personellement n'utiliser que AA même si cela implique une injection de dépendance moins élaborée.
303+
304+
### ORMLite
305+
306+
Rien de prévu nativement pour l'intégration à ORMLite mais voici ce qui peut être réalisé :
307+
308+
- créer une classe générique Service<T> :
309+
310+
{% highlight java %}
311+
@EBean(scope = Scope.Singleton)
312+
public class UserService extends Service<User> {
313+
314+
@RootContext
315+
Context context;
316+
317+
public UserService() { }
318+
319+
@AfterInject
320+
public void setDao() {
321+
this.setDao(context, User.class);
322+
}
323+
}
324+
{% endhighlight %}
325+
326+
- déclarer un service UserService par exemple héritant de notre classe Service :
327+
328+
{% highlight java %}
329+
public abstract class Service<T> {
330+
331+
protected Dao<T, Long> dao;
332+
333+
public void setDao(Context context, Class<T> clazz) {
334+
try {
335+
dao = DaoManager.createDao(OpenHelperManager.getHelper(context, DatabaseHelper.class).getConnectionSource(), clazz);
336+
} catch (SQLException e) {
337+
e.printStackTrace();
338+
}
339+
}
340+
341+
public Dao<T, Long> getDao() {
342+
return this.dao;
343+
}
344+
}
345+
{% endhighlight %}
346+
347+
- injecter le bean UserService dans une activité :
348+
349+
{% highlight java %}
350+
@EActivity(R.layout.my_bookings)
351+
public class MyBookings extends Activity {
352+
353+
@Bean
354+
UserService userService;
355+
356+
...
357+
}
358+
{% endhighlight %}
359+
360+
On peut ainsi utiliser les DAO générés par ORMLite dans nos services, puis injecter nos services dans toutes les classes annotées par AA.
361+
Dans l'idéal, il faudrait créer une annotation @OrmLiteDao(MyObject.class) afin de simplifier ce code.
362+
363+
364+
## Conclusion
365+
366+
AndroidAnnotations est simple et efficace. Il est vraiment complet et allège d'une façon considérable le code, laissant apparaître uniquement ce qui est important : le fonctionnel. Je le conseille donc fortement pour tous les nouveaux projets Android, car il n'a pas vraiment d'inconvénients : il est léger (50Ko) et n'a pas d'impact sur les performances (tout le code est généré à la compilation donc pas d'introspection ni d'injection au runtime).
367+
368+
Par ailleurs, [Pierre-Yves Ricau](https://github.com/pyricau), le créateur du projet, est très actif et ouvert à la discussion. Il suit de près les évolutions d'Android et adapte rapidement AndroidAnnotations.
19.8 KB
Loading

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy