Современные подходы к разработке мобильных приложений для платформы Android | Статья в журнале «Молодой ученый»

Отправьте статью сегодня! Журнал выйдет 30 ноября, печатный экземпляр отправим 4 декабря.

Опубликовать статью в журнале

Авторы: , ,

Рубрика: Информационные технологии

Опубликовано в Молодой учёный №25 (159) июнь 2017 г.

Дата публикации: 22.06.2017

Статья просмотрена: 518 раз

Библиографическое описание:

Сорока, В. Г. Современные подходы к разработке мобильных приложений для платформы Android / В. Г. Сорока, В. И. Сабурова, Д. С. Фатеев. — Текст : непосредственный // Молодой ученый. — 2017. — № 25 (159). — С. 47-49. — URL: https://moluch.ru/archive/159/44856/ (дата обращения: 19.11.2024).



Создание приложения, использующего REST API в наше время стало обыденностью. Однако библиотеки Android не могут похвастаться удобным инструментарием для выполнения такой простой задачи. Перед разработчиком стоит несколько задач:

‒ Совершать вызовы к API из потока отличного от основного (main thread)

‒ Обрабатывать полученные данные и помещать их в локальное хранилище

‒ Код должен быть ёмким, кратким, понятным и многофункциональным

Стандартные процедуры увеличивают трудоёмкость написания кода, делают его менее читабельным и устойчивым к возникновению ошибок. Для общения с сервером придется написать AsyncTask либо запустить Tread/Runnable на выполнение асинхронной обработки. Необходимо изучить потоки данных в Java для того, чтоб обработать результат запроса с помощью http-клиента. В данном случае никак не обойтись без большого количества try/catch блоков для обработки всевозможных ошибок. Для последующий записи и хранения полученных данных потребуется создать локальную БД SQLite, для взаимодействия с которой необходимы знания языка SQL. Запись и чтение желательно проводить в отдельных от UI потоках, что приводит к созданию еще большего количества AsyncTask. Для каждого запроса нужно писать свой код, подготавливать данные для записи в БД, а затем для их получения.

Однако существует иной подход к решению данной задачи. В этом поможет использование open source библиотек.

RxJava и RxAndroid позволят совершать асинхронную обработку, используя принципы функционального реактивного программирования

Retrofit2 позволяет легко обращаться с HTTP запросами и без проблем обрабатывать ответы

Realm — объектная БД, принцип работы которой основан на работе с моделями данных

Для того, чтоб показать минимальные возможности данных библиотек, будет использоваться Github API. Результатом будет получение, обработка, запись в локальную БД и отображение данных, предоставленных Github API. Этим и обработкой данных займется Retrofit. Асинхронность будет обеспечена через RxJava. Запрос вернет модель, которая будет помещена в локальную БД При помощи Realm. Слушатель изменений в БД в реальном времени отображает результат на экран.

Установка

Ниже указаны основные зависимости:

dependencies {

compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

compile 'io.reactivex.rxjava2:rxjava:2.1.0'

compile 'com.squareup.retrofit2:retrofit:2.2.0'

compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'

compile 'com.squareup.retrofit2:converter-gson:2.2.0'

kapt «io.realm:realm-annotations-processor:3.1.3"}

В build.gradle на уровне приложения необходимо добавить:

apply plugin: 'realm-android'

На уровне проекта:

dependencies {

classpath "io.realm:realm-gradle-plugin:3.1.3"}

А также добавить App Permissions в AndroidManifest.xml:

RetrofitService

Retrofit использует Java интерфейс в качестве прокси для вызовов REST API. Аннотация @GET указывает на то, что будет выполнен get-запрос. В качестве параметров необходимо указать url/путь. Данный код совершает get-запрос и возвращает объект типа User, обернутый в Flowable

interface GithubService {

@GET("/users/{user}") fun getUsers(@Path("user") user: String): Flowable}

Github REST API возвращает JSON данного вида:

{"login": "linkedin",

"blog": "http://engineering.linkedin.com",

"public_repos": 73,

//...truncated JSON}

Далее необходимо определить модель. Поля переменных в модели автоматически парсятся из JSON ответа. Необходимость ручного парсинга отпадает. Чтоб все сработало автоматически необходимо использовать аннотацию @ SerializedName, где параметром будет имя ключа из входного JSON.

open class User : RealmObject() {

var login: String? = null

var id: Int = 0

@SerializedName("avatar_url") var avatarUrl: String? = null}

В классе, унаследованном от android.app.Application проинициализируем Retrofit service как указано в коде ниже.

val restAdapter = Retrofit.Builder()

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

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

.addConverterFactory(GsonConverterFactory.create())

.build()

service = restAdapter.create(GithubService::class.java)

Метод baseUrl() принимает на вход базовую часть адреса. addCallAdapterFactory() и addConverterFactory() принимают на вход адаптеры для работы с оберткой и самими данными соответственно, преобразовывая входящий JSON в объект.

Чтоб получить сервис из Activity используем

companion object {

var service: GithubService? = null}

Дальше в ход вступает RxJava, который запустит цепочку получения и записи данных.

RxJava. Создание асинхронного потока

Объект Flowable, полученный из get-запроса дает возможность работать с потоком данных когда тот станет доступным. Необходимо указать поток который будет следить за изменениями потока данных. Для этого нужно подписаться на новый поток при помощи метода subscribeOn() и передать в него параметр Schedulers.newThread(). Далее указать поток, который будет следить за результатом (обозревать). Это делает метод observeOn() с параметром AndroidSchedulers.mainThread(). Код ниже демонстрирует работу с объектом Flowable

service.getUsers(it)

.subscribeOn(Schedulers.newThread())

.observeOn(AndroidSchedulers.mainThread())

.subscribe({ r -> // действие с данными} },

{ e -> // обработка ошибок})

}.forEach { compositeDisposable.add(it) }

getService().getUsers() возвращает объект типа Flowable. Метод subscribe перегружен двумя функциями обратного вызова такими как onNext() и onError(). onNext() вызывается при завершении работы с потоком данных и предоставляет данные для работы с ними. onError() вызывается при возникновении ошибки в процессе получения данных для последующей обработки исключения. Каждый Flowable должен быть помещен в CompositeDisposable для возможности отписаться ото всех событий сразу. Это может быть сделано, например, в момент разрушения Activity

Код ниже демонстрирует работу CompositeDisposable

override fun onDestroy() {

super.onDestroy()

compositeDisposable.clear()}

Данные получены. Все Flowable гарантированно будут отписаны. Остается лишь записать полученные данные в БД.

Realm. Асинхронная запись данных

Для начала необходимо проинициализировать Realm в классе, наследуемом от android.app.Application.

Ниже представлена инициализация Realm

Realm.init(this)

val realmConfiguration = RealmConfiguration.Builder()

.deleteRealmIfMigrationNeeded()

.build()

Realm.setDefaultConfiguration(realmConfiguration)

Realm.init(this) принимает на вход контекст приложения. Установка параметра deleteRealmIfMigrationNeeded() изменит поведение того, как будет обработана ошибка миграции.

Далее в основном Activity необходимо получить экземпляр Realm. Для упрощения работы можно повесить слушатель на изменение модели в БД. Теперь отображение данных напрямую зависит от состояния модели. Слушатель будет оповещать адаптер

val realm: Realm = Realm.getDefaultInstance()

realm.addChangeListener { mCardAdapter.notifyDataSetChanged() }

Заполним метод subscribe. Запишем данные в БД и обработаем ошибки

.subscribe({ r -> realm.executeTransactionAsync { data -> data.insert(r) } },

{ e -> e.printStackTrace()})

Сделаем обработчик нажатия на кнопку очистки

findViewById(R.id.button_clear).setOnClickListener {

realm.executeTransactionAsync { data -> data.delete(User::class.java) }}

executeTransactionAsync может принимать на вход три анонимных класса таких как Realm.Transaction() с функцией обратного вызова execute(), Realm.Transaction.OnSuccess() c функцией onSuccess() и Realm.Transaction.OnError() c onError() для работы с управляемой моделью, для выполнения действий по окончании транзакции и обработке ошибок в ходе транзакции соответственно.

По уничтожении Activity в меоде onDestroy() также как и с CompositeDisposable нужно вызвать realm.close()

Заключение

Данные получены и записаны в локальное хранилище. Возможные ошибки обработаны. Отображение реагирует на любое изменение данных. Графический интерфейс не будет заблокирован, так как все манипуляции с данными происходят в отдельных потоках. Сложная на вид задача реализована минимальным количеством кода. Полный код программы можно найти в репозитории по адресу https://github.com/slowfarm/RXKotlin

Литература:

  1. https://realm.io
  2. https://habrahabr.ru/post/269417/. — «Введение в RxJava: Почему Rx?»
  3. http://reactivex.io
  4. http://square.github.io/retrofit/
  5. https://habrahabr.ru/post/314028/. — «Изучаем Retrofit 2»
  6. https://kotlinlang.org/docs/tutorials/kotlin-android.html
Основные термины (генерируются автоматически): API, JSON, REST, GET, данные, локальная БД, поток данных, асинхронная обработка, локальное хранилище, обратный вызов.


Задать вопрос