Intégration de RxJava, MVP, Retrofit, Dagger2 et OkHttp pour les requêtes réseau sous Android

Au fil des ans, les technologies de gestion des requêtes réseau sous Android ont considérablement évolué, passant de HttpClient et HttpURLConnection à des bibliothèques comme Volley et OkHttp, puis à Retrofit. Cependant, une utilisation directe de ces outils peut entraîner une duplication de code répétitive pour chaque requête réseau, ce qui est peu pratique pour les développeurs expérimentés.

Cet article présente une approche d'encapsulation des requêtes réseau en tirant parti des technologies suivantes :

  • Retrofit2 : Pour la définition des points d'accès API via des interfaces.
  • OkHttp : Comme client HTTP sous-jacent pour Retrofit.
  • RxJava : Pour gérer les opérations asynchrones et basées sur les événements.
  • Dagger2 : Pour l'injection de dépendances, facilitant le découplage et la gestion des dépendances.
  • Modèle MVP (Model-View-Presenter) : Pour une architecture logicielle structurée et testable.

Concepts Clés et Leur Application

1. Retrofit2 : Interface et Configuration

Retrofit2 simplifie la définition des requêtes réseau en utilisant des interfaces Java. Il s'appuie sur OkHttp pour exécuter les requêtes HTTP. La configuration typique implique la spécification d'un client HTTP (OkHttp), d'une URL de base, d'un adaptateur pour les appels RxJava et d'un convertisseur pour la sérialisation/désérialisation des données (par exemple, Gson).


Retrofit.Builder retrofitBuilder = new Retrofit.Builder();
retrofitBuilder.client(okHttpClientInstance)
               .baseUrl(ApiService.BASE_URL)
               .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
               .addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = retrofitBuilder.build();

// Définition de l'interface du service API
public interface ApiService {
    String BASE_URL = "https://api.example.com/";

    @GET("items/{id}")
    Observable<itemresponse> getItem(@Path("id") String itemId);
}

// Création d'une instance du service API
ApiService apiService = retrofit.create(ApiService.class);
</itemresponse>

2. RxJava : Programmation Réactive et Asynchrone

RxJava est une bibliothèque puissante pour la programmation réactive en Java. Elle repose sur le modèle Observateur-Observateur, permettant de gérer des flux d'événements asynchrones. Les concepts clés incluent :

  • Observable : Émet une séquence d'événements (données, erreurs, achèvement).
  • Observer (ou Subscriber) : Réagit aux événements émis par l'Observable.
  • subscribe() : Établit la connexion entre l'Observable et l'Observer.

RxJava améliore la gestion de l'asynchronisme en évitant les "callbacks hell" grâce à des opérateurs de combinaison et en fournissant des notifications explicites pour l'achèvement (onCompleted()) et les erreurs (onError()).


// Exemple d'utilisation de RxJava
apiService.getItem("123")
    .subscribeOn(Schedulers.io()) // Exécuter sur un thread d'IO
    .observeOn(AndroidSchedulers.mainThread()) // Observer les résultats sur le thread principal
    .subscribe(new Subscriber<itemresponse>() {
        @Override
        public void onCompleted() {
            // L'opération est terminée avec succès
        }

        @Override
        public void onError(Throwable e) {
            // Gérer les erreurs
        }

        @Override
        public void onNext(ItemResponse response) {
            // Traiter les données reçues
        }
    });
</itemresponse>

3. Dagger2 : Injection de Dépendances

Dagger2 est un framework d'injection de dépendances qui facilite le découplage des composants logiciels. Il utilise des annotations pour gérer la création et l'injection des objets nécessaires. Les annotations principales sont :

  • @Inject : Pour demander une injection.
  • @Module : Définit des classes qui fournissent des dépendances.
  • @Provides : Marque des méthodes dans un module qui produisent des instances de dépendances.
  • @Component : Connecte les modules aux classes qui nécessitent des injections.
  • @Scope / @Singleton : Définit la durée de vie des instances injectées.

L'utilisation de Dagger2 améliore la testabilité et la maintenabilité du code en rendant les dépendances explicites et en facilitant le remplacement des implémentations.

4. Modèle MVP : Architecture Applicative

Le modèle MVP sépare l'interface utilisateur (View), la logique métier (Model) et la logique de présentation (Presenter). Le Presenter agit comme intermédiaire, récupérant les données du Model et mettant à jour la View. Cette séparation rend le code plus organisé, plus facile à tester et à maintenir, en particulier dans les applications complexes.

Encapsulation d'un Framework de Requête Réseau

Structure du Projet

Une structure de projet bien organisée est essentielle. On peut distinguer :

  • data : Contient la logique pour récupérer les données (locale et distatne).
  • di : Gère la configuration de Dagger2, y compris les modules pour OkHttp et Retrofit.
  • network : Définit les interfaces d'API et les gestionnaires de réponses.

Initialisation de Retrofit

Configurer Retrofit avec OkHttp, RxJava et un convertisseur Gson.


@Module
public class NetworkModule {

    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient() {
        // Configuration d'OkHttpClient avec des intercepteurs, timeouts, etc.
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        // ... configuration ...
        return builder.build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(OkHttpClient okHttpClient) {
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.client(okHttpClient)
               .baseUrl(ApiService.BASE_URL)
               .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
               .addConverterFactory(GsonConverterFactory.create());
        return builder.build();
    }

    @Provides
    @Singleton
    ApiService provideApiService(Retrofit retrofit) {
        return retrofit.create(ApiService.class);
    }
}

Gestion des Réponses API Unifiées

Définir une classe de réponse commune pour gérer les codes d'état et les messages d'erreur de manière standardisée.


public class ApiResponse<t> {
    private int statusCode;
    private String message;
    private T data;

    // Getters et Setters
    public int getStatusCode() { return statusCode; }
    public String getMessage() { return message; }
    public T getData() { return data; }
}
</t>

Utiliser une fonction RxJava pour transformer la réponse unifiée en un Observable des données réelles ou en une erreur.


public class ApiErrorHandlingFunc<t> implements Func1<apiresponse>, Observable<t>> {
    @Override
    public Observable<t> call(ApiResponse<t> response) {
        if (response.getStatusCode() != 200) {
            return Observable.error(new ApiException(response.getMessage(), response.getStatusCode()));
        } else {
            return Observable.just(response.getData());
        }
    }
}
</t></t></t></apiresponse></t>

Gestionnaire de Souscripteurs (Subscriber)

Créer un Subscriber personnalisé pour gérer les callbacks RxJava, afficher des indicateurs de chargement, gérer les erreurs et déclencher des actions après la complétion.


public abstract class BaseSubscriber<t> extends Subscriber<t> {

    private final IView mView; // Interface pour interagir avec la vue (Presenter -> View)
    private final boolean showLoading;

    public BaseSubscriber(IView view, boolean showLoading) {
        this.mView = view;
        this.showLoading = showLoading;
    }

    @Override
    public void onStart() {
        if (showLoading && mView != null) {
            mView.showLoadingIndicator();
        }
    }

    @Override
    public void onCompleted() {
        if (showLoading && mView != null) {
            mView.hideLoadingIndicator();
        }
    }

    @Override
    public void onError(Throwable e) {
        if (showLoading && mView != null) {
            mView.hideLoadingIndicator();
        }
        handleError(e);
        if (mView != null) {
            mView.onRequestFailed(e.getMessage());
        }
    }

    // Méthode abstraite pour traiter les données
    public abstract void onSuccess(T data);

    @Override
    public void onNext(T t) {
        onSuccess(t);
    }

    private void handleError(Throwable e) {
        // Logique de gestion des erreurs (ex: erreurs réseau, erreurs serveur)
        if (e instanceof SocketTimeoutException) {
            // Gérer timeout
        } else if (e instanceof ConnectException) {
            // Gérer connexion perdue
        } else if (e instanceof ApiException) {
            // Gérer erreurs API spécifiques
            ApiException apiException = (ApiException) e;
            // Afficher message d'erreur selon apiException.getStatusCode()
        } else {
            // Erreur générique
        }
    }
}
</t></t>

Intercepteurs OkHttp

Utiliser des intercepteurs OkHttp pour ajouter des en-têtes communs, des paramètres de requête (comme les signatures, les timestamps) ou pour le logging.


public class CommonParamsInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Request.Builder requestBuilder = originalRequest.newBuilder();

        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String signature = generateSignature(originalRequest, timestamp);

        HttpUrl.Builder urlBuilder = originalRequest.url().newBuilder();
        if (originalRequest.method().equals("GET")) {
            urlBuilder.addQueryParameter("timestamp", timestamp);
            urlBuilder.addQueryParameter("signature", signature);
        } else if (originalRequest.method().equals("POST")) {
            // Pour POST, les paramètres peuvent être dans le corps.
            // Il faut gérer la modification du corps si nécessaire.
            // L'exemple original modifiait le FormBody.
            // Ici, on ajoute potentiellement des en-têtes.
            requestBuilder.addHeader("X-Timestamp", timestamp);
            requestBuilder.addHeader("X-Signature", signature);
        }

        Request newRequest = requestBuilder.url(urlBuilder.build()).build();
        return chain.proceed(newRequest);
    }

    private String generateSignature(Request request, String timestamp) {
        // Logique de génération de signature personnalisée
        // Cela dépend de la manière dont la signature doit être calculée
        return "dummy_signature";
    }
}

Lancement des Requêtes

Dans le Presenter, appeler la méthode du service API et souscrire au Observable résultant en utilisant le BaseSubscriber personnalisé.


public class LoginPresenter {
    private final ApiService apiService;
    private final ILoginView loginView; // Interface pour la vue de connexion

    public LoginPresenter(ApiService apiService, ILoginView loginView) {
        this.apiService = apiService;
        this.loginView = loginView;
    }

    public void attemptLogin(String username, String password) {
        apiService.login(username, password)
                .map(new ApiErrorHandlingFunc<>()) // Gérer la réponse unifiée
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new BaseSubscriber<user>(loginView, true) { // true pour afficher le chargement
                    @Override
                    public void onSuccess(User user) {
                        loginView.onLoginSuccess(user);
                    }

                    @Override
                    public void onRequestFailed(String message) {
                         // La méthode onError de BaseSubscriber a déjà appelé cette méthode
                         // ici on peut ajouter des logiques spécifiques au succès partiel ou autre
                    }
                });
    }
}
</user>

Intégration avec Dagger2

Configurer Dagger2 pour fournir les instances nécessaires comme ApiService, OkHttpClient, et injecter ces dépendances dans les Activitys ou Fragments.


@Singleton
@Component(modules = {AppModule.class, NetworkModule.class})
public interface ApiComponent {
    ApiService getApiService();
    OkHttpClient getOkHttpClient();

    // Méthode pour injecter dans une Activity spécifique, par exemple
    void inject(LoginActivity activity);
}

// Dans une Activity :
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);

    // Injection des dépendances
    ((MyApp) getApplication()).getApiComponent().inject(this);

    // Initialisation du Presenter avec les dépendances injectées
    loginPresenter = new LoginPresenter(apiService, this);
}

Cette approche combinée de Retrofit, RxJava, Dagger2 et MVP permet de construire un système robuste et maintenable pour la gestion des requêtes réseau dans les applications Android.

Étiquettes: Android rxjava mvp retrofit dagger2

Publié le 9 juin à 10h02