Accueil > Non classé > Clean Android Code : I CAN HAZ INT IDS?

Clean Android Code : I CAN HAZ INT IDS?

Disclaimer : cet article contient des switch, et pourrait donc heurter la sensibilité d’un public non averti. Je tiens à décliner toute responsabilité en cas de switchite aiguë consécutive à une pratique trop assidue d’Android.

Développeurs Android, avez-vous remarqué à quel point votre code utilise des int comme identifiant à tout bout de champ ?
Il n’est pas rare de devoir écrire le code suivant :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MyActivity extends Activity {

    private static final int WARNING_DIALOG = 0;
    private static final int DOWNLOAD_PROGRESS_DIALOG = 1;
    private static final int CONFIRM_LOGOUT_DIALOG = 2;

    public void iCanHasCheezburger() {
        showDialog(WARNING_DIALOG);
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case WARNING_DIALOG:
            return createWarningDialog();
        case DOWNLOAD_PROGRESS_DIALOG:
            return createDownloadProgressDialog();
        case CONFIRM_LOGOUT_DIALOG:
            return createConfirmLogoutDialog();
        default:
            return null;
        }
    }
    // [...]
}

Cette manière de faire est d’ailleurs recommandée par le guide de développement Android officiel.

Papy Android réalisé avec Androidify : http://markoi.de/HIy



Référencer les entiers sous forme de constantes, c’est certes mieux qu’utiliser directement les valeurs (ex: showDialog(42);), mais ce n’est qu’un pis aller. Écrire private static final int WARNING_DIALOG = 1; c’est long. Du coup, on copie/colle.

Et qui dit copier/coller dit risque de se planter, en ayant par exemple deux constantes avec la même valeur (c’est du vécu !).

Le SDK Android propose pourtant un système de génération d’identifiants uniques que vous connaissez bien. A titre de rappel, on déclare un composant dans un layout XML en lui attribuant un nouvel identifiant avec @+id/ :


1
2
3
4
5
6
<Button
    android:id="@+id/sexyButton"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Click me, I'm sexy"
/>

Ce qui a pour effet d’ajouter l’identifiant à la classe R :


1
2
3
4
5
6
public final class R {
    // [...]
    public static final class id {
        public static final int sexyButton=0x7f050001;
    }
}

Dans l’activité, une fois le layout gonflé (comment traduisez-vous inflate ?), on obtient une référence vers l’instance du composant grâce à son identifiant :


1
Button sexyButton = (Button) findViewById(R.id.sexyButton);

Pourquoi ne pas utiliser ce système d’identifiants uniques pour vos propres besoins ? Eh oui, c’est possible !

Pour cela, il vous suffit de créer des ressources de type id. Par exemple, en créant un fichier res/values/ids.xml :


1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item type="id" name="warningDialog" />
    <item type="id" name="downloadProgressDialog" />
    <item type="id" name="confirmLogoutDialog" />
</resources>

Et hop, on se débarrasse des constantes “caca” :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyActivity extends Activity {

    public void iCanHasCheezburger() {
        showDialog(R.id.warningDialog);
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case R.id.warningDialog:
            return createWarningDialog();
        case R.id.downloadProgressDialog:
            return createDownloadProgressDialog();
        case R.id.confirmLogoutDialog:
            return createConfirmLogoutDialog();
        default:
            return null;
        }
    }
    // [...]
}

Vous pouvez également utiliser cette technique si vous préférez créer vos menus programmatiquement plutôt qu’en XML :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    menu.add(Menu.NONE, R.id.memeMenuItem, Menu.NONE, R.string.meme);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.memeMenuItem:
        allYourBaseAreBelongToUs();
        return true;
    default:
        return super.onOptionsItemSelected(item);
    }
}

J’espère que cet article vous sera utile ;-) !

Note des relecteurs : Bastien suggère d’utiliser un AnnotationProcessor pour vérifier que toutes les constantes ont une valeur différente, tandis que Mathieu précise qu’on pourrait aussi utiliser des enums, mais que c’est contre-performant.

Pub de fin : vous aurez remarqué que le lien vers Androidify utilise Markoi.de, un réducteur d’url spécifique aux applications de l’Android market. Je ne suis pas encore bien certain de l’intérêt du schmilblick (à part les stats), mais je trouve ça fun.

Share
  1. 21/02/2011 à 19:26 | #1

    C’est une solution intelligente, mais ça ne règle malheureusement pas le problème de fond: le fait qu’Android nous incite à remplacer les enums par des entiers pour des raisons de “performance” (comme tu l’as mentionné).

    Ils auraient plutôt dû essayer d’optimiser les enums en question, éventuellement en trouvant un moyen pour les transformer en entiers à la compilation… Ou en faisant évoluer la VM pour les gérer plus efficacement.

    Dans 5 ans, quand les téléphones seront tous 20 fois plus puissant, on regrettera d’être coincés avec des int constants…

  2. 21/02/2011 à 22:17 | #2

    On peut aussi espérer que d’ici là, une nouvelle API aura été développée, avec un design un peu plus pensé en amont. L’API Fragment est un exemple de nouvelle API sortie du chapeau pour faire des choses qui étaient déjà possible par le passé, mais avec une API à priori plus adaptée.

    A mon sens, l’activité est un god object.

    Par ailleurs, entièrement contrôlée par le “conteneur”, managée via des callbacks, et nécessitant d’hériter d’une classe particulière. Ça vous rappellerai pas les servlets ? Sans parler de l’AndroidManifest.xml qui me fait beaucoup penser à un web.xml.

    Les API JEE ont évoluent justement vers une disparition progressive du web.xml, et l’utilisation d’annotations en lieu et place d’un héritage spécifique. Une voie à suivre ?

  3. 22/02/2011 à 10:49 | #3

    @Pierre-Yves RICAU
    Intéressant. Je ne connais pas bien l’écosystème Android mais, vu comme ça, il y a encore de l’espoir :)

    J’aime beaucoup l’idée d’une parallèle Android / JEE. Les APIs ne sont pas figées, et les erreurs de jeunesse de la plateforme vont être progressivement corrigées.

    Quid du “Spring Framework” pour Android? (je ne parle pas forcément d’injection de dépendance, mais plutôt des simplifications apportées par Spring au développement web)

  4. 22/02/2011 à 11:25 | #4

    Je ne crois pas qu’il y ait pour l’instant de framework largement adopté et proposant une manière “simplifiée” d’écrire du code Android.

    On trouve cependant un certain d’approches, qui souffre malgré de leur jeunesse et d’une base d’utilisateurs faible :

    RoboGuice : Plus que l’injection via Guice, on peut revenir l’injection des vues à la UiBinder en GWT, et la notion de scope activity.

    AndroidAnnotations : Reprend certaines idées de RoboGuice mais au compile time plutôt que runtime, propose l’équivalent des @UiHandler de GWT, et une gestion des threads simplifiée avec @UiThread et @Background (disclaimer : j’en suis l’auteur)

    DroidFu : Propose un certain nombre de composants pour se simplifier la vie, notamment la gestion des threads et du HTTP.

    GreenDroid : propose des composants de UI permettant d’unifier le style et de faire de jolies applis

    Quand à Spring, il existe depuis peu le framework Spring Android, officiellement supporté par SpringSource. Il se contente pour l’instant d’intégrer les RestTemplate de Spring MVC… Mais c’est déjà pas mal !

    Deux articles sur le sujet : http://blog.springsource.com/2010/12/17/spring-android-and-maven-part-1
    et http://blog.springsource.com/2011/02/09/spring-android-and-maven-part-2

    (Petit conseil au passage : ajoutez ce blog à vos RSS, les articles liés à Spring sont en général de bonne qualité).

  5. Thierry
    17/03/2011 à 13:36 | #5

    Avoid Enum a été enlevé de la doc. C’est donc correct à utiliser maintenant! :)

    http://stackoverflow.com/questions/5143256/why-was-avoid-enums-where-you-only-need-ints-removed-from-androids-performance

  6. 17/03/2011 à 14:12 | #6

    Merci pour l’info !

  7. 06/03/2015 à 16:51 | #7

    Avoid Enum a été enlevé de la doc. C’est donc correct à utiliser maintenant! :)

  1. 21/02/2011 à 11:13 | #1
  2. 04/07/2015 à 07:56 | #2