JobPulse — Client Android natif d'offres d'emploi

AndroidJavaRetrofitOkHttpRoomWorkManager· 3 min de lecture

Un client Android natif construit face à une API REST d'offres d'emploi : s'inscrire ou se connecter, parcourir des offres paginées, et conserver localement celles sur lesquelles revenir — avec le cycle de vie du jeton et la couche HTTP traités une seule fois, au même endroit, plutôt que redéfinis dans chaque écran.

Le problème et les contraintes

Un client mobile face à une API authentifiée pose trois problèmes faciles à mal résoudre : le jeton arrive après le démarrage et change à la connexion/déconnexion, chaque requête a besoin des mêmes en-têtes, et reconstruire la pile HTTP à chaque appel jette silencieusement le pool de connexions. La contrainte était de rassembler tout cela en un seul endroit, pour que les activités ne manipulent que des appels typés et des résultats.

Approche

  • Une instance Retrofit unique, mise en cache statiquement et reconstruite uniquement quand le jeton change réellement, afin que les pools de connexions et de threads d'OkHttp survivent d'une requête à l'autre.
  • Un intercepteur OkHttp ajoute Accept: application/json et, si un jeton est présent, Authorization: Bearer … — les écrans ne touchent jamais aux en-têtes.
  • Surface d'API découpée par responsabilité en interfaces typées : AuthApi (inscription / connexion / déconnexion / profil) et JobsApi (liste / création), avec un conteneur générique PagedResponse<T> pour que la pagination soit modélisée une fois et non par endpoint.
  • Favoris basculés localement sous forme d'un Set<String> d'identifiants d'offres : la liste survit aux redémarrages et reste lisible sans aller-retour réseau.
localbeareractivitiestyped calls onlyAuthApiregister, login,logout, meJobsApilist, createPagedResponse<T>SharedPreferencesfavorites: Set<String>Retrofit + OkHttpcached, keyed on tokenAccept + Bearerjob-board API

Description complète :

  1. activities — typed calls only → AuthApi — register, login, — logout, me
  2. activities — typed calls only → JobsApi — list, create — PagedResponse<T>
  3. activities — typed calls only → SharedPreferences — favorites: Set<String> (local)
  4. AuthApi — register, login, — logout, me → Retrofit + OkHttp — cached, keyed on token — Accept + Bearer
  5. JobsApi — list, create — PagedResponse<T> → Retrofit + OkHttp — cached, keyed on token — Accept + Bearer
  6. Retrofit + OkHttp — cached, keyed on token — Accept + Bearer → job-board API (bearer)
Tout ce qui se trouve à droite de la colonne des activités est ce qu'un écran ne touche jamais : une seule instance Retrofit mise en cache et indexée sur le token, et l'intercepteur qui pose les en-têtes. Les favoris sont mis à part parce qu'ils ne franchissent volontairement jamais cette frontière.

Compromis faits

Decision

Une instance Retrofit mise en cache et indexée sur le jeton, plutôt qu'une par appel : préserve la réutilisation des connexions et évite de réallouer le client à chaque requête, au prix d'une logique d'invalidation explicite — l'instance doit savoir quand le jeton a changé, un petit état que j'assume au lieu de l'éviter.

Decision

Les favoris dans SharedPreferences plutôt que dans une table Room : un ensemble d'identifiants entiers n'a besoin ni de schéma, ni de migration, ni de DAO, et le coût de lecture/écriture est négligeable à cette taille. Room et WorkManager sont en place pour le chemin de synchronisation, là où un vrai miroir local des offres a besoin de structure.

Résultats

Une boucle fonctionnelle connexion → parcours paginé → mise en favori face à une API réelle, avec les préoccupations HTTP et d'authentification isolées derrière deux interfaces et un intercepteur au lieu d'être disséminées dans les activités.

Ce que je referais différemment

Conditionner HttpLoggingInterceptor en Level.BODY à BuildConfig.DEBUG — en l'état, un build de production écrirait les jetons Bearer et les corps de réponse complets dans logcat, et c'est le seul changement que je ferais avant de publier quoi que ce soit. Je déplacerais aussi les favoris côté serveur : ils sont aujourd'hui liés à l'appareil, donc le même compte sur un second téléphone repart vide.