SlideShare ist ein Scribd-Unternehmen logo
1 von 60
Практическое использование RxJava 2 в
Android
Виноградный Евгений
Android Developer
Как последовательно выполнить 2 цепи?
Условие:
Например, у нас есть Single для получения списка комментариев и Single для публикации
комментария. Необходимо обеспечить отправку комментария после загрузки списка.
Как последовательно выполнить 2 цепи?
Условие:
Например, у нас есть Single для получения списка комментариев и Single для публикации
комментария. Необходимо обеспечить отправку комментария после загрузки списка.
Single.fromCallable { dataRepository.loadComments() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(…)
Как последовательно выполнить 2 цепи?
Условие:
Например, у нас есть Single для получения списка комментариев и Single для публикации
комментария. Необходимо обеспечить отправку комментария после загрузки списка.
Single.fromCallable { dataRepository.addComment(…) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(…)
Как последовательно выполнить 2 цепи?
Решение:
Использовать Semaphore из java.util.concurrent.
val semaphore = Semaphore(1)
Single.fromCallable { dataRepository.loadComments() }
.doOnSubscribe { semaphore.acquire() }
.subscribeOn(Schedulers.io())
.doFinally { semaphore.release() }
.observeOn(AndroidSchedulers.mainThread())
.subscribe(…)
Как последовательно выполнить 2 цепи?
Решение:
Использовать Semaphore из java.util.concurrent.
val semaphore = Semaphore(1)
Single.fromCallable { dataRepository.addComment(…) }
.doOnSubscribe { semaphore.acquire() }
.subscribeOn(Schedulers.io())
.doFinally { semaphore.release() }
.observeOn(AndroidSchedulers.mainThread())
.subscribe(…)
Как последовательно выполнить 2 цепи?
Какие возможности предоставляет нам такой подход?
• Потокобезопасность для цепи.
Как последовательно выполнить 2 цепи?
Какие возможности предоставляет нам такой подход?
• Потокобезопасность для цепи.
• Потокобезопасность для отдельного участка цепи.
Как последовательно выполнить 2 цепи?
Какие возможности предоставляет нам такой подход?
• Потокобезопасность для цепи.
• Потокобезопасность для отдельного участка цепи.
• Возможность запускать одновременно определенное количество цепей.
Как последовательно выполнить 2 цепи?
Какие возможности предоставляет нам такой подход?
• Потокобезопасность для цепи.
• Потокобезопасность для отдельного участка цепи.
• Возможность запускать одновременно определенное количество цепей.
• Нет необходимости объединять все цепи в одну.
Как последовательно выполнить 2 цепи?
Обрабатываем ошибки красиво
Условие:
Необходимо выполнить запрос на сервер и:
• При отсутствии Интернет-соединения сразу выдать ошибку.
Обрабатываем ошибки красиво
Условие:
Необходимо выполнить запрос на сервер и:
• При отсутствии Интернет-соединения сразу выдать ошибку.
• При возникновении ошибки повторить запрос с задержкой в 5 секунд, не больше 3 раз.
Обрабатываем ошибки красиво
Условие:
Необходимо выполнить запрос на сервер и:
• При отсутствии Интернет-соединения сразу выдать ошибку.
• При возникновении ошибки повторить запрос с задержкой в 5 секунд, не больше 3 раз.
• Наложить ограничение в 1 минуту на выполнение всей операции.
Обрабатываем ошибки красиво
Условие:
Необходимо выполнить запрос на сервер и:
• При отсутствии Интернет-соединения сразу выдать ошибку.
• При возникновении ошибки повторить запрос с задержкой в 5 секунд, не больше 3 раз.
• Наложить ограничение в 1 минуту на выполнение всей операции.
dataRepository.requestDataFromNetwork()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(…)
Обрабатываем ошибки красиво
Решение:
dataRepository.requestDataFromNetwork()
…
.retryWhen { throwableObservable -> throwableObservable
.flatMap {
if (it is IOException) {
Observable.error(it)
} else {
Observable.just(it)
}
}
}
…
Обрабатываем ошибки красиво
Решение:
dataRepository.requestDataFromNetwork()
…
.retryWhen { throwableObservable -> throwableObservable
.flatMap {
if (it is IOException || attemptsCount.getAndIncrement() == 3) {
Observable.error(it)
} else {
Observable.just(it)
}
}
}
…
Обрабатываем ошибки красиво
Решение:
dataRepository.requestDataFromNetwork()
…
.retryWhen { throwableObservable -> throwableObservable
.flatMap {
if (it is IOException || attemptsCount.getAndIncrement() == 3) {
Observable.error(it)
} else {
Observable.just(it)
}
}
.delay(5, TimeUnit.SECONDS)
}
…
Обрабатываем ошибки красиво
Решение:
dataRepository.requestDataFromNetwork()
…
.retryWhen { throwableObservable -> throwableObservable
.flatMap {
if (it is IOException || attemptsCount.getAndIncrement() == 3) {
Observable.error(it)
} else {
Observable.just(it)
}
}
.delay(5, TimeUnit.SECONDS)
}
.timeout(1, TimeUnit.MINUTES)
…
Обрабатываем ошибки красиво
class ErrorTransformer<T> constructor(val maxAttempts…) : ObservableTransformer<T, T> {
…
override fun apply(upstream: Observable<T>) = upstream
.retryWhen { throwableObservable -> throwableObservable
.flatMap {
if (it is IOException || attemptsCount.getAndIncrement() == maxAttemptsCount) {
Observable.error(it)
} else {
Observable.just(it)
}
}
.delay(delay, TimeUnit.MILLISECONDS)
}
.timeout(timeout, TimeUnit.MILLISECONDS)
}
Обрабатываем ошибки красиво
class ErrorTransformer<T> constructor(val maxAttempts…) : ObservableTransformer<T, T> {
…
override fun apply(upstream: Observable<T>) = upstream
.retryWhen { throwableObservable -> throwableObservable
.flatMap {
Crashlytics.logException(it)
if (it is IOException || attemptsCount.getAndIncrement() == maxAttemptsCount) {
Observable.error(it)
} else {
Observable.just(it)
}
}
.delay(delay, TimeUnit.MILLISECONDS)
}
.timeout(timeout, TimeUnit.MILLISECONDS)
}
Обрабатываем ошибки красиво
Использование:
val DELAY = TimeUnit.SECONDS.toMillis(5)
val TIMEOUT = TimeUnit.MINUTES.toMillis(1)
val ATTEMPTS_COUNT = 3
dataRepository.requestDataFromNetwork()
.subscribeOn(Schedulers.io())
.compose(ErrorTransformer(TIMEOUT, DELAY, ATTEMPTS_COUNT))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(…)
Обрабатываем ошибки красиво
Использование:
val DELAY = TimeUnit.SECONDS.toMillis(5)
val TIMEOUT = TimeUnit.MINUTES.toMillis(1)
val ATTEMPTS_COUNT = 3
dataRepository.requestDataFromNetwork()
.subscribeOn(Schedulers.io())
.compose(ErrorTransformer(TIMEOUT, DELAY, ATTEMPTS_COUNT))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(…)
…
RxJavaPlugins.setErrorHandler { e -> … }
Обрабатываем ошибки красиво
Пример использования Subject
Условие:
Необходимо отслеживать подключение и отключение Интернет-соединения для выполнения
запросов на сервер.
Пример использования Subject
PublishSubject
val source = PublishSubject.create<Int>()
// Получит 1, 2, 3, 4 и onComplete
source.subscribe(getFirstObserver())
source.onNext(1)
source.onNext(2)
source.onNext(3)
// Получит 4 и onComplete для следующего наблюдателя тоже
source.subscribe(getSecondObserver());
source.onNext(4)
source.onComplete()
Пример использования Subject
RelplaySubject
val source = ReplaySubject.create<Int>()
// Он получит 1, 2, 3, 4
source.subscribe(getFirstObserver())
source.onNext(1)
source.onNext(2)
source.onNext(3)
source.onNext(4)
source.onComplete()
// Он также получит 1, 2, 3, 4 так как он использует Replay Subject
source.subscribe(getSecondObserver())
Пример использования Subject
BehaviorSubject
val source = BehaviorSubject.create<Int>()
// Получит 1, 2, 3, 4 and onComplete
source.subscribe(getFirstObserver())
source.onNext(1)
source.onNext(2)
source.onNext(3)
// Получит 3(последний элемент) и 4(последующие элементы) и onComplete
source.subscribe(getSecondObserver())
source.onNext(4)
source.onComplete()
Пример использования Subject
AsyncSubject
val source = AsyncSubject.create<Int>()
// Получит только 4 и onComplete
source.subscribe(getFirstObserver())
source.onNext(1)
source.onNext(2)
source.onNext(3)
// Тоже получит только 4 и onComplete
source.subscribe(getSecondObserver())
source.onNext(4)
source.onComplete()
Пример использования Subject
Решение:
class ConnectivityReceiver(val connectivitySubject: PublishSubject<Boolean>… {
override fun onReceive(context: Context, arg1: Intent) {
connectivitySubject.onNext(isConnected())
}
private fun isConnected(): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
val activeNetwork = connectivityManager.activeNetworkInfo
return activeNetwork != null && activeNetwork.isConnected
}
}
Пример использования Subject
Решение:
…
override fun onStart() {
registerReceiver(connectivityReceiver,
IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
super.onStart()
}
override fun onStop() {
unregisterReceiver(connectivityReceiver)
super.onStop()
}
…
Пример использования Subject
Другие примеры использования:
• Автоматическое отслеживание просмотренных элементов списка.
• Обработка нажатий (кликов) на элементы списка.
• Отображение ошибок.
• Обновление элементов.
… и многое другое.
Пример использования Subject
Анимирование View
Условие:
Необходимо реализовать последовательное появление нескольких View.
Анимирование View
Условие:
Необходимо реализовать последовательное появление нескольких View.
Анимирование View
Решение:
Будем использовать ViewPropertyAnimator.
ViewCompat.animate(view)
.setDuration(duration)
.alpha(1f)
Анимирование View
Решение:
fun fadeIn(view: View, duration: Long) = Completable.create {
ViewCompat.animate(view)
.setDuration(duration)
.alpha(1f)
.withEndAction {
it.onComplete()
}
}
Анимирование View
Использование:
val durationMs = 1000L
fadeIn(button1, durationMs)
.andThen(fadeIn(button2, durationMs))
.andThen(fadeIn(button3, durationMs))
.andThen(fadeIn(button4, durationMs))
.subscribe()
Анимирование View
Условие:
Необходимо реализовать одновременное появление нескольких View, но продолжительность
анимации одной из них должна быть на 1 секунду больше.
Анимирование View
Условие:
Необходимо реализовать одновременное появление нескольких View, но продолжительность
анимации одной из них должна быть на 1 секунду больше.
Анимирование View
Более красивое решение:
fun View.fadeIn(duration: Long = 1000L) = Completable.create {
ViewCompat.animate(this)
.setDuration(duration)
.alpha(1f)
.withEndAction {
it.onComplete()
}
}
Анимирование View
Использование:
button1.fadeIn()
.mergeWith(button2.fadeIn(2000))
.mergeWith(button3.fadeIn())
.mergeWith(button4.fadeIn())
.subscribe()
Анимирование View
Реактивный поиск
Условие:
Необходимо реализовать поиск и при изменении строки запрашивать данные с сервера.
Должны соблюдаться условия:
• Данные с сервера должны запрашиваться не чаще, чем 1 раз в 500 миллисекунд.
Реактивный поиск
Условие:
Необходимо реализовать поиск и при изменении строки запрашивать данные с сервера.
Должны соблюдаться условия:
• Данные с сервера должны запрашиваться не чаще, чем 1 раз в 500 миллисекунд.
• Пустые строки должны игнорироваться.
Реактивный поиск
Условие:
Необходимо реализовать поиск и при изменении строки запрашивать данные с сервера.
Должны соблюдаться условия:
• Данные с сервера должны запрашиваться не чаще, чем 1 раз в 500 миллисекунд.
• Пустые строки должны игнорироваться.
• Если строка была отредактирована, но ее содержимое осталось прежним, то запрос
отправляться не должен.
Реактивный поиск
Условие:
Необходимо реализовать поиск и при изменении строки запрашивать данные с сервера.
Должны соблюдаться условия:
• Данные с сервера должны запрашиваться не чаще, чем 1 раз в 500 миллисекунд.
• Пустые строки должны игнорироваться.
• Если строка была отредактирована, но ее содержимое осталось прежним, то запрос
отправляться не должен.
• Должен отменяться последний запрос, если строка изменилась.
Реактивный поиск
Решение:
editText.textChanges()
.subscribe(…)
Реактивный поиск
Решение:
editText.textChanges()
.delay(500, TimeUnit.MILLISECONDS)
.subscribe(…)
Реактивный поиск
Решение:
editText.textChanges()
.delay(500, TimeUnit.MILLISECONDS)
.filter { !it.isEmpty() }
.subscribe(…)
Реактивный поиск
Решение:
editText.textChanges()
.delay(500, TimeUnit.MILLISECONDS)
.filter { !it.isEmpty() }
.map { it.toString() }
.distinctUntilChanged()
.subscribe(…)
Реактивный поиск
Решение:
editText.textChanges()
.delay(500, TimeUnit.MILLISECONDS)
.filter { !it.isEmpty() }
.map { it.toString() }
.distinctUntilChanged()
.switchMap { dataRepository.getResultsByQuery(it) }
.subscribe(…)
Реактивный поиск
Решение:
editText.textChanges()
.debounce(500, TimeUnit.MILLISECONDS) // Более корректное использование
.filter { !it.isEmpty() }
.map { it.toString() }
.distinctUntilChanged()
.switchMap { dataRepository.getResultsByQuery(it) }
.subscribe(…)
Реактивный поиск
Кэширование данных
Условие:
Необходимо организовать кэширование данных в памяти, на диске и при отсутствии данных
запросить их с сервера.
Кэширование данных
Условие:
Необходимо организовать кэширование данных в памяти, на диске и при отсутствии данных
запросить их с сервера.
Кэширование данных
Решение:
…
fun getDataFromMemory() = memoryDataSource.getData()
fun getDataFromDisk() = diskDataSource.getData().doOnNext { data ->
memoryDataSource.cacheInMemory(data)
}
fun getDataFromNetwork() = networkDataSource.getData().doOnNext { data ->
diskDataSource.saveToDisk(data)
memoryDataSource.cacheInMemory(data)
}
…
Кэширование данных
Использование:
val memory = dataSource.getDataFromMemory()
val disk = dataSource.getDataFromDisk()
val network = dataSource.getDataFromNetwork()
Observable.concat<Data>(memory, disk, network)
.subscribeOn(Schedulers.io())
.firstElement()
.toObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(…)
Кэширование данных
Спасибо за внимание!

Weitere ähnliche Inhalte

Was ist angesagt?

Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозит
Elena Kotina
 
Александр Сычев "Разделяй и властвуй: CQRS в iOS"
Александр Сычев "Разделяй и властвуй: CQRS в iOS"Александр Сычев "Разделяй и властвуй: CQRS в iOS"
Александр Сычев "Разделяй и властвуй: CQRS в iOS"
IT Event
 
Stream API: рекомендации лучших собаководов
Stream API: рекомендации лучших собаководовStream API: рекомендации лучших собаководов
Stream API: рекомендации лучших собаководов
tvaleev
 

Was ist angesagt? (20)

Spring the ripper
Spring the ripperSpring the ripper
Spring the ripper
 
Multithreading in java past and actual
Multithreading in java past and actualMultithreading in java past and actual
Multithreading in java past and actual
 
Архитектура. Доступноять программных систем.
Архитектура. Доступноять программных систем.Архитектура. Доступноять программных систем.
Архитектура. Доступноять программных систем.
 
Java осень 2014 занятие 3
Java осень 2014 занятие 3Java осень 2014 занятие 3
Java осень 2014 занятие 3
 
Java осень 2014 занятие 5
Java осень 2014 занятие 5Java осень 2014 занятие 5
Java осень 2014 занятие 5
 
Java 8. Thread pools
Java 8. Thread poolsJava 8. Thread pools
Java 8. Thread pools
 
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на Swift
 
Java 9: what is there beyond modularization
Java 9: what is there beyond modularizationJava 9: what is there beyond modularization
Java 9: what is there beyond modularization
 
Spring Boot Test horror
Spring Boot Test horrorSpring Boot Test horror
Spring Boot Test horror
 
java
javajava
java
 
МАИ, Сети ЭВМ, Лекция №4
МАИ, Сети ЭВМ, Лекция №4МАИ, Сети ЭВМ, Лекция №4
МАИ, Сети ЭВМ, Лекция №4
 
Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозит
 
Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"
Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"
Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"
 
Curse of spring boot test [VRN]
Curse of spring boot test [VRN]Curse of spring boot test [VRN]
Curse of spring boot test [VRN]
 
Curse of spring boot test
Curse of spring boot testCurse of spring boot test
Curse of spring boot test
 
Java threads - part 3
Java threads - part 3Java threads - part 3
Java threads - part 3
 
Java threads - part 2
Java threads - part 2Java threads - part 2
Java threads - part 2
 
Java threads - part 1
Java threads - part 1Java threads - part 1
Java threads - part 1
 
Александр Сычев "Разделяй и властвуй: CQRS в iOS"
Александр Сычев "Разделяй и властвуй: CQRS в iOS"Александр Сычев "Разделяй и властвуй: CQRS в iOS"
Александр Сычев "Разделяй и властвуй: CQRS в iOS"
 
Stream API: рекомендации лучших собаководов
Stream API: рекомендации лучших собаководовStream API: рекомендации лучших собаководов
Stream API: рекомендации лучших собаководов
 

Ähnlich wie Practical usage of RxJava 2

Михаил Давыдов: JavaScript. Асинхронность
Михаил Давыдов: JavaScript. АсинхронностьМихаил Давыдов: JavaScript. Асинхронность
Михаил Давыдов: JavaScript. Асинхронность
Yandex
 
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo
 
Adymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlAdymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher Sql
Oleksandr Petrov
 
Практическое применение HTML5 в Я.Почте
Практическое применение HTML5 в Я.ПочтеПрактическое применение HTML5 в Я.Почте
Практическое применение HTML5 в Я.Почте
Alexey Androsov
 
Наталья Чуфырина, Mail.Ru Group, «Как создать команду по автоматизации тестир...
Наталья Чуфырина, Mail.Ru Group, «Как создать команду по автоматизации тестир...Наталья Чуфырина, Mail.Ru Group, «Как создать команду по автоматизации тестир...
Наталья Чуфырина, Mail.Ru Group, «Как создать команду по автоматизации тестир...
Mail.ru Group
 
Java осень 2013 лекция 6
Java осень 2013 лекция 6Java осень 2013 лекция 6
Java осень 2013 лекция 6
Technopark
 
Влад Селиверстов – Веб-сервер Phantom
Влад Селиверстов – Веб-сервер Phantom  Влад Селиверстов – Веб-сервер Phantom
Влад Селиверстов – Веб-сервер Phantom
Media Gorod
 
ZFConf 2011: Воюем за ресурсы: Повышение производительности Zend Framework пр...
ZFConf 2011: Воюем за ресурсы: Повышение производительности Zend Framework пр...ZFConf 2011: Воюем за ресурсы: Повышение производительности Zend Framework пр...
ZFConf 2011: Воюем за ресурсы: Повышение производительности Zend Framework пр...
ZFConf Conference
 
Web осень 2013 лекция 9
Web осень 2013 лекция 9Web осень 2013 лекция 9
Web осень 2013 лекция 9
Technopark
 

Ähnlich wie Practical usage of RxJava 2 (20)

RDSDataSource: Promises
RDSDataSource: PromisesRDSDataSource: Promises
RDSDataSource: Promises
 
Михаил Давыдов: JavaScript. Асинхронность
Михаил Давыдов: JavaScript. АсинхронностьМихаил Давыдов: JavaScript. Асинхронность
Михаил Давыдов: JavaScript. Асинхронность
 
Полный цикл тестирования React-приложений, Алексей Андросов и Наталья Стусь
Полный цикл тестирования React-приложений, Алексей Андросов и Наталья СтусьПолный цикл тестирования React-приложений, Алексей Андросов и Наталья Стусь
Полный цикл тестирования React-приложений, Алексей Андросов и Наталья Стусь
 
React со скоростью света: не совсем обычный серверный рендеринг
React со скоростью света: не совсем обычный серверный рендерингReact со скоростью света: не совсем обычный серверный рендеринг
React со скоростью света: не совсем обычный серверный рендеринг
 
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher SqlAlexander Dymo - Barcamp 2009 - Faster Higher Sql
Alexander Dymo - Barcamp 2009 - Faster Higher Sql
 
Adymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher SqlAdymo Barcamp Presentation Faster Higher Sql
Adymo Barcamp Presentation Faster Higher Sql
 
Async
AsyncAsync
Async
 
Luxoft async.net
Luxoft async.netLuxoft async.net
Luxoft async.net
 
Практическое применение HTML5 в Я.Почте
Практическое применение HTML5 в Я.ПочтеПрактическое применение HTML5 в Я.Почте
Практическое применение HTML5 в Я.Почте
 
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptСергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
 
Наталья Чуфырина, Mail.Ru Group, «Как создать команду по автоматизации тестир...
Наталья Чуфырина, Mail.Ru Group, «Как создать команду по автоматизации тестир...Наталья Чуфырина, Mail.Ru Group, «Как создать команду по автоматизации тестир...
Наталья Чуфырина, Mail.Ru Group, «Как создать команду по автоматизации тестир...
 
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
 
Алексей Андросов "Яндекс.Почта: архитектура фронтенда как она есть"
Алексей Андросов "Яндекс.Почта: архитектура фронтенда как она есть"Алексей Андросов "Яндекс.Почта: архитектура фронтенда как она есть"
Алексей Андросов "Яндекс.Почта: архитектура фронтенда как она есть"
 
Java осень 2013 лекция 6
Java осень 2013 лекция 6Java осень 2013 лекция 6
Java осень 2013 лекция 6
 
Cоздание приложений со знанием Perl
Cоздание приложений со знанием PerlCоздание приложений со знанием Perl
Cоздание приложений со знанием Perl
 
Влад Селиверстов – Веб-сервер Phantom
Влад Селиверстов – Веб-сервер Phantom  Влад Селиверстов – Веб-сервер Phantom
Влад Селиверстов – Веб-сервер Phantom
 
Многопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметкиМногопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметки
 
Другая виртуализация
Другая виртуализацияДругая виртуализация
Другая виртуализация
 
ZFConf 2011: Воюем за ресурсы: Повышение производительности Zend Framework пр...
ZFConf 2011: Воюем за ресурсы: Повышение производительности Zend Framework пр...ZFConf 2011: Воюем за ресурсы: Повышение производительности Zend Framework пр...
ZFConf 2011: Воюем за ресурсы: Повышение производительности Zend Framework пр...
 
Web осень 2013 лекция 9
Web осень 2013 лекция 9Web осень 2013 лекция 9
Web осень 2013 лекция 9
 

Practical usage of RxJava 2