Trang chủHướng dẫnRetrofit trong Android: Hướng dẫn sử dụng và ví dụ chi tiết

Retrofit trong Android: Hướng dẫn sử dụng và ví dụ chi tiết

CyStack blog 6 phút để đọc
CyStack blog08/07/2025
Locker Avatar

Chris Pham

Technical Writer

Locker logo social
Reading Time: 6 minutes

Chào mừng bạn đến với bài hướng dẫn về Retrofit trong Android. Hôm nay, chúng ta sẽ sử dụng thư viện do Square phát triển này để xử lý các yêu cầu REST API trong ứng dụng Android.

Retrofit trong Android

Retrofit trong Android

Retrofit là một REST client có tính an toàn kiểu (type-safe) dành cho Android và Java, giúp đơn giản hóa việc sử dụng các web service dạng RESTful. Chúng ta sẽ bỏ qua các phiên bản Retrofit 1.x và đi thẳng vào Retrofit 2 vì phiên bản này có nhiều tính năng mới và API nội bộ của nó đã thay đổi so với các phiên bản trước.

Retrofit 2 mặc định tận dụng OkHttp làm lớp mạng (networking layer) và xây dựng dựa trên nó. Retrofit tự động serialize (đổi đối tượng sang định dạng khác có tính tuần tự hóa) các response dạng JSON bằng một POJO (Plain Old Java Object) mà ta phải định nghĩa trước cho cấu trúc JSON. Để serialize JSON, chúng ta cần một bộ chuyển đổi như Gson.

Ta cần thêm các phụ thuộc sau vào file build.gradle.

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

Các phụ thuộc của OkHttp đã được tích hợp sẵn trong phụ thuộc của Retrofit 2. Nếu muốn sử dụng một phụ thuộc OkHttp riêng, ta nên loại bỏ phụ thuộc OkHttp khỏi Retrofit 2 như sau:

compile ('com.squareup.retrofit2:retrofit:2.1.0') {  
  // exclude Retrofit’s OkHttp dependency module and define your own module import
  exclude module: 'okhttp'
}
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
compile 'com.squareup.okhttp3:okhttps:3.4.1'
  • logging-interceptor sẽ tạo ra một chuỗi log ghi lại toàn bộ response trả về.
  • Ngoài ra, còn có các bộ chuyển đổi khác để parse JSON sang kiểu dữ liệu cần thiết. Dưới đây là một vài trong số đó:
    1. Jackson : com.squareup.retrofit2:converter-jackson:2.5.0
    2. Moshi : com.squareup.retrofit2:converter-moshi:2.5.0
    3. Protobuf : com.squareup.retrofit2:converter-protobuf:2.5.0
    4. Wire : com.squareup.retrofit2:converter-wire:2.5.0
    5. Simple XML : com.squareup.retrofit2:converter-simplexml:2.5.0

Thêm quyền truy cập internet vào file AndroidManifest.xml.

Các Interceptor của OkHttp

Interceptor là một cơ chế mạnh mẽ có trong OkHttp để theo dõi, viết lại và thử lại các lời gọi/yêu cầu. Interceptor có thể được chia thành hai loại chính:

  • Interceptor ứng dụng: Để đăng ký một interceptor ứng dụng, ta cần gọi phương thức addInterceptor() trên OkHttpClient.Builder.
  • Interceptor mạng: Để đăng ký một interceptor mạng, hãy gọi addNetworkInterceptor() thay vì addInterceptor().

Thiết lập Interface cho Retrofit

package com.journaldev.retrofitintro;

import com.journaldev.retrofitintro.pojo.MultipleResource;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

class APIClient {

    private static Retrofit retrofit = null;

    static Retrofit getClient() {

        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

        retrofit = new Retrofit.Builder()
                .baseUrl("<https://reqres.in>")
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();

        return retrofit;
    }

}

Phương thức getClient() trong đoạn code trên sẽ được gọi mỗi khi thiết lập một interface Retrofit. Retrofit cung cấp một danh sách các annotation cho mỗi phương thức HTTP: @GET, @POST, @PUT, @DELETE, @PATCH@HEAD.

Đây là class APIInterface.java

package com.journaldev.retrofitintro;

import com.journaldev.retrofitintro.pojo.MultipleResource;
import com.journaldev.retrofitintro.pojo.User;
import com.journaldev.retrofitintro.pojo.UserList;

import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;

interface APIInterface {

    @GET("/api/unknown")
    Call<MultipleResource> doGetListResources();

    @POST("/api/users")
    Call<User> createUser(@Body User user);

    @GET("/api/users?")
    Call<UserList> doGetUserList(@Query("page") String page);

    @FormUrlEncoded
    @POST("/api/users?")
    Call<UserList> doCreateUserWithField(@Field("name") String name, @Field("job") String job);
}

Trong class trên, chúng ta đã định nghĩa một số phương thức thực hiện các yêu cầu HTTP với annotation. @GET("api/unknown") sẽ gọi phương thức doGetListResources();. doGetListResources() là tên phương thức.

MultipleResource.java là một class Model POJO cho đối tượng response, được dùng để map các tham số của response với các biến tương ứng. Các class POJO này đóng vai trò là kiểu trả về của phương thức. Dưới đây là một class POJO đơn giản cho MultipleResource.java.

package com.journaldev.retrofitintro.pojo;

import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;

public class MultipleResource {

    @SerializedName("page")
    public Integer page;
    @SerializedName("per_page")
    public Integer perPage;
    @SerializedName("total")
    public Integer total;
    @SerializedName("total_pages")
    public Integer totalPages;
    @SerializedName("data")
    public List<Datum> data = null;

    public class Datum {

        @SerializedName("id")
        public Integer id;
        @SerializedName("name")
        public String name;
        @SerializedName("year")
        public Integer year;
        @SerializedName("pantone_value")
        public String pantoneValue;

    }
}

Annotation @SerializedName được dùng để chỉ định tên của trường trong phản hồi JSON.

Phản hồi JSON của ứng dụng Retrofit trên Android

Xem qua class POJO và sao chép nó vào cấu trúc project Android Studio của bạn. Các class POJO được bao bọc trong một class Call của Retrofit. Lưu ý là một JSONArray được serialize thành một List chứa các đối tượng trong các class POJO.

Tham số phương thức: Có rất nhiều lựa chọn tham số để truyền vào một phương thức:

  • @Body: Gửi các đối tượng Java dưới dạng body của request.
  • @Path: Sử dụng URL động.
  • @Query: Ta có thể thêm một tham số phương thức với @Query và tên của tham số query đi kèm kiểu dữ liệu. Để mã hóa một query theo URL, sử dụng định dạng: @Query(value = "auth_token",encoded = true) String auth_token.
  • @Field: Gửi dữ liệu dưới dạng form-urlencoded (định dạng dữ liệu biểu mẫu gửi qua HTTP). Việc này yêu cầu một annotation @FormUrlEncoded được đính kèm với phương thức. Tham số @Field chỉ hoạt động với POST.

Lưu ý: @Field yêu cầu một tham số bắt buộc. Trong trường hợp @Field có tính tùy chọn, ta có thể sử dụng @Query và truyền giá trị null.

Cấu trúc project ví dụ dùng Retrofit trên Android

Cấu trúc project ví dụ

Package pojo định nghĩa bốn class dữ liệu cho mỗi response từ điểm cuối (endpoint) API (được định nghĩa trong class APIInterface.java).

File User.java:

package com.journaldev.retrofitintro.pojo;

import com.google.gson.annotations.SerializedName;

public class User {

    @SerializedName("name")
    public String name;
    @SerializedName("job")
    public String job;
    @SerializedName("id")
    public String id;
    @SerializedName("createdAt")
    public String createdAt;

    public User(String name, String job) {
        this.name = name;
        this.job = job;
    }

}

Class trên được sử dụng để tạo body của response cho phương thức createUser().

File UserList.java:

package com.journaldev.retrofitintro.pojo;

import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;

public class UserList {

    @SerializedName("page")
    public Integer page;
    @SerializedName("per_page")
    public Integer perPage;
    @SerializedName("total")
    public Integer total;
    @SerializedName("total_pages")
    public Integer totalPages;
    @SerializedName("data")
    public List<Datum> data = new ArrayList();

    public class Datum {

        @SerializedName("id")
        public Integer id;
        @SerializedName("first_name")
        public String first_name;
        @SerializedName("last_name")
        public String last_name;
        @SerializedName("avatar")
        public String avatar;

    }
}

File CreateUserResponse.java:

package com.journaldev.retrofitintro.pojo;

import com.google.gson.annotations.SerializedName;

public class CreateUserResponse {

    @SerializedName("name")
    public String name;
    @SerializedName("job")
    public String job;
    @SerializedName("id")
    public String id;
    @SerializedName("createdAt")
    public String createdAt;
}

MainActivity.java là nơi chúng ta gọi mỗi điểm cuối API đã được định nghĩa trong class Interface và hiển thị từng trường trong một Toast/TextView.

package com.journaldev.retrofitintro;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.journaldev.retrofitintro.pojo.CreateUserResponse;
import com.journaldev.retrofitintro.pojo.MultipleResource;
import com.journaldev.retrofitintro.pojo.User;
import com.journaldev.retrofitintro.pojo.UserList;

import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity {

    TextView responseText;
    APIInterface apiInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        responseText = (TextView) findViewById(R.id.responseText);
        apiInterface = APIClient.getClient().create(APIInterface.class);

        /**
         GET List Resources
         **/
        Call<MultipleResource> call = apiInterface.doGetListResources();
        call.enqueue(new Callback<MultipleResource>() {
            @Override
            public void onResponse(Call<MultipleResource> call, Response<MultipleResource> response) {

                Log.d("TAG",response.code()+"");

                String displayResponse = "";

                MultipleResource resource = response.body();
                Integer text = resource.page;
                Integer total = resource.total;
                Integer totalPages = resource.totalPages;
                List<MultipleResource.Datum> datumList = resource.data;

                displayResponse += text + " Page\\n" + total + " Total\\n" + totalPages + " Total Pages\\n";

                for (MultipleResource.Datum datum : datumList) {
                    displayResponse += datum.id + " " + datum.name + " " + datum.pantoneValue + " " + datum.year + "\\n";
                }

                responseText.setText(displayResponse);

            }

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

        /**
         Create new user
         **/
        User user = new User("morpheus", "leader");
        Call<User> call1 = apiInterface.createUser(user);
        call1.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                User user1 = response.body();

                Toast.makeText(getApplicationContext(), user1.name + " " + user1.job + " " + user1.id + " " + user1.createdAt, Toast.LENGTH_SHORT).show();

            }

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

        /**
         GET List Users
         **/
        Call<UserList> call2 = apiInterface.doGetUserList("2");
        call2.enqueue(new Callback<UserList>() {
            @Override
            public void onResponse(Call<UserList> call, Response<UserList> response) {

                UserList userList = response.body();
                Integer text = userList.page;
                Integer total = userList.total;
                Integer totalPages = userList.totalPages;
                List<UserList.Datum> datumList = userList.data;
                Toast.makeText(getApplicationContext(), text + " page\\n" + total + " total\\n" + totalPages + " totalPages\\n", Toast.LENGTH_SHORT).show();

                for (UserList.Datum datum : datumList) {
                    Toast.makeText(getApplicationContext(), "id : " + datum.id + " name: " + datum.first_name + " " + datum.last_name + " avatar: " + datum.avatar, Toast.LENGTH_SHORT).show();
                }

            }

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

        /**
         POST name and job Url encoded.
         **/
        Call<UserList> call3 = apiInterface.doCreateUserWithField("morpheus","leader");
        call3.enqueue(new Callback<UserList>() {
            @Override
            public void onResponse(Call<UserList> call, Response<UserList> response) {
                UserList userList = response.body();
                Integer text = userList.page;
                Integer total = userList.total;
                Integer totalPages = userList.totalPages;
                List<UserList.Datum> datumList = userList.data;
                Toast.makeText(getApplicationContext(), text + " page\\n" + total + " total\\n" + totalPages + " totalPages\\n", Toast.LENGTH_SHORT).show();

                for (UserList.Datum datum : datumList) {
                    Toast.makeText(getApplicationContext(), "id : " + datum.id + " name: " + datum.first_name + " " + datum.last_name + " avatar: " + datum.avatar, Toast.LENGTH_SHORT).show();
                }

            }

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

    }
}

apiInterface = APIClient.getClient().create(APIInterface.class); được dùng để khởi tạo APIClient. Để map class Model với response, ta sử dụng: MultipleResource resource = response.body();.

Ứng dụng trên khi chạy sẽ gọi từng endpoint và hiển thị một thông báo Toast tương ứng cho chúng.

Tổng kết

Bài hướng dẫn sử dụng Retrofit trong Android của chúng tôi đến đây là kết thúc. Bạn có thể tải xuống project ví dụ ở trên từ link này. Hãy áp dụng ngay những kĩ thuật này để tối ưu hóa việc quản lý các yêu cầu mạng và mở rộng khả năng kết nối hiệu quả với các API.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất