OData + RxJava + Retrofit 2 para aplicação android

Diante de um projeto com um problema até então não visto. Eu tive que fumar a documentação e, neste artigo, mostrarei como usar o RxJava e o Retrofit 2 - você pode resolver o problema de criar um cliente Odata para um aplicativo Android.

Muito obrigado Jake Wharton por criar essas ferramentas confortáveis.

Bem-vindo ao mundo da magia


Temos um aplicativo que, de acordo com o protocolo Odata, deve extrair dados do servidor, exibi-los em listas que devem ser carregadas à medida que rola e enviar os dados criados pelo usuário para o servidor. Uma tarefa trivial, mas não estava aqui, o que funciona sem problemas em Java - também não quer funcionar com o Android.

E as bibliotecas e a documentação no Odata são apenas do Apache - Olingo e Microsoft em C #.

Neste artigo , não considerarei o protocolo Odata , a Microsoft possui uma documentação muito boa e deixarei os links no final do artigo.

Aqui está uma breve definição com o Wiki Open_Data_Protocol

Protocolo de dados abertos (OData)
Open Data Protocol (OData) — - . , HTTP-, XML JSON.

4.0, OData — , OASIS.

E aqui começa a parte mais interessante: Odata é um tipo de SQL na API REST e, para dados dinâmicos , é exatamente isso.

Mas temos uma linguagem fortemente tipada e sem conhecimento do modelo - o processamento e o armazenamento de dados criam uma tarefa bastante difícil.

A solução não pode ser padrão e descrita repetidamente na rede.

Vou dizer ainda mais, exceto por esses links para a documentação na rede - tudo está ruim sobre o assunto.

Agora vamos fazer a mágica:

Criar um serviço para trabalhar com a rede.

Usaremos Retrofit 2 e relacionados Quadrados produtos.

inclua dependências no arquivo 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 essas dependências são a biblioteca de rede mais poderosa em Java. Não vejo o objetivo de

descrever como trabalhar com o Retrofit 2 , aqui está um bom manual: usamos o Retrofit 2 em um aplicativo Android .

Crie a 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

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

E criamos algum tipo de controlador para receber solicitações:
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

Portanto, o modelo de dados Poduct é armazenado no servidor, que possui alguns parâmetros e atributos. Todos os dados vão no formato JSON e, para o trabalho, precisamos de uma classe POJO.

Eu recomendo no HttpLoggingInterceptor para configurar o nível de detalhe de interceptação - Level.BODY.

Fazemos uma solicitação de listagem 4 para maximizar a estrutura de dados e a resposta será aproximadamente neste 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" // 
    }
  ]
}

Listagem 5

E já com base nesses dados, é possível solicitar uma manipulação adicional de dados.

Ou seja, este é um objeto completo com algum tipo de comportamento e atributos, para obter essas informações ao criar uma solicitação, é necessário adicionar condições com base nas quais receberemos nosso DataSource sem inventar uma bicicleta nova e sem apertar muletas.

O clímax e o deleite dos filhotes com o conforto do instrumento


E aqui o momento é verdadeiro, o poder, a força e a simplicidade da biblioteca Retrofit 2 . Agora você pode obter propriedades usando o documento de serviço 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);

Listagem 6

Aqui está a heroica biblioteca Silushka, Retrofit 2 . O URL dinâmico assume toda essa massa de parâmetros com os quais você pode reproduzir o código enquanto tiver imaginação suficiente.

Vai parecer algo assim:
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

Sumário


Foi uma experiência útil, que me apressei em compartilhar, no devido tempo, este artigo realmente removeria muitos problemas e perguntas.

No artigo, não entrei em detalhes desnecessários no Retrofit 2 e OData e indiquei links para a documentação se houvesse necessidade de aprofundar.

Não posso fornecer a versão completa do código, pelo qual peço desculpas, o produto é comercial.

E, como prometido, vincula:

→  Documentação da Microsoft: Open Data Protocol
→  Documentação OData 4.0 Java Library
→  Crie aplicativos da Internet totalmente funcionais usando o Open Data Protocol

All Articles