Используем Retrofit 2 в Android-приложении – Devcolibri

Используем Retrofit 2 в Android-приложении

Используем Retrofit 2 в Android-приложении

Retrofit — это известная среди Android-разработчиков библиотека для сетевого взаимодействия, некоторые даже считают её в каком-то роде стандартом. Причин для такой популярности масса: библиотека отлично поддерживает REST API, легко тестируется и настраивается, а запросы по сети с её помощью выполняются совсем просто. В этой статье я покажу вам, как настроить и использовать Retrofit, чтобы реализовать работу с сетью в вашем приложении.

Настройка Retrofit

Добавьте следующую зависимость в файл build.gradle:

implementation 'com.squareup.retrofit2:retrofit:2.4.0'

Мы будем использовать Gson для преобразования JSON в POJO. Retrofit предоставляет зависимость, которая автоматически конвертирует JSON в POJO. Для этого добавьте ещё одну зависимость в файл build.gradle:

implementation 'com.squareup.retrofit2:converter-gson:2.3.0'

Если в вашем приложении ещё не разрешена работа с сетью, то обязательно добавьте соответствующую строку в файл AndroidManifest:

<uses-permission android:name="android.permission.INTERNET"/>

После того, как зависимости добавлены, нам необходимо написать код для настройки библиотеки Retrofit.

Создайте класс с именем NetworkService:

public class NetworkService {
    
}

Этот класс должен быть singleton-объектом, поэтому объявите статическую переменную и функцию, которая создаёт и возвращает переменную того же типа, что и класс. Если вы не знаете, как работает данный паттерн, то ознакомьтесь с этой статьёй, в которой есть примеры реализации на языке Java.

public class NetworkService {
    private static NetworkService mInstance;
 
    public static NetworkService getInstance() {
        if (mInstance == null) {
            mInstance = new NetworkService();
        }
        return mInstance;
    }
}

Для тестирования мы используем JSONPlaceholder, который предоставляет фейковый онлайн REST API для разработчиков:

https://jsonplaceholder.typicode.com

Теперь мы объявим и инициализируем Retrofit в конструкторе NetworkService:

public class NetworkService {
    private static NetworkService mInstance;
    private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
    private Retrofit mRetrofit;
 
    private NetworkService() {
        mRetrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
 
    public static NetworkService getInstance() {
        if (mInstance == null) {
            mInstance = new NetworkService();
        }
        return mInstance;
    }
}

Настройка завершена, теперь нам нужно определить конечные точки, которые будут возвращать данные.

Добавление конечных точек

Создайте интерфейс с именем JSONPlaceHolderApi:

public interface JSONPlaceHolderApi {
 
}

На сайте JSONPlaceHolder URL /posts/id — это конечная точка, которая возвращает сообщение с соответствующим идентификатором. Данная конечная точка принимает GET-запрос и возвращает данные в формате JSON в следующем виде:

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

Сначала мы создадим соответствующий POJO для JSON-ответа:

public class Post {
    @SerializedName("userId")
    @Expose
    private int userId;
    @SerializedName("id")
    @Expose
    private int id;
    @SerializedName("title")
    @Expose
    private String title;
    @SerializedName("body")
    @Expose
    private String body;
 
    public int getUserId() {
        return userId;
    }
 
    public void setUserId(int userId) {
        this.userId = userId;
    }
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public String getTitle() {
        return title;
    }
 
    public void setTitle(String title) {
        this.title = title;
    }
 
    public String getBody() {
        return body;
    }
 
    public void setBody(String body) {
        this.body = body;
    }
}

Как вы видите, это простой POJO-класс. Переменные мы аннотировали с помощью @SerializedName(), передав туда имя. Эти имена фактически являются ключами в возвращаемых из API JSON-данных, поэтому вы можете как угодно изменять имя переменной, но убедитесь, что имя, переданное в аннотацию @SerializedName(), точно присутствует в JSON.

В интерфейсе, созданном выше, определите конечные точки с требуемыми параметрами:

public interface JSONPlaceHolderApi {
    @GET("/posts/{id}")
    public Call<Post> getPostWithID(@Path("id") int id);
}

Поскольку мы отправляем GET-запрос, нам нужно применить к методу аннотацию @GET, внутри которой находится конечная точка, на которую мы хотим отправить запрос. Как вы видите, мы не добавляем полный URL, т.к. Retrofit автоматически возьмёт BASE_URL, переданный в класс NetworkService, и добавит его к остальной части URL-адреса.

Возвращаемый тип метода называется Call<Post>. Call — это класс, предоставляемый непосредственно самой библиотекой. И все методы в интерфейсе должны возвращать значения этого типа. Это generic-класс, принимающий в себя тип объекта, который мы хотим конвертировать в JSON. Мы передали Post, т.к. это именно тот объект, в который хотим преобразовать JSON-ответ. В параметры мы передали целое число и аннотировали его с помощью @Path, внутри которого записали id. Retrofit возьмёт это значение и в конечной точке заменит на него {id}. Таким образом, если мы передадим значение 1 в качестве параметра, то конечная точка будет выглядеть так — /posts/1, если передадим значение 10, то конечная точка получится — /posts/10.

Теперь нам нужно, чтобы Retrofit предоставил реализацию интерфейса JSONPlaceHolderApi. Для этого используем метод create():

public class NetworkService {
    private static NetworkService mInstance;
    private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
    private Retrofit mRetrofit;
 
    private NetworkService() {
        mRetrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
 
    public static NetworkService getInstance() {
        if (mInstance == null) {
            mInstance = new NetworkService();
        }
        return mInstance;
    }
    
    public JSONPlaceHolderApi getJSONApi() {
        return mRetrofit.create(JSONPlaceHolderApi.class);
    }
}

Далее нужно получить JSONPlaceHolderApi из NetworkService и отправить запрос:

NetworkService.getInstance()
                .getJSONApi()
                .getPostWithID(1)
                .enqueue(new Callback<Post>() {
                    @Override
                    public void onResponse(@NonNull Call<Post> call, @NonNull Response<Post> response) {
                        Post post = response.body();
 
                        textView.append(post.getId() + "\n");
                        textView.append(post.getUserId() + "\n");
                        textView.append(post.getTitle() + "\n");
                        textView.append(post.getBody() + "\n");
                    }
 
                    @Override
                    public void onFailure(@NonNull Call<Post> call, @NonNull Throwable t) {
 
                        textView.append("Error occurred while getting request!");
                        t.printStackTrace();
                    }
                });

Возвращённый объект Call содержит метод с именем enqueue, который принимает в качестве параметра Callback<T>. В onResponse мы получаем результат Response<Post>, содержащий возвращённый с сервера объект Post. Чтобы получаем сам объект Post, используем метод response.body(). Остальная часть кода понятна без дополнительных пояснений.

Отправка различных типов запросов

В API JSONPlaceHolder есть много разных конечных точек, которые можно использовать.

Получение списка сообщений

@GET("/posts")
public Call<List<Post>> getAllPosts();

Для получения списка всех сообщений мы изменили конечную точку и возвращаемый тип функции.

Отправка запроса с параметром

Если вы хотите отправить запрос с параметром, то нужно всего лишь использовать аннотацию @Query() для соответствующего параметра в методе:

@GET("/posts")
public Call<List<Post>> getPostOfUser(@Query("userId") int id);

Поэтому если мы передадим значение 6 в параметре метода, то конечная точка будет следующей — /posts?userId=6.

Отправка POST запроса

Для отправки POST-запроса нужно просто изменить аннотацию метода.

@POST("/posts")
public Call<Post> postData(@Body Post data);

Чтобы сформировать тело запроса для данного метода, мы используем аннотацию @Body для передаваемого параметра. Retrofit будет использовать Gson для конвертации @Body в JSON.

Существует ещё несколько различных типов запросов, которые вы можете использовать, но это уже тема для отдельной статьи.

Перехват запросов

Retrofit предоставляет способ перехвата запросов и их логирования в Logcat. Давайте настроим перехватчик и посмотрим на эту магию. Добавьте следующую зависимость в файл build.gradle:

implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'

Обновите класс NetworkService таким образом:

public class NetworkService {
    private static NetworkService mInstance;
    private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
    private Retrofit mRetrofit;
 
    private NetworkService() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
 
        OkHttpClient.Builder client = new OkHttpClient.Builder()
                .addInterceptor(interceptor);
 
        mRetrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(client.build())
                .build();
    }
 
    public static NetworkService getInstance() {
        if (mInstance == null) {
            mInstance = new NetworkService();
        }
        return mInstance;
    }
 
    public JSONPlaceHolderApi getJSONApi() {
        return mRetrofit.create(JSONPlaceHolderApi.class);
    }
}

Теперь, когда вы отправляете или получаете запрос, все его данные, включая URL, заголовки, тело, будут выведены в лог:

D/OkHttp: <-- 200  https://jsonplaceholder.typicode.com/posts/1 (3030ms)
    date: Tue, 24 Apr 2018 15:25:19 GMT
    content-type: application/json; charset=utf-8
    set-cookie: __cfduid=d16d4221ddfba20b5464e6829eed4e3d11524583519; expires=Wed, 24-Apr-19 15:25:19 GMT; path=/; domain=.typicode.com; HttpOnly
    x-powered-by: Express
    vary: Origin, Accept-Encoding
    access-control-allow-credentials: true
    cache-control: public, max-age=14400
    pragma: no-cache
    expires: Tue, 24 Apr 2018 19:25:19 GMT
04-24 15:25:16.204 7023-7056/com.thetehnocafe.gurleensethi.retrofitexample D/OkHttp: x-content-type-options: nosniff
    etag: W/"124-yiKdLzqO5gfBrJFrcdJ8Yq0LGnU"
    via: 1.1 vegur
    cf-cache-status: HIT
    expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
    server: cloudflare
    cf-ray: 410994f328963066-SIN
04-24 15:25:16.246 7023-7056/com.thetehnocafe.gurleensethi.retrofitexample D/OkHttp: {
04-24 15:25:16.247 7023-7056/com.thetehnocafe.gurleensethi.retrofitexample D/OkHttp:   "userId": 1,
      "id": 1,
      "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
      "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
    }
    <-- END HTTP (292-byte body)

На этом наша статья заканчивается. Код для этого проекта вы можете найти на GitHub.

Перевод статьи «Getting Started with Retrofit in Android»
Виктория Дубровская

ПОХОЖИЕ ПУБЛИКАЦИИ

    None Found

48745
08/11/2018

1 комментариев к статье "Используем Retrofit 2 в Android-приложении"

  1. .enqueue(new Callback() {
    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) {
    Post post = response.body();

    textView.append(post.getId() + “\n”);
    textView.append(post.getUserId() + “\n”);
    textView.append(post.getTitle() + “\n”);
    textView.append(post.getBody() + “\n”);
    }

    @Override
    public void onFailure(@NonNull Call call, @NonNull Throwable t) {

    textView.append(“Error occurred while getting request!”);
    t.printStackTrace();
    }
    });
    Допустим у нас несколько методов описных в public interface JSONPlaceHolderApi
    и несколько мест в коде где делается запросы , соответственно для каждого запроса свой метод в интерфейсе и свои POJO
    Вопрос :
    Можно ли в методе .enqueue не писать реализацию анонимного параметризированного класса Callback?
    Есть ли смысл писать отдельные классы (типа class CallbackPost implements Callback ) ?
    и просто в нужных местах кода вызывать .enqueue передавая в него экземпляр этого класса

Добавить комментарий

Сайт использует cookie-файлы для того, чтобы вам было удобнее им пользоваться. Для продолжения работы с сайтом, вам необходимо принять использование cookie-файлов.

Я ознакомлен(а)