Quem sou eu? Rafael Toledo Dev Java / Android www.rafaeltoledo.net Integração REST ● Praticamente nenhum app funciona isoladamente ● Integração com APIs é essencial ● Eficiência é um requisito, sempre! Material Necessário Material Necessário Material Necessário Material Necessário Tesoura Sem Ponta (para não se machucar) REST de Referência https://api.example.com GET - /product /product/1 /product?name=? POST - /product (json no corpo) PUT - /product/1 (json no corpo) DELETE - /product/1 Product JSON { "id": 55041 "name": "Biscoito (ou bolacha?) Recheado", "categories-array": [ "doce", "biscoito", "bolacha" ], "description": "...", "permalink": "http://example.com/product/55041", } Método 1 - Apache HttpClient final String ENDPOINT = "https://api.example.com"; HttpClient client = DefaultHttpClient.getInstance(); HttpGet get = new HttpGet(ENDPOINT + "/product"); HttpResponse response = client.execute(get); if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { String raw = EntityUtils.toString(response.getEntity(), "UTF-8"); JSONArray json = new JSONArray(raw); for (int i = 0; i < json.length(); i++) { Product product = new Product(); product.setName(json.getJSONObject(i).getString("name")); product.setId(json.getJSONObject(i).getLong("id")); ... Agravante Uso de AsyncTasks, Services ou WorkerThreads Gerenciar a request em uma thread separada / atualizar a UI na main thread OkHttp http://square.github.io/okhttp SPDY/HTTP GZIP transparente Gerenciamento de Cache Recover automático em caso de falhas de rede Método 1A - OkHttp Apache Wrapper HttpClient client = new OkApacheClient(); HttpGet get = new HttpGet(ENDPOINT + "/product"); HttpResponse response = client.execute(get); if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { String raw = EntityUtils.toString(response.getEntity(), "UTF-8"); JSONArray json = new JSONArray(raw); for (int i = 0; i < json.length(); i++) { Product product = new Product(); product.setName(json.getJSONArray(i).getString("name")); ... } Dependências // build.gradle do módulo do app ... dependencies { ... compile 'com.squareup.okhttp:okhttp-apache:2.0.+' } Método 2 - OkHttp OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(ENDPOINT + "/product").build(); Response response = client.newCall(request).execute(); String raw = response.body().string(); JSONArray json = new JSONArray(raw); ... Dependências // build.gradle do módulo do app ... dependencies { ... compile 'com.squareup.okhttp:okhttp:2.0.+' } Método 2A - OkHttp + Gson public class Product { private String name; @SerializedName("categories-array") private List<String> categoriesArray; ... } Método 2A - OkHttp + Gson OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(ENDPOINT + "/product").build(); Response response = client.newCall(request).execute(); String raw = response.body().string(); JSONArray json = new JSONArray(raw); Gson gson = new Gson(); for (int i = 0; i < json.length(); i++) { Product product = gson .fromJson(json.getJSONObject(i).toString(), Product.class); ... } Método 2A - OkHttp + Gson OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(ENDPOINT + "/product").build(); Response response = client.newCall(request).execute(); String raw = response.body().string(); Type listType = new TypeToken<List<Product>>{}.getType(); List<Product> products = new Gson().fromJson(raw, listType); Dependências // build.gradle do módulo do app ... dependencies { ... compile 'com.squareup.okhttp:okhttp:2.0.+' compile 'com.google.code.gson:gson:2.3' } Retrofit http://square.github.io/retrofit Transforma o seu REST em uma interface! Integração com o OkHttp (classpath) Método 3 - Retrofit public interface ExampleService { @GET("/product") List<Product> getProducts(); @POST("/product") void saveProduct(@Body Product product); @PUT("/product/{id}") void updateProduct(@Path("id") long id, @Body Product product); @DELETE("/product/{id}") void deleteProduct(@Path("id") long id); } Método 3 - Retrofit public interface ExampleService { @GET("/product/{id}") Product getProduct(@Path("id") long id); @GET("/product") Product getProductByName(@Query("name") String name); } Método 3 - Retrofit public class ExampleApi { private static ExampleService instance; public static ExampleService getInstance() { if (instance == null) { RestAdapter adapter = new RestAdapter.Builder() .setEndpoint("https://api.example.com") .build(); instance = adapter.create(ExampleService.class); } return instance; } } Método 3 - Retrofit E pronto! Product product = ExampleApi.getInstance().getProduct(1); Porém Ainda depende do uso de AsyncTasks, Services ou WorkerThreads Método 3A - Retrofit com Callbacks public interface ExampleService { @GET("/product") List<Product> getProducts(); // ...modificamos para... @GET("/product") void getProducts(Callback<List<Product>> callback); Método 3A - Retrofit com Callbacks E pronto! ExampleApi.getInstance().getProduct(1, new Callback<Product>() { @Override public void success(Product product, Response response) {} @Override public void failure(RetrofitError error) {} }); Vantagens Gerenciado automaticamente para os callbacks serem executados na UI Dispensa o tratamento de exceptions na request (método failure) Dependências // build.gradle do módulo do app ... dependencies { ... compile 'com.squareup.okhttp:okhttp:2.0.+' compile 'com.squareup.retrofit:retrofit:1.7.+' } Calma!!! ainda pode melhorar... Por que melhorar mais ainda? ● Verbosidade ● Código macarrônico ● Bagunça de código com chamadas encadeadas ● Muitas classes anônimas RxJava Programação Funcional Reativa no Java É uma das grandes febres no Open Source Android hoje Ganhou mais fama ainda por causa do port Android feito pela Netflix RxJava http://github.com/ReactiveX/RxJava Retrofit é compatível com RxJava! http://blog.danlew.net/ - Blog do GDE Dan Lew, com uma série de 4 tutoriais sobre RxJava Método 4 - Retrofit e RxJava public interface ExampleService { @GET("/product/{id}") void getProduct(@Path("id") long id, Callback<Product> callback); // ...modificamos para... @GET("/product/{id}") Observable<Product> getProduct(@Path("id") long id); Método 4 - Retrofit e RxJava ExampleApi.getInstance().getProduct(1).subscribe(onSuccess, onError); protected Action1<Product> onSuccess = new Action1<Product>() { @Override public void call(Product product) {} }; protected Action1<Throwable> onError = new Action1<Throwable>() { @Override public void call(Throwable error) {} }; Dependências // build.gradle do módulo do app ... dependencies { ... compile 'com.squareup.okhttp:okhttp:2.0.+' compile 'com.squareup.retrofit:retrofit:1.7.+' compile 'com.netflix.rxjava:rxjava-android:0.20.+' } Qual a necessidade disso? ● Chamada mais limpa e clara ● Maior coesão no tratamento de erro / sucesso ● Se o tratamento for comum a todas as Activities / Fragments, você pode criar a implementação comum e estender :) Calma aí!!! E como fica o controle na thread de UI? Já testei e deu pau! Método 4 - Retrofit e RxJava ExampleApi.getInstance().getProduct(1) .observeOn(AndroidSchedulers.mainThread()) .subscribe(onSuccess, onError); Apesar de bacana... Ainda temos o problema da "verbosidade" do Java Retrolambda Porque o Java 5/6/7 não poderia ficar de fora! https://github.com/orfjackal/retrolambda Traz ao Java 5/6/7 uma das mais poderosas características do Java 8: o uso de lambdas! Retrolambda Como o Java do Android é o 6 e 7 (full, a partir do Kitkat), precisávamos disso! https://github.com/evant/gradle-retrolambda Retrolambda - Como Usar // no build.gradle principal buildscript { dependencies { classpath 'com.android.tools.build:gradle:0.13.3' classpath 'me.tatarka:gradle-retrolambda:2.4.0' } } Retrolambda - Como Usar // no build.gradle do projeto apply plugin: 'com.android.application' apply plugin: 'retrolambda' android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } Método 4 - Sem Retrolambda ExampleApi.getInstance().getProduct(1).subscribe(onSuccess, onError); protected Action1<Product> onSuccess = new Action1<Product>() { @Override public void call(Product product) {} }; protected Action1<Throwable> onError = new Action1<Throwable>() { @Override public void call(Throwable error) {} }; Método 5 - Com Retrolambda ExampleApi.getInstance().getProduct(1).subscribe(onSuccess, onError); protected Action1<Product> onSuccess = product -> { }; protected Action1<Throwable> onError = error -> { }; Método 5 - Com Retrolambda protected Action1<Throwable> onError = error -> { dialog.dismiss(); }; protected Action1<Throwable> onError = error -> dialog.dismiss(); Obrigado! www.rafaeltoledo.net