OData + RxJava + Retrofit 2 für Android-Anwendungen

Konfrontiert mit einem Projekt mit einem bisher ungesehenen Problem. Ich musste die Dokumentation rauchen und in diesem Artikel werde ich Ihnen erklären, wie Sie mit RxJava und Retrofit 2 das Problem der Erstellung eines Odata- Clients für eine Android-Anwendung lösen können .

Vielen Dank, Jake Wharton, für die Erstellung so komfortabler Werkzeuge.

Willkommen in der Welt der Magie


Wir haben eine Anwendung, die gemäß dem Odata- Protokoll Daten vom Server extrahieren , in Listen anzeigen muss , die beim Scrollen geladen werden sollen, und die vom Benutzer erstellten Daten an den Server senden muss. Eine triviale Aufgabe, aber es war nicht hier, was in Java problemlos funktioniert - will auch nicht mit Android arbeiten.

Die Bibliotheken und Dokumentationen zu Odata stammen nur von Apache - Olingo und Microsoft in C #.

In diesem Artikel werde ich das Odata- Protokoll nicht berücksichtigen, Microsoft hat eine sehr gute Dokumentation und ich werde Links am Ende des Artikels hinterlassen.

Hier ist eine kurze Definition mit dem Wiki Open_Data_Protocol

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

4.0, OData — , OASIS.

Und hier beginnt der interessanteste Teil: Odata ist eine Art SQL in der REST-API, und genau das ist es für dynamisch generierte Daten.

Wir haben jedoch eine stark typisierte Sprache und ohne Kenntnis des Modells - Datenverarbeitung und -speicherung stellen eine ziemlich schwierige Aufgabe dar.

Die Lösung kann nicht Standard sein und wird im Netzwerk wiederholt beschrieben.

Ich werde noch mehr sagen, bis auf diese Links zur Dokumentation im Netzwerk - alles ist schlecht zu diesem Thema.

Jetzt machen wir die Magie:

Erstellen Sie einen Service für die Arbeit mit dem Netzwerk.

Wir werden Retrofit 2 und verwandte Square- Produkte verwenden.

Fügen Sie der Datei build.gradle Abhängigkeiten hinzu
// 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

Alle diese Abhängigkeiten sind die leistungsstärkste Netzwerkbibliothek in Java. Ich verstehe nicht

, wie man mit Retrofit 2 arbeitet. Hier ist ein gutes Handbuch: Wir verwenden Retrofit 2 in einer Android-Anwendung .

Erstellen Sie die NetworkService-Klasse:
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

Erstellen Sie die API:
public interface ApiService {
//    
@GET("odata/product")
Observable<List<ProductsDto>> getProducts();
}
listing 3

Und wir erstellen eine Art Controller, um Anforderungen abzurufen:
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

Das Poduct-Datenmodell wird also auf dem Server gespeichert, der einige Parameter und Attribute aufweist. Alle Daten sind im JSON-Format und für die Arbeit benötigen wir eine POJO-Klasse.

Ich empfehle in HttpLoggingInterceptor, die Ebene der Abfangdetails zu konfigurieren - Level.BODY.

Wir stellen eine Anfrage für Listing 4 , um die Datenstruktur zu maximieren, und die Antwort wird ungefähr in diesem Format vorliegen:

//         
//   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

Und bereits auf Basis dieser Daten ist es möglich, einen Antrag auf weitere Datenmanipulationen zu stellen.

Das heißt, dies ist ein vollwertiges Objekt mit einer Art von Verhalten und Attributen. Um diese Informationen beim Erstellen einer Anfrage zu erhalten, müssen Bedingungen hinzugefügt werden, auf deren Grundlage wir unsere DataSource erhalten, ohne ein neues Fahrrad zu erfinden und ohne Krücken daran zu schrauben.

Der Höhepunkt und die Freude der Welpen am Komfort des Instruments


Und hier ist der Moment wahr, die Kraft, Stärke und Einfachheit der Retrofit 2- Bibliothek . Jetzt können Sie Eigenschaften mithilfe des Odata- Servicedokuments abrufen :

//   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);

Listing 6

Hier ist die Silushka Heroic Library Retrofit 2 . Dynamic Url übernimmt all diese Masse an Parametern, mit denen Sie im Code spielen können, solange Sie genug Vorstellungskraft haben.

Es wird ungefähr so ​​aussehen:
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

Zusammenfassung


Es war eine nützliche Erfahrung, die ich gerne teile. Zu gegebener Zeit würde dieser Artikel wirklich eine Reihe von Problemen und Fragen beseitigen.

In dem Artikel habe ich nicht auf unnötige Details zu Retrofit 2 und OData eingegangen und Links zur Dokumentation angegeben, wenn es notwendig war, tiefer zu gehen.

Ich kann nicht die Vollversion des Codes bereitstellen, für die ich mich entschuldige. Das Produkt ist kommerziell.

Und wie versprochen Links:

→  Microsoft-Dokumentation: Open Data Protocol
→  Dokumentation OData 4.0 Java Library
→  Erstellen Sie voll funktionsfähige Internetanwendungen mit dem Open Data Protocol

All Articles