OData + RxJava + Retrofit 2 para la aplicación de Android

Frente a un proyecto con un problema nunca visto hasta ahora. Tuve que fumar la documentación y en este artículo le diré cómo usar RxJava y Retrofit 2 : puede resolver el problema de crear un cliente Odata para una aplicación de Android.

Muchas gracias Jake Wharton por crear herramientas tan cómodas.

Bienvenido al mundo de la magia.


Tenemos una aplicación que, de acuerdo con el protocolo Odata, debe extraer datos del servidor, mostrarlos en listas que se deben cargar a medida que se desplaza y enviar los datos creados por el usuario al servidor. Una tarea trivial, pero no estaba aquí, lo que funciona sin problemas en Java: no quiere trabajar también con Android.

Y las bibliotecas y la documentación en Odata son solo de Apache - Olingo y Microsoft en C #.

En este artículo no consideraré el protocolo Odata , Microsoft tiene muy buena documentación y dejaré enlaces al final del artículo.

Aquí hay una breve definición con el Wiki Open_Data_Protocol

Protocolo de datos abiertos (OData)
Open Data Protocol (OData) — - . , HTTP-, XML JSON.

4.0, OData — , OASIS.

Y aquí comienza la parte más interesante, Odata es un tipo de SQL en la API REST, y para los datos dinámicos es lo más importante.

Pero tenemos un lenguaje fuertemente tipado y sin conocimiento del modelo: el procesamiento y el almacenamiento de datos crean una tarea bastante difícil.

La solución no puede ser estándar y repetidamente descrita en la red.

Diré aún más, excepto por estos enlaces a la documentación en la red: todo es malo en el tema.

Ahora hagamos la magia:

cree un servicio para trabajar con la red.

Utilizaremos Retrofit 2 y productos Square relacionados.

agregar dependencias al archivo 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

Todas estas dependencias son la biblioteca de red más poderosa en Java. No veo el punto de

describir cómo trabajar con Retrofit 2 , hay un buen manual: utilizamos Retrofit 2 en una aplicación de Android .

Cree la clase 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

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

Y creamos algún tipo de controlador para extraer solicitudes:
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

Entonces, el modelo de datos Poduct se almacena en el servidor, que tiene algunos parámetros y atributos. Todos los datos van en formato JSON y para el trabajo necesitamos una clase POJO.

Recomiendo en HttpLoggingInterceptor configurar el nivel de detalle de intercepción - Level.BODY.

Hacemos una solicitud de listado 4 para maximizar la estructura de datos y la respuesta será aproximadamente en este formato:

//         
//   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" // 
    }
  ]
}

listado 5

Y ya sobre la base de estos datos, es posible realizar una solicitud para manipulaciones adicionales de datos.

Es decir, este es un objeto completo con algún tipo de comportamiento y atributos, para obtener esta información al crear una solicitud, es necesario agregar condiciones sobre la base de las cuales recibiremos nuestro DataSource sin inventar una bicicleta nueva y sin atornillar muletas.

El clímax y el deleite de los cachorros de la comodidad del instrumento.


Y aquí el momento es cierto, el poder, la fuerza y ​​la simplicidad de la biblioteca Retrofit 2 . Ahora puede obtener propiedades utilizando el documento de servicio 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);

Listado 6

Aquí está la biblioteca heroica de Silushka Retrofit 2 . Dynamic Url adquiere toda esta masa de parámetros con los que puedes jugar en el código mientras tengas suficiente imaginación.

Se verá algo como esto:
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

Resumen


Fue una experiencia útil, que me apresuro a compartir, a su debido tiempo este artículo realmente eliminaría un montón de problemas y preguntas.

En el artículo, no entré en detalles innecesarios sobre Retrofit 2 y OData e indiqué enlaces a la documentación si era necesario profundizar.

No puedo proporcionar la versión completa del código, por lo que pido disculpas, el producto es comercial.

Y, como se prometió, enlaces:

→  Documentación de Microsoft: Protocolo de datos abiertos
→  Documentación Biblioteca Java OData 4.0
→  Crear aplicaciones de Internet completamente funcionales utilizando el Protocolo de datos abiertos

All Articles