image


Одним из самых захватывающих объявлений на Google I/O в этом году стала официальная поддержка Kotlin для разработки под Android.


Котлин на самом деле не новый язык, ему уже >5 лет и он довольно зрелый. Здесь вы можете получить более подробную информацию о языке


Я планирую поделиться некоторыми «практиками» использования Kotlin в разработке Android.


Retrofit — очень популярная библиотека для работы с сетью, и она широко используется в сообществе разработчиков. Даже Google использует его в своих образцах кода.


В этой статье я расскажу о том, как использовать REST API в ваших приложениях с помощью Retrofit + Kotlin + RxJava. Мы будем использовать API Github для получения списка разработчиков Java в Омске, Россия. Также, я затрону некоторые особенности языка программирования и расскажу о том, как мы можем применять их к тому, что мы, разработчики Android, делаем ежедневно — вызывать API.


0. Установка


В Android Studio 3.0 (Preview) теперь есть встроенная поддержка Kotlin, без ручной установки плагинов. Отсюда можно установить preview-версию.


1. Добавление зависимостей


Чтобы использовать Retrofit, вам нужно добавить зависимости в файл build.gradle:


dependencies {
    // retrofit
    compile "com.squareup.retrofit2:retrofit:2.3.0"
    compile "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
    compile "com.squareup.retrofit2:converter-gson:2.3.0"

    // rxandroid
    compile "io.reactivex.rxjava2:rxandroid:2.0.1"
}

RxJava2 поможет сделать наши вызовы реактивными.
GSON-конвертер будет обрабатывать десериализацию и сериализацию тел запроса и ответа.
RxAndroid поможет нам с привязкой RxJava2 к Android.


2. Создание классов данных


Как правило, следующим шагом является создание классов данных, которые являются POJO (Plain Old Java Objects) и которые будут представлять ответы на вызовы API.


С API Github пользователи будут представлены как объекты.


Как это будет выглядеть на Kotlin:


data class User(
        val login: String,
        val id: Long,
        val url: String,
        val html_url: String,
        val followers_url: String,
        val following_url: String,
        val starred_url: String,
        val gists_url: String,
        val type: String,
        val score: Int
)

Классы данных в Котлине


Классы данных в Kotlin — это классы, специально разработанные для хранения данных.


Компилятор Kotlin автоматически помогает нам реализовать методы equals (), hashCode () и toString () в классе, что делает код еще короче, потому что нам не нужно это делать самостоятельно.


Мы можем переопределить реализацию по умолчанию любого из этих методов путем определения метода.


Отличной особенностью является то, что мы можем создавать результаты поиска в одном файле Kotlin — скажем, SearchResponse.kt. Наш окончательный класс ответа на поиск будет содержать все связанные классы данных и выглядеть следующим образом:


SearchResponse.kt


data class User(
        val login: String,
        val id: Long,
        val url: String,
        val html_url: String,
        val followers_url: String,
        val following_url: String,
        val starred_url: String,
        val gists_url: String,
        val type: String,
        val score: Int
)

data class Result (val total_count: Int, val incomplete_results: Boolean, val items: List<User>)

3. Создание API-интерфейса


Следующим шагом, который мы обычно делаем в Java, является создание интерфейса API, который мы будем использовать для создания запросов и получения ответов.


Как правило, в Java я хотел бы создать удобный «фабричный» класс, который создает службу API, когда это необходимо, и я бы сделал что-то вроде этого:


GithubApiService.java


public interface GithubApiService {

    @GET("search/users")
    Observable<Result> search(@Query("q") String query, @Query("page") int page, @Query("per_page") int perPage);

    /**
     * Factory class for convenient creation of the Api Service interface
     */
    class Factory {

        public static GithubApiService create() {
            Retrofit retrofit = new Retrofit.Builder()
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl("https://api.github.com/")
                    .build();

            return retrofit.create(GithubApiService.class);
        }
    }
}

Чтобы использовать этот интерфейс, мы делаем следующие вызовы:


GithubApiService apiService = GithubApiService.Factory.create();
apiService.search(/** search parameters go in here **/);

Чтобы повторить тоже самое в Котлине, у нас будет эквивалентный интерфейс GithubApiService.kt, который будет выглядеть так:


GithubApiService.kt


interface GithubApiService {

    @GET("search/users")
    fun search(@Query("q") query: String,
               @Query("page") page: Int,
               @Query("per_page") perPage: Int): Observable<Result>

    /**
     * Companion object to create the GithubApiService
     */
    companion object Factory {
        fun create(): GithubApiService {
            val retrofit = Retrofit.Builder()
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl("https://api.github.com/")
                    .build()

            return retrofit.create(GithubApiService::class.java);
        }
    }
}

Использование этого интерфейса и фабричного класса будет выглядеть так:


val apiService = GithubApiService.create()
apiService.search(/* search params go in here */)

Обратите внимание, что нам не нужно было использовать «имя» сопутствующего объекта для ссылки на метод, мы использовали только имя класса.


Синглтоны и сопутствующие объекты в Котлине


Чтобы понять, какие сопутствующие объекты в Котлине есть, мы должны сначала понять, какие объявления объектов находятся в Котлине. Объявление объекта в Котлине — это способ, которым одиночный элемент создается в Котлине.


Синглтоны в Котлине так же просты, как объявление объекта и присвоение ему имени. Например:


object SearchRepositoryProvider {
    fun provideSearchRepository(): SearchRepository {
        return SearchRepository()
    }
}

Использование указанного выше объявления объекта:


val repository = SearchRepositoryProvider.provideSearchRepository();

Благодаря этому мы смогли создать поставщика, который предоставит нам экземпляр репозитория (который поможет нам подключиться к API Github через GithubApiService).


Объявление объекта инициализируется при первом доступе — так же, как работает Singleton.


Однако, объекты-компаньоны — это тип объявления объекта, который соответствует ключевому слову companion. Объекты Companion можно сравнить с статическими методами или полями на Java. Фактически, если вы ссылаетесь на объект-компаньон с Java, он будет выглядеть как статический метод или поле.


Сопутствующий объект — это то, что используется в версии GithubApiService.kt Kotlin выше.


4. Создание репозитория


Поскольку мы стараемся как можно больше абстрагировать наши процессы, мы можем создать простой репозиторий, который обрабатывает вызов GithubApiService и строит строку запроса.


Строка запроса, соответствующая нашей спецификации для этого демонстрационного приложения (для поиска разработчиков Java в Омске) с использованием API Github, — это местоположение: Omsk + language: Java, поэтому мы создадим метод в репозитории, который позволит нам построить эту строку, передавая местоположение и язык в качестве параметров.


Наш поисковый репозиторий будет выглядеть так:


class SearchRepository(val apiService: GithubApiService) {
    fun searchUsers(location: String, language: String): Observable<Result> {
        return apiService.search(query = "location:$location+language:$language")
    }
}

Строковые шаблоны в Котлине


В блоке вышеприведенного кода мы использовали функцию Kotlin, называемую «String templates», чтобы построить нашу строку запроса. Строковые шаблоны начинаются со знака доллара — $ и значение переменной, следующей за ней, конкатенируется с остальной частью строки. Это аналогичная функция для строковой интерполяции в groovy.


5. Делаем запрос и получаем ответ API при помощи RxJava


Теперь, когда мы настроили наши классы ответов, наш интерфейс репозитория, теперь мы можем сделать запрос и получить ответ API с помощью RxJava. Этот шаг похож на то, как мы будем делать это на Java. В коде Kotlin это выглядит так:


val repository = SearchRepositoryProvider.provideSearchRepository()
repository.searchUsers("Omsk", "Java")
        .observeOn(AndroidSchedulers.mainThread())
        .subscribeOn(Schedulers.io())
        .subscribe ({
            result ->
            Log.d("Result", "There are ${result.items.size} Java developers in Lagos")
        }, { error ->
            error.printStackTrace()
        })

Мы сделали наш запрос и получили ответ. Теперь можем делать с ним все, что захотим.


Полезные ссылки:



Заключение


Таким образом, в этом посте мы рассмотрели некоторые интересные возможности/свойства языка Kotlin и способы их применения при использовании Retrofit + RxJava для сетевых вызовов.


Были затронуты:


  • Классы данных
  • Объявления объектов
  • Сопутствующие объекты
  • Строковые шаблоны
  • Взаимодействие с Java

Комментарии (2)


  1. jehy
    21.08.2017 19:07

    Так и не нашёл плюсов по сравнению с Apache httpComponents.


    1. vba
      23.08.2017 11:38

      Использование RxJava из коробки и без мучений?