OData + RxJava + Retrofit 2 pour application Android

Face à un projet avec un problème jusque-là invisible. J'ai dû fumer la documentation et dans cet article, je vais vous expliquer comment utiliser RxJava et Retrofit 2 - vous pouvez résoudre le problème de la création d'un client Odata pour une application Android.

Merci beaucoup Jake Wharton d' avoir créé des outils aussi confortables.

Bienvenue dans le monde de la magie


Nous avons une application qui, selon le protocole Odata, doit extraire les données du serveur, les afficher dans des listes qui doivent être chargées lors du défilement et envoyer les données créées par l'utilisateur au serveur. Une tâche triviale, mais ce n'était pas le cas, ce qui fonctionne sans problème en Java - ne veut pas aussi fonctionner avec Android.

Et les bibliothèques et la documentation sur Odata proviennent uniquement d'Apache - Olingo et de Microsoft en C #.

Dans cet article, je ne considérerai pas le protocole Odata , Microsoft a une très bonne documentation et je laisserai des liens à la fin de l'article.

Voici une brève définition avec le Wiki Open_Data_Protocol

Protocole Open Data (OData)
Open Data Protocol (OData) — - . , HTTP-, XML JSON.

4.0, OData — , OASIS.

Et ici commence la partie la plus intéressante, Odata est une sorte de SQL dans l'API REST, et c'est ce que c'est pour les données générées dynamiquement.

Mais nous avons un langage fortement typé et sans connaissance du modèle - le traitement et le stockage des données créent une tâche assez difficile.

Dont la solution ne peut être standard et décrite à plusieurs reprises sur le réseau.

J'en dirai encore plus, à l'exception de ces liens vers la documentation sur le réseau - tout est mauvais sur le sujet.

Faisons maintenant la magie:

créer un service réseau.

Nous utiliserons Retrofit 2 et les produits Square associés

ajouter des dépendances au fichier build.gradle
// Network
    implementation 'com.squareup.retrofit2:retrofit:2.7.1' //   Retrofit 2
    implementation 'com.squareup.retrofit2:converter-gson:2.7.1' //     JSON 
    implementation 'com.squareup.okhttp3:logging-interceptor:4.3.1' //  
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.1' //     RxJava
    implementation 'com.squareup.okhttp3:okhttp:4.3.1' // OkHttp -  HTTP-
listing 1

Toutes ces dépendances sont la bibliothèque réseau la plus puissante de Java. Je ne vois pas l'intérêt de

décrire comment travailler avec Retrofit 2 , voici un bon manuel: Nous utilisons Retrofit 2 dans une application Android .

Créez la classe NetworkService:
public class NetworkService {

    private static final String TAG = "NetworkService";

    private static final NetworkService
            mInstance = new NetworkService();

    private String mToken;
    private Retrofit mRetrofit;

    public static NetworkService getInstance() {
        return mInstance;
    }

    private NetworkService() {

        RxJava2CallAdapterFactory rxAdapter =
                RxJava2CallAdapterFactory
                        .createWithScheduler(Schedulers.io());

        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 

        OkHttpClient.Builder okHttpClient =
                new OkHttpClient.Builder()
                        .addInterceptor(interceptor)
                        .addInterceptor(chain -> {
                            Request newRequest =
                                    chain.request().newBuilder()
                                           .addHeader("Accept",
                                                     "application/json,text/plain,*/*")
                                           .addHeader("Content-Type",
                                                    "application/json;odata.metadata=minimal")
                                           .addHeader("Authorization", mToken)
                                    .build();

                            return chain.proceed(newRequest);
                        });

        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
                .create();

        mRetrofit = new Retrofit.Builder()
                .baseUrl(Const.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(rxAdapter)
                .client(okHttpClient.build())
                .build();
    }
    public ApiService getNetworkClient(){
        return mRetrofit.create(ApiService.class);
    }
}

listing 2

Créez l'API:
public interface ApiService {
//    
@GET("odata/product")
Observable<List<ProductsDto>> getProducts();
}
listing 3

Et nous créons une sorte de contrôleur pour extraire les demandes:
public class ProductsController {

    private ApiService mApiService;

    private List<ProductsDto> listProducts;
    private Gson gson;

    public ProductsController() {

        mApiService = App.getNetworkService().getNetworkClient();

        listProducts = new ArrayList<>();

        gson = new Gson();
    }

    public void productsBtnClick() {

        mApiService.getProducts()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DisposableObserver<List<ProductsDto>>() {
                    @Override
                    public void onNext(List<ProductsDto> products) {

                        listProducts.addAll(listProducts);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {
                    }
                });
    }
listing 4

Ainsi, le modèle de données Poduct est stocké sur le serveur, qui a certains paramètres et attributs. Toutes les données sont au format JSON et pour le travail, nous avons besoin d'une classe POJO.

Je recommande dans HttpLoggingInterceptor de configurer le niveau de détail d'interception - Level.BODY.

Nous faisons une demande de listage 4 pour maximiser la structure des données et la réponse sera approximativement dans ce format:

//         
//   JSON
{
  "@odata.context":"https://example.xxx/api/odata/$metadata","value":[
    {
      "name":"product","kind":"EntitySet","url":"product" //  
    },{
      "name":"blogs","kind":"EntitySet","url":"blogs"  // 
    },{
      "name":"posts","kind":"EntitySet","url":"posts"  // 
    },{
      "name":"comments","kind":"EntitySet","url":"comments" // 
    },{
      "name":"rates","kind":"EntitySet","url":"rates" // 
    }
  ]
}

listing 5

Et déjà sur la base de ces données, il est possible de faire une demande pour d'autres manipulations de données.

Autrement dit, il s'agit d'un objet à part entière avec une sorte de comportement et d'attributs, afin d'obtenir ces informations lors de la création d'une demande, il est nécessaire d'ajouter des conditions sur la base desquelles nous recevrons notre DataSource sans inventer un nouveau vélo et sans lui visser de béquilles.

L'apogée et le plaisir des chiots du confort de l'instrument


Et ici, le moment est vrai, la puissance, la force et la simplicité de la bibliothèque Retrofit 2 . Vous pouvez maintenant obtenir des propriétés à l'aide du document de service Odata :

//   properties  product
@GET("odata/product?$filter=Id eq 111&$expand=dateReading($orderby=Date desc")
Observable<List<ProductsDto>> getProducts();

//  properties  blogs
@GET("odata/blogs?$orderby=Date desc")
Observable<List<BlogsDto>> getBlogs();

//        
@GET("odata/product?$filter=((Id eq 19) and (Name eq 'Available')) and ((Status eq 'OPEN') or ((Status eq 'CLOSED') and (Date ge 2020-02-13T06:39:48Z)))&$orderby=Status asc,Date desc&$top=10&$expand=AuthorId,CategoryId($expand=weight)&$count=true")
Observable<List<ProductsDto>> getProducts();
//        ,
//       . 

// :
@GET
Observable<List<ProductsDto>> getProducts(@Url String url);

liste 6

Voici la bibliothèque héroïque de Silushka Retrofit 2 . Dynamic Url prend toute cette masse de paramètres avec lesquels vous pouvez jouer dans le code aussi longtemps que vous avez assez d'imagination.

Cela ressemblera à ceci:
private void buttonGetProduct() {
//     
        String one = "odata/product?$filter=Id eq ";
        String id = "777";
        String tree = "&$expand=dateReading($orderby=Date desc)";
        String url = one + id + tree;

        mApiService.getProduct(url)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DisposableObserver<List<ProductDto>>() {
                    @Override
                    public void onNext(List<ProductDto> products) {

//       ,     JSON,
//         
                        mProductsDto.addAll(countersDtos);
                    }

                    @Override
                    public void onError(Throwable e) {
                    }

                    @Override
                    public void onComplete() {
                    }
                });
    }
listing 7

Sommaire


Ce fut une expérience utile, que je m'empresse de partager, en temps voulu, cet article éliminerait vraiment un tas de problèmes et de questions.

Dans l'article, je ne suis pas entré dans des détails inutiles sur Retrofit 2 et OData et j'ai indiqué des liens vers la documentation s'il était nécessaire d'approfondir.

Je ne peux pas fournir la version complète du code, pour lequel je m'excuse, le produit est commercial.

Et, comme promis, des liens:

→  Documentation Microsoft: Open Data Protocol
→  Documentation OData 4.0 Java Library
→  Créer des applications Internet entièrement fonctionnelles en utilisant Open Data Protocol

All Articles