Retrofit là gì? Những kiến thức cần nắm về Retrofit trong Android

24516

Retrofit trong Android – Luôn là một chủ đề mà các Android Developer luôn thắc mắc. Vậy Retrofit là gì? Ngắn gọn, Retrofit là một công nghệ được phát triển bởi Square, nó được xây dựng dựa trên rất nhiều công nghệ mạnh mẽ cho phép giải quyết tốt các yêu cầu từ phía client và server một cách nhanh và hiệu quả nhất. Tóm lại, Retrofit là một REST Client dành Android và cả Java. Retrofit được phát triển giúp cho quá trình kết nối client – server trở nên dễ dàng, nhanh chóng. Đối với Retrofit bạn có thể GET, POST, PUT, DELETE …

Ngoài ra Retrofit ngày càng được phổ biến hơn với nhiều ưu điểm mà nó đem lại, ví dụ như là hiệu năng, thời gian hoàn thành task nhanh hơn các thư viện phổ biến khác như AsyncTask và Volley.

retrofit-la-gi
So sánh Retrofit với các thư viện khác

1. Retrofit là gì?

Nói một cách dễ hiểu hơn, Retrofit chính là một type-safe HTTP Client cho Java và Android. Nó làm cho việc truy xuất và tải lên JSON (hoặc dữ liệu có cấu trúc khác) tương đối dễ dàng thông qua một dịch vụ web dựa trên REST. Trong Retrofit, bạn định cấu hình bộ chuyển đổi nào được sử dụng để tuần tự hóa dữ liệu. Thông thường, đối với JSON, bạn sử dụng GSon, nhưng bạn có thể thêm bộ chuyển đổi tùy chỉnh để xử lý XML hoặc các giao thức khác. Retrofit sử dụng thư viện OkHttp cho các yêu cầu HTTP.

  Cách làm HTTPS hoạt động trên local trong 5 phút

Một ví dụ về Retrofit:

Ngoài ra những tiện lợi nêu trên, Retrofit còn hỗ trợ bạn phân tích chuỗi JSON bằng các thư viện chuyển đổi như thế này. Ở phần 4 bên dưới mình sẽ nói rõ hơn về các converter này.

-Moshi: com.squareup.retrofit:converter-moshi

-Jackson: com.squareup.retrofit:converter-jackson

-Gson: com.squareup.retrofit:converter-gson

Trên thế giới hiện nay, Retrofit rất phổ biến với ưu điểm của nó chính là hiệu năng, thời gian thực thi task nhanh hơn AsyncTask hay Volley. Một ví dụ về Retrofit.

// Retrofit turns your HTTP API into a Java interface

public interface GitHubService {  

  @GET("users/{user}/repos")

  Call> listRepos(@Path("user") String user);

}

// the Retrofit class generates an implementation of the above interface

Retrofit retrofit = new Retrofit.Builder()  

    .baseUrl("https://api.github.com/")

    .build();

GitHubService service = retrofit.create(GitHubService.class);

// each Call from the created GitHubService can make a synchronous or

// asynchronous HTTP request to the remote webserver

Call> repos = service.listRepos("octocat");  

// let's use the Call asynchronously

call.enqueue(new Callback>() {  

  @Override void onResponse(Response> contributors) {

    // ...
  }
});

Một lưu ý nhỏ là Retrofit không hỗ trợ tải hình ảnh, đa phần các lập trình viên sẽ sử dụng các giải pháp khác như dùng thư viện Glide hoặc Picasso để làm những tác vụ đấy.

Sử dụng Retrofit

Để làm việc với Retrofit về cơ bản, bạn cần ba lớp sau:

  • Lớp mô hình được sử dụng làm mô hình JSON
  • Các giao diện xác định các hoạt động HTTP có thể có
  • Lớp Retrofit.Builder – Phiên bản sử dụng giao diện và API Builder để cho phép xác định điểm cuối URL cho các hoạt động HTTP

Mỗi phương thức của một giao diện đại diện cho một lệnh gọi API khả thi. Nó phải có chú thích HTTP (GETPOST, v.v) để chỉ định loại yêu cầu và URL tương đối. Giá trị trả về bao bọc phản hồi trong một Call object với loại kết quả mong đợi.

@GET("users")
Call<List<User>> getUsers();

Bạn có thể sử dụng các khối thay thế và tham số truy vấn để điều chỉnh URL. Một khối thay thế được thêm vào URL tương đối với {}. Với sự giúp đỡ của @Path chú thích trên tham số phương thức, giá trị của tham số đó được liên kết với khối thay thế cụ thể.

@GET("users/{name}/commits")
Call<List<Commit>> getCommitsByName(@Path("name") String name);
@POST("users")
Call<User> postUser(@Body User user)

2. Điều kiện tiên quyết

Các ví dụ sau đang sử dụng Eclipse IDE cùng với hệ thống xây dựng Gradle. Bài tập này giả định rằng bạn đã quen thuộc với Gradle và Using Gradle với Eclipse.

Các công cụ khác như Visual Studio Code hoặc IntelliJ cho phép làm điều tương tự, vì vậy bạn sẽ có thể sử dụng công cụ yêu thích của mình.

3. Bài tập: A first retrofit client

Trong bài tập này, bạn sẽ tạo một ứng dụng khách REST độc lập. Các phản hồi được tạo bởi một mock server (server giả lập).

3.1. Tạo và thiết lập Project

Tạo Gradle project mới gọi com.huongdancode.retrofitgerrit. Thêm một package mới vào src/main/java với tên là com.huongdancode.retrofitgerrit.

Thêm các phần dependency sau vào file build.gradle.

// retrofit
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'

// Junit
testImplementation("org.junit.jupiter:junit-jupiter-api:5.0.0")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0")
// to run JUnit 3/4 tests:
testImplementation("junit:junit:4.12")
testRuntime("org.junit.vintage:junit-vintage-engine:4.12.0")

3.2. Xác định API và Retrofit adapter

Trong cái JSON trả về từ Gerrit, chúng tôi chỉ quan tâm đến subject của những thay đổi. Do đó, hãy tạo lớp data trong default package đã thêm trước đó.

package com.huongdancode.java.retrofitgerrit;

public class Change {
    String subject;

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }
}

Xác định API REST cho Retrofit qua giao diện sau.

package com.huongdancode.java.retrofitgerrit;

import java.util.List;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface GerritAPI {

    @GET("changes/")
    Call<List<Change>> loadChanges(@Query("q") String status);
}

Tạo lớp controller. Lớp này tạo ứng dụng Retrofit client, gọi API Gerrit và xử lý kết quả (in kết quả của lệnh gọi đến console).

package com.huongdancode.java.retrofitgerrit;

import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class Controller implements Callback<List<Change>> {

    static final String BASE_URL = "https://git.eclipse.org/r/";

    public void start() {
        Gson gson = new GsonBuilder()
                .setLenient()
                .create();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();

        GerritAPI gerritAPI = retrofit.create(GerritAPI.class);

        Call<List<Change>> call = gerritAPI.loadChanges("status:open");
        call.enqueue(this);

    }

    @Override
    public void onResponse(Call<List<Change>> call, Response<List<Change>> response) {
        if(response.isSuccessful()) {
            List<Change> changesList = response.body();
            changesList.forEach(change -> System.out.println(change.subject));
        } else {
            System.out.println(response.errorBody());
        }
    }

    @Override
    public void onFailure(Call<List<Change>> call, Throwable t) 
    {
        t.printStackTrace();
    }
}

Tạo một lớp với một main-method để khởi động controller.

package com.huongdancode.java.retrofitgerrit;

public class Main {

    public static void main(String[] args) {
        Controller controller = new Controller();
        controller.start();
    }
}

4. Các converter và adapter của Retrofit

4.1. Converter

Retrofit có thể được cấu hình để sử dụng một converter cụ thể. Converter này xử lý data (de)serialization. Một số converter đã có sẵn cho các định dạng serialization khác nhau.

  • Convert sang JSON:
    • Gson: com.squareup.retrofit:converter-gson
    • Jackson: com.squareup.retrofit:converter-jackson
    • Moshi: com.squareup.retrofit:converter-moshi
  • Convert sang Protocol Buffers:
    • Protobuf: com.squareup.retrofit:converter-protobuf
    • Wire: com.squareup.retrofit:converter-wire
  • Convert sang XML:
    • Simple XML: com.squareup.retrofit:converter-simplexml

Bên cạnh các converter được liệt kê, bạn cũng có thể tạo converter tùy chỉnh để xử lý các giao thức khác bằng cách phân lớp Converter.Factory.

4.2. Retrofit Adapters

Retrofit cũng có thể mở rộng adapter để kết hợp với các thư viện khác như RxJava 2.x, Java 8 và Guava.

Có thể tìm thấy tổng quan về các adapter có sẵn trên Github square/retrofit/retrofit-adapters/.

Ví dụ về RxJava 2.x adapter có thể được lấy bằng cách sử dụng Gradle:

compile 'com.squareup.retrofit2:adapter-rxjava2:latest.version'

hoặc sử dụng Apache Maven:

<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>adapter-rxjava2</artifactId>
  <version>latest.version</version>
</dependency>

Phương thức sử dụng để thêm adapter:

retrofit2.Retrofit.Builder.addCallAdapterFactory(Factory)

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com")
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .build();

Với việc áp dụng adapter này, các giao diện Retrofit có thể trả về các RxJava 2.x, như: Observable, Flowable hoặc Single, v.v.

@GET("users")
Observable<List<User>> getUsers();

5. Xác thực Retrofit

Retrofit hỗ trợ các lệnh gọi API cần xác thực. Việc xác thực có thể được thực hiện bằng cách sử dụng tên người dùng và mật khẩu (xác thực HTTP Basic) hoặc API token.

Có hai cách, cách bạn có thể xử lý xác thực. Cách một là xác thực có thể dụng username và password (HTTP Basic authentication) hoặc sử dụng Token. Một khả năng khác sẽ là sử dụng OkHttp interceptor cho việc đó.

5.1. Xác thực bằng annotation

Giả sử rằng bạn muốn yêu cầu thông tin chi tiết người dùng của mình, yêu cầu bạn xác thực. Bạn có thể làm điều này bằng cách thêm một tham số mới vào định nghĩa API của mình, như sau:

@GET("user")
Call<UserDetails> getUserDetails(@Header("Authorization") String credentials)

Với sự giúp đỡ của @Header("Authorization") annotation mà bạn yêu cầu Retrofit thêm vào mảng Authorization request header với giá trị bạn cung cấp cho credentials.

Để tạo thông tin xác thực cơ bản, bạn có thể sử dụng class Credentials với method basic(String, String). Method này lấy tên người dùng và mật khẩu và trả về thông tin xác thực cho basic.

Credentials.basic("ausername","apassword");

Nếu bạn muốn sử dụng API token và không có Basic authentication, chỉ cần gọi phương thức getUserDetails(String) với token của bạn.

5.2. OkHttp interceptor

Phương pháp trên chỉ thêm thông tin đăng nhập, nếu bạn yêu cầu chi tiết người dùng của mình. Nếu bạn có nhiều cuộc gọi yêu cầu bạn xác thực, bạn có thể sử dụng interceptor cho việc này. Một interceptor được sử dụng để sửa đổi từng request trước khi nó được thực hiện và thay đổi request header. Ưu điểm là bạn không phải thêm @Header("Authorization") cho mỗi API.

Khi phát triển ứng dụng trên nền tảng Android có những việc ta sẽ thấy khá nhàm chán như kiểm soát kích thước file download và thời gian download file đó, bắt lỗi và exception, xử lý kết nối mạng, ..v.v. Tuy nhiên có thứ giúp ta giải quyết những vấn đề đó một cách nhanh chóng, đó là OkHttp.

Để thêm một interceptor, bạn sẽ sử dụng method okhttp3.OkHttpClient.Builder.addInterceptor(Interceptor) trên OkHttp Builder.

OkHttpClient okHttpClient = new OkHttpClient().newBuilder().addInterceptor(new Interceptor() {
            @Override
            public okhttp3.Response intercept(Chain chain) throws IOException {
                Request originalRequest = chain.request();

                Request.Builder builder = originalRequest.newBuilder().header("Authorization",
                        Credentials.basic("aUsername", "aPassword"));

                Request newRequest = builder.build();
                return chain.proceed(newRequest);
            }
        }).build();

Tạo OkHttp client với method retrofit2.Retrofit.Builder.client(OkHttpClient).

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com")
    .client(okHttpClient)
    .build();

Bạn có thể nhận thấy việc sử dụng lớp Credentials cho Basic authentication. Một lần nữa, nếu bạn muốn sử dụng API token, chỉ cần sử dụng token thay thế.

6. Bài tập: Sử dụng Retrofit để truy vấn Gerrit trong Java

Phần sau đây mô tả cách tạo một ứng dụng Java tối thiểu sử dụng Retrofit để truy vấn các đối tượng từ API Gerrit. Kết quả được in trên console.

6.1. Tạo và thiết lập Project

Bài tập này giả định rằng bạn đã quen với Gradle va Buildship của Eclipse.

Tạo một dự án Gradle mới với tên com.huongdancode.java.retrofitgerrit. Thêm một package mới vào src/main/java với tên com.huongdancode.java.retrofitgerrit.

Thêm dependency vào file build.gradle

// retrofit
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'

// Junit
testImplementation("org.junit.jupiter:junit-jupiter-api:5.0.0")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0")
// to run JUnit 3/4 tests:
testImplementation("junit:junit:4.12")
testRuntime("org.junit.vintage:junit-vintage-engine:4.12.0")

6.2. Xác định API và Retrofit adapter

Trong cái JSON trả về từ Gerrit, chúng tôi chỉ quan tâm đến subject của những thay đổi. Do đó, hãy tạo lớp data trong default package đã thêm trước đó. (Xem để hiểu rõ hơn JSON là gì?)

package com.huongdancode.java.retrofitgerrit;

public class Change {
    String subject;

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }
}

Xác định REST API cho Retrofit qua giao diện sau.

package com.huongdancode.java.retrofitgerrit;

import java.util.List;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface GerritAPI {

    @GET("changes/")
    Call<List<Change>> loadChanges(@Query("q") String status);
}

Tạo lớp controller. Lớp này tạo ứng dụng Retrofit client, gọi API Gerrit và xử lý kết quả (in kết quả của lệnh gọi đến console).

package com.huongdancode.java.retrofitgerrit;

import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class Controller implements Callback<List<Change>> {

    static final String BASE_URL = "https://git.eclipse.org/r/";

    public void start() {
        Gson gson = new GsonBuilder()
                .setLenient()
                .create();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();

        GerritAPI gerritAPI = retrofit.create(GerritAPI.class);

        Call<List<Change>> call = gerritAPI.loadChanges("status:open");
        call.enqueue(this);

    }

    @Override
    public void onResponse(Call<List<Change>> call, Response<List<Change>> response) {
        if(response.isSuccessful()) {
            List<Change> changesList = response.body();
            changesList.forEach(change -> System.out.println(change.subject));
        } else {
            System.out.println(response.errorBody());
        }
    }

    @Override
    public void onFailure(Call<List<Change>> call, Throwable t) {
        t.printStackTrace();
    }
}

Tạo một class với main-method để khởi động controller.

package com.huongdancode.java.retrofitgerrit;

public class Main {

    public static void main(String[] args) {
        Controller controller = new Controller();
        controller.start();
    }
}

7. Bài tập: Sử dụng Retrofit để chuyển đổi XML response từ RSS

Phần này mô tả việc sử dụng Retrofit để chuyển đổi XML reponse với sự trợ giúp của SimpleXMLConverter.

7.1. Tạo và thiết lập Project

Về bài tập này thì mặc định là bạn đã biết về GradleBuildship của Eclipse.

Tạo một project Gradle mới với tên com.huongdancode.java.retrofitxml. Thêm package mới vào src/main/java với tên com.huongdancode.java.retrofitxml.

Thêm những dependency này vào file build.gradle.

implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:converter-simplexml:2.1.0'

7.2. Xác định mapping XML

RSS sẽ trông như thế này:

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
    <title>Eclipse and Android Information</title>
    <link>https://www.huongdancode.com</link>
    <description>Eclipse and Android Information</description>
    <language>en</language>
    <copyright>Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany (CC BY-NC-SA 3.0)</copyright>
    <pubDate>Tue, 03 May 2016 11:46:11 +0200</pubDate>
<item>
    <title>Android user interface testing with Espresso - Tutorial</title>
    <description> This tutorial describes how to test Android applications with the Android Espresso testing framework.</description>
    <link>https://www.huongdancode.com/tutorials/AndroidTestingEspresso/article.html</link>
    <author>minhvu@huongdancode.com (minh vu)</author>
    <guid>https://www.huongdancode.com/tutorials/AndroidTestingEspresso/article.html</guid>
</item>
<item>
    <title>Using the Gradle build system in the Eclipse IDE - Tutorial</title>
    <description>This article describes how to use the Gradle tooling in Eclipse</description>
    <link>https://www.huongdancode.com/tutorials/EclipseGradle/article.html</link>
    <author>minhvu@huongdancode.com (minh vu)</author>
    <guid>https://www.huongdancode.com/tutorials/EclipseGradle/article.html</guid>
</item>
<item>
    <title>Unit tests with Mockito - Tutorial</title>
    <description>This tutorial explains testing with the Mockito framework for writting software tests.</description>
    <link>https://huongdancode.com/tutorials/Mockito/article.html</link>
    <author>minhvu@huongdancode.com (minh vu)</author>
    <guid>https://www.huongdancode.com/tutorials/Mockito/article.html</guid>
</item>
</channel>
</rss>

Bên cạnh XML header, file này còn chứa các phần tử XML khác nhau. rss-element không chỉ chứa channel-element mà còn chứa các phần tử khác (như title, description, pubDate) và một số item-element.

Tạo hai lớp dữ liệu có tên RSSFeedArticle.

package com.huongdancode.java.retrofitxml;

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root(name = "item", strict = false)
public class Article {

    @Element(name = "title")
    private String title;

    @Element(name = "link")
    private String link;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getLink() {
        return link;
    }

    public void setLink(String link) {
        this.link = link;
    }
}
package com.huongdancode.java.retrofitxml;

import java.util.List;

import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Path;
import org.simpleframework.xml.Root;

@Root(name="rss", strict=false)
public class RSSFeed {

    @Element(name="title")
    @Path("channel")
    private String channelTitle;

    @ElementList(name="item", inline=true)
    @Path("channel")
    private List<Article> articleList;

    public String getChannelTitle() {
        return channelTitle;
    }

    public void setChannelTitle(String channelTitle) {
        this.channelTitle = channelTitle;
    }

    public List<Article> getArticleList() {
        return articleList;
    }

    public void setArticleList(List<Article> articleList) {
        this.articleList = articleList;
    }


}

Lớp Article đại diện cho một bài báo duy nhất và chỉ lưu trữ tiêu đề và liên kết đến nó. Đây là mảng duy nhất chúng tôi quan tâm.

Với annotation @Root class được đánh dấu là (de)serialized. Tùy theo lựa chọn, bạn có thể cung cấp tên trong annotation @Root đại diện cho tên của XML element.

Khi strict được set là false, strict parsing sẽ bị tắt. Điều này báo cho trình phân tích cú pháp không bị lỗi và đưa ra một ngoại lệ, nếu element hoặc attribute XML được tìm thấy mà không có ánh xạ nào được cung cấp.

7.3. Xác định API và Retrofit adapter

Xác định REST API cho Retrofit qua giao diện sau.

package com.huongdancode.java.retrofitxml;

import retrofit2.Call;
import retrofit2.http.GET;

public interface huongdancodeAPI {

    @GET("article.rss")
    Call<RSSFeed> loadRSSFeed();
}

Tạo lớp controller. Lớp này tạo ứng dụng Retrofit client, gọi API của huongdancode và xử lý kết quả (in kết quả của lệnh gọi đến console)

package com.huongdancode.java.retrofitxml;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.simplexml.SimpleXmlConverterFactory;

public class Controller implements Callback<RSSFeed> {

    static final String BASE_URL = "http://huongdancode.com/";

    public void start() {
        Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
                .addConverterFactory(SimpleXmlConverterFactory.create()).build();

        huongdancodeAPI huongdancodeAPI = retrofit.create(huongdancodeAPI.class);

        Call<RSSFeed> call = huongdancodeAPI.loadRSSFeed();
        call.enqueue(this);
    }

    @Override
    public void onResponse(Call<RSSFeed> call, Response<RSSFeed> response) {
        if (response.isSuccessful()) {
            RSSFeed rss = response.body();
            System.out.println("Channel title: " + rss.getChannelTitle());
            rss.getArticleList().forEach(
                    article -> System.out.println("Title: " + article.getTitle() + " Link: " + article.getLink()));
        } else {
            System.out.println(response.errorBody());
        }
    }

    @Override
    public void onFailure(Call<RSSFeed> call, Throwable t) {
        t.printStackTrace();
    }
}

Và bước cuối là tạo class với main để khởi động controller.

package com.huongdancode.java.retrofitxml;

public class Application {

    public static void main(String[] args) {
        Controller ctrl = new Controller();
        ctrl.start();
    }

}

Kết bài

Trên đây là bài giới thiệu sơ lược qua việc ứng dụng Retrofit trong lập trình Android, cảm ơn các bạn đã đọc tới đây. Nếu các bạn có góp ý gì cho bài viết này, hãy comment ở bên dưới nhé!

TopDev tổng hợp

Có thể bạn quan tâm:

Xem thêm tuyển android lương cao hấp dẫn tại TopDev