Книжная полка Сохранить
Размер шрифта:
А
А
А
|  Шрифт:
Arial
Times
|  Интервал:
Стандартный
Средний
Большой
|  Цвет сайта:
Ц
Ц
Ц
Ц
Ц

Эффективное использование потоков в операционной системе Android

Покупка
Артикул: 712462.01.99
К покупке доступен более свежий выпуск Перейти
Чтобы написать действительно полезное и удобное приложение для Android, то без многопоточности никак не обойтись, но как узнать о технологиях и методах, которые помогут решить такую задачу? Книга с практической точки зрения описывает несколько асинхронных механизмов, доступных в программной среде Android SDK, а также рассматривает основные принципы и правила выбора одного из них, лучше всего подходящего для создаваемого приложения. Издание предназначено для программистов разной квалификации, уже работающих под Android и желающих улучшить качество создаваемых программ.
Ёранссон, А. Эффективное использование потоков в операционной системе Android / А. Ёранссон ; пер. с англ. А.В. Снастина. - Москва : ДМК Пресс, 2015. - 304 с. - ISBN 978-5-97060-168-6. - Текст : электронный. - URL: https://znanium.com/catalog/product/1028042 (дата обращения: 19.04.2024). – Режим доступа: по подписке.
Фрагмент текстового слоя документа размещен для индексирующих роботов. Для полноценной работы с документом, пожалуйста, перейдите в ридер.
Эффективное 
использование потоков 
в операционной системе 
Android

Андерс Ёранссон

Efficient Android 
Threading

Anders Gо..ransson

Эффективное 
использование потоков 
в операционной системе 
Android

Москва, 2015

Андерс Ёранссон

УДК 004.451.9Android:004.451.2
ББК 32.972.11
 
Е69

 
 
Ёранссон А.

Е69 
Эффективное использование потоков в операционной системе
Android / пер. с англ. А. В. Снастина. – М.: ДМК Пресс, 2015. –
304 с.: ил.

ISBN 978-5-97060-168-6

Чтобы написать действительно полезное и удобное приложение для Android,

то без многопоточности никак не обойтись, но как узнать о технологиях и методах, которые помогут решить такую задачу? Книга с практической точки зрения
описывает несколько асинхронных механизмов, доступных в программной среде
AndroidSDK,атакжерассматриваетосновныепринципыиправилавыбораодного
из них, лучше всего подходящего для создаваемого приложения.

Издание предназначено для программистов разной квалификации, уже ра
ботающих под Android и желающих улучшить качество создаваемых программ.

 
УДК 004.451.9Android:004.451.2
 
ББК 32.972.11

© 2015 DMK Press
Authorized Russian translation of the English edition of Efficient Android

Threading, ISBN 9781449364137 © 2014 Anders Gо..ransson

This translation is published and sold by permission of O’Reilly Media, Inc.,

which owns or controls all rights to publish and sell the same.

Все права защищены. Любая часть этой книги не может быть воспроиз
ведена в какой бы то ни было форме и какими бы то ни было средствами без
письменного разрешения владельцев авторских прав.

Материал, изложенный в данной книге, многократно проверен. Но по
скольку вероятность технических ошибок все равно существует, издательство
не может гарантировать абсолютную точность и правильность приводимых
сведений. В связи с этим издательство не несет ответственности за возможные
ошибки, связанные с использованием книги.

ISBN 978-1-449-36413-7 (анг.)
Copyright © 2014 Anders Gо..ransson

ISBN 978-5-97060-168-6 (рус.)
© Оформление, перевод, ДМК Пресс, 2015

Посвящается Анне, Фабиану и Иде

Содержание

Предисловие ...........................................................13

Глава 1. Компоненты ОС Android и необходимость 
параллельных вычислений .........................................19
Стек программной среды ОС Android ........................................................................19
Архитектура приложения ...............................................................................................21
Приложение .................................................................................................................21
Компоненты .................................................................................................................21
Activity ..................................................................................................................22
Service ....................................................................................................................24
ContentProvider..................................................................................................24
BroadcastReceiver ..............................................................................................25
Выполнение приложения ................................................................................................25
Процесс Linux .............................................................................................................25
Жизненный цикл .......................................................................................................26
Запуск приложения ..........................................................................................26
Завершение приложения ................................................................................27
Структурирование приложений для улучшения производительности .........29
Создание отзывчивых приложений с помощью потоков ............................30
Резюме ...................................................................................................................................32

Часть I. Основы ........................................................33

Глава 2. Многопоточность в Java .................................34
Основы использования потоков ...................................................................................34
Выполнение .................................................................................................................35
Приложение с одним потоком ..............................................................................37
Многопоточное приложение .................................................................................37
Увеличение потребления ресурсов .............................................................38
Повышенная сложность ..................................................................................38
Нарушение целостности данных .................................................................38
Безопасное состояние потока ........................................................................................40
Внутренняя блокировка и монитор Java ...........................................................41
Синхронизация доступа к совместно используемым ресурсам ................43
Использование внутренней блокировки ...................................................43
Явное использование механизмов блокировки ......................................45
Пример: потребитель и производитель ..............................................................46
Стратегии выполнения задачи ......................................................................................48
Проектирование параллельного выполнения .................................................49
Резюме ...................................................................................................................................50

Содержание  7

Глава 3. Потоки в ОС Android .......................................51
Потоки приложения ОС Android .................................................................................51
UI-поток ........................................................................................................................51
Связующие потоки ....................................................................................................52
Фоновые потоки .........................................................................................................53
Процесс Linux и потоки ...................................................................................................53
Планирование .............................................................................................................57
Приоритет ............................................................................................................58
Управляющие группы ......................................................................................59
Резюме ...................................................................................................................................61

Глава 4. Взаимодействие потоков ...............................62
Программные каналы .......................................................................................................62
Основы использования программного канала ................................................64
Пример: обработка текста в рабочем потоке ....................................................66
Совместно используемая память..................................................................................68
Механизм сигналов ...................................................................................................69
Блокирующая очередь BlockingQueue .......................................................................71
Передача сообщений в ОС Android .............................................................................73
Пример: простая передача сообщений ...............................................................74
Классы, используемые при реализации механизма передачи 
сообщений ....................................................................................................................77
Класс MessageQueue .........................................................................................77
Интерфейс MessageQueue.IdleHandler ......................................................79
Пример: использование интерфейса IdleHandler 
для завершения ненужного потока .............................................................80
Класс Message .............................................................................................................82
Состояние «инициализировано» .................................................................84
Состояние «ожидание» ...................................................................................85
Состояние «передано» .....................................................................................85
Состояние «готово к повторному использованию» ..............................85
Класс Looper ................................................................................................................86
Завершение работы объекта Looper ............................................................87
Объект Looper в UI-потоке ............................................................................88
Класс Handler ..............................................................................................................88
Создание и настройка ......................................................................................89
Создание сообщения ........................................................................................90
Вставка сообщения в очередь ........................................................................90
Пример: передача сообщений в двух направлениях .............................92
Обработка сообщений ......................................................................................95
Удаление сообщений из очереди ..........................................................................97
Наблюдение за очередью сообщений .................................................................99
Получение текущего состояния очереди сообщений ...........................99

 Содержание

Отслеживание обработки очереди сообщений .....................................102
Взаимодействие с UI-потоком ....................................................................................103
Резюме .................................................................................................................................104

Глава 5. Взаимодействие между процессами ..............105
Механизм вызова удалённых процедур в ОС Android .......................................105
Объект Binder............................................................................................................106
Язык AIDL ..........................................................................................................................108
Синхронные вызовы удалённых процедур .....................................................110
Асинхронные вызовы удалённых процедур ...................................................113
Передача сообщений с использованием объекта Binder ....................................115
Однонаправленное взаимодействие .................................................................117
Взаимодействие в двух направлениях .............................................................119
Резюме .................................................................................................................................120

Глава 6. Управление памятью ...................................121
Сборка мусора ...................................................................................................................121
Утечки памяти, связанные с использованием потоков .......................................123
Выполнение потока .................................................................................................125
Внутренние классы .........................................................................................126
Статические внутренние классы ................................................................127
Рассогласование жизненных циклов ........................................................128
Взаимодействие потоков .......................................................................................131
Отправка сообщения с данными ................................................................132
Передача сообщения с задачей ...................................................................133
Устранение утечек памяти ............................................................................................134
Использование статических внутренних классов ........................................135
Использование слабых ссылок ...........................................................................135
Остановка рабочего потока ..................................................................................136
Переключение рабочих потоков .........................................................................136
Очистка очереди сообщений ...............................................................................136
Резюме .................................................................................................................................137

Часть II. Механизмы асинхронного выполнения ...........138

Глава 7. Управление жизненным циклом простого 
потока ..................................................................139
Основы использования потоков .................................................................................139
Жизненный цикл .....................................................................................................139
Прерывания ...............................................................................................................141
Неперехватываемые исключения ......................................................................143
Управление потоком .......................................................................................................145
Определение и запуск потока ..............................................................................145
Анонимный внутренний класс ...................................................................145

Содержание  9

Общедоступный поток ..................................................................................146
Определение потока как статического внутреннего класса .............146
Обзор возможных вариантов выбора определения потока ..............147
Сохранение потока в рабочем состоянии ........................................................147
Сохранение потока в рабочем состоянии средствами класса 
Activity ................................................................................................................148
Сохранение потока в рабочем состоянии средствами класса 
Fragment..............................................................................................................151
Резюме .................................................................................................................................153

Глава 8. HandlerThread: механизм очереди 
сообщений высокого уровня .....................................155
Основы использования HandlerThread ....................................................................155
Жизненный цикл HandlerThread ...............................................................................157
Случаи использования ...................................................................................................159
Повторяющееся выполнение задачи .................................................................159
Связанные задачи ....................................................................................................160
Пример: обеспечение надёжности данных с помощью 
SharedPreferences .............................................................................................160
Объединение задач в цепочку .............................................................................163
Пример: сетевые вызовы в цепочке задач ...............................................163
Вставка задач по условию .....................................................................................166
Резюме .................................................................................................................................167

Глава 9. Управление выполнением потока средствами 
фреймворка Executor ..............................................168
Executor ...............................................................................................................................169
Пулы потоков ....................................................................................................................171
Предопределённые пулы потоков ......................................................................172
Пулы потоков, определяемые разработчиком ...............................................173
Конфигурация ThreadPoolExecutor .........................................................173
Проектирование пула потоков ............................................................................175
Определение размера .....................................................................................175
Динамические потоки в пуле ......................................................................177
Ограниченная или неограниченная очередь задач ..............................177
Конфигурация потока ....................................................................................178
Расширение возможностей ThreadPoolExecutor .................................179
Жизненный цикл .....................................................................................................180
Корректное завершение работы пула потоков ..............................................181
Варианты использования пула потоков и возникающие 
при этом сложности ................................................................................................183
Предпочтение отдаётся созданию потока, а не организации 
очереди ................................................................................................................183

 Содержание

Обработка предварительно подготовленных очередей задач ..........183
Опасная ситуация при нулевом количестве базовых потоков 
в пуле ...................................................................................................................184
Управление задачами ......................................................................................................184
Представление задачи ............................................................................................185
Добавление задач .....................................................................................................186
Заявление отдельной задачи .......................................................................187
Метод invokeAll ................................................................................................188
Метод InvokeAny .............................................................................................190
Отвергнутые задачи ................................................................................................191
ExecutorCompletionService ...........................................................................................191
Резюме .................................................................................................................................194

Глава 10. Связывание фоновой задачи с UI-потоком 
с помощью AsyncTask ..............................................196
Основы использования класса AsyncTask ...............................................................196
Создание и начало работы ....................................................................................199
Отмена .........................................................................................................................200
Состояния ...................................................................................................................202
Пример: ограничение режима выполнения AsyncTask только 
одной задачей в любой момент времени .................................................203
Реализация AsyncTask ....................................................................................................203
Пример: загрузка изображений ..........................................................................204
Выполнение задачи в фоновом режиме ...................................................................207
Глобальная среда выполнения в приложении ...............................................209
Выполнение в разных версиях платформы ....................................................211
Настраиваемое выполнение .................................................................................213
Пример: неглобальное последовательное выполнение ......................213
Альтернативы AsyncTask ...............................................................................................214
Случаи излишне упрощённой реализации AsyncTask................................215
Фоновые задачи, для которых требуется объект Looper ...........................216
Локальная служба ...................................................................................................216
Использование метода execute(Runnable) ......................................................216
Резюме .................................................................................................................................217

Глава 11. Службы....................................................218
Причины использования служб для асинхронного выполнения ...................218
Локальные, удалённые и глобальные службы .......................................................220
Создание и выполнение .................................................................................................222
Жизненный цикл .............................................................................................................223
Запускаемая служба ........................................................................................................226
Реализация метода onStartCommand ...............................................................226
Повторный запуск ...................................................................................................227
Служба, управляемая пользователем ...............................................................230

Содержание  11

Пример: соединение по протоколу Bluetooth ........................................230
Служба, управляемая задачей .............................................................................234
Пример: параллельная загрузка .................................................................234
Подключаемая служба ...................................................................................................237
Локальное подключение .......................................................................................239
Выбор механизма асинхронного выполнения .......................................................242
Резюме .................................................................................................................................243

Глава 12. Класс IntentService ....................................244
Основы использования IntentService .......................................................................244
Эффективные способы использования IntentService .........................................246
Задачи, выполнение которых должно быть последовательным .............246
Пример: взаимодействие с веб-службой .................................................246
Асинхронное выполнение в BroadcastReceiver .............................................249
Пример: периодически выполняемые длительные операции..........250
Сравнение IntentService и Service ..............................................................................252
Резюме .................................................................................................................................253

Глава 13. Доступ к провайдерам контента с помощью 
AsyncQueryHandler ..................................................254
Краткий обзор основ использования провайдеров контента ...........................254
Настройка ContentProvider для обработки в фоновом режиме ......................256
Использование AsyncQueryHandler..........................................................................258
Пример: список контактов с раскрывающимися элементами .................260
Как работает AsyncQueryHandler ......................................................................263
Ограничения ..............................................................................................................264
Резюме .................................................................................................................................265

Глава 14. Автоматическое выполнение в фоновом 
режиме с помощью загрузчиков Loader ......................266
Фреймворк Loader ...........................................................................................................268
Класс LoaderManager .............................................................................................268
Сравнение методов initLoader() и restartLoader()...............................270
Интерфейс LoaderCallbacks .................................................................................272
Класс AsyncTaskLoader ..........................................................................................274
Надёжная загрузка данных с помощью CursorLoader ........................................275
Использование CursorLoader ..............................................................................275
Пример: список контактов ....................................................................................276
Добавление поддержки CRUD ...........................................................................277
Пример: использование CursorLoader вместе с обработчиком 
AsyncQueryHandler .........................................................................................278
Реализация специализированных загрузчиков .....................................................281
Жизненный цикл загрузчика ..............................................................................282
Фоновый режим загрузки .....................................................................................283

 Содержание

Пример: простой специализированный загрузчик ..............................284
Управление контентом ...........................................................................................286
Доставка кэшированных результатов ...............................................................287
Пример: специализированный загрузчик файлов .......................................288
Работа с несколькими загрузчиками ................................................................291
Резюме .................................................................................................................................292

Глава 15. Подведение итогов: выбор механизма 
асинхронного выполнения .......................................294
Сохраняйте простоту ......................................................................................................295
Управление потоками и ресурсами ............................................................................296
Организация обмена сообщениями для улучшения отзывчивости ...............297
Как избежать неожиданного и нежелательного завершения задачи .............298
Простой доступ к провайдерам контента ................................................................299

Список литературы .................................................301

Предисловие

Книга «Эффективное использование потоков в операционной системе Android» демонстрирует методы проектирования высококачественных и надёжных в эксплуатации многопоточных приложений 
для операционной системы Android. В книге рассматриваются асинхронные механизмы, доступные в программной среде Android SDK, 
и соответствующие приемы создания быстрых, эффективных и правильно структурированных приложений.
Не вызывает сомнений, что без многопоточности немыслимо создание действительно полезных и удобных приложений, но ее применение влечет увеличение сложности приложений и вероятности 
появления ошибок во время выполнения. Одна из причин этой проблемы – трудности, непосредственно связанные с самим механизмом 
одновременной работы нескольких потоков, и другая причина – нерациональное использование возможностей платформы Android.
Цель этой книги – научить разработчиков приложений правильно 
выбирать требуемый асинхронный механизм, основываясь на полном 
понимании всех его достоинств и недостатков. При использовании 
правильно выбранного асинхронного механизма там, где он наиболее 
уместен, изрядная доля сложности перекладывается на программную платформу, при этом исходный код приложения становится более простым для сопровождения и поддержки, а вероятность ошибок 
в нём снижается. Как правило, асинхронное выполнение не должно 
повышать сложность исходного кода сверх реальной необходимости, 
и этого можно добиться посредством разумного выбора асинхронного 
механизма из комплекта, предлагаемого в программной среде Android.
Несмотря на то что любой асинхронный механизм высокого уровня уже с первого взгляда может показаться весьма удобным для 
практического применения, постоянно требуется не бездумное использование, но глубокое его понимание, иначе в приложении будут 
возникать труднообъяснимые ошибки времени выполнения, снижение производительности или утечки памяти. Именно поэтому в книге 
содержатся не только практические рекомендации и примеры, но и 
подробные объяснения лежащих в их основе механизмов асинхронного выполнения.

Для кого предназначена эта книга
Эта книга предназначена для программистов на языке Java, уже знакомых с общими принципами программирования для платформы 

 Предисловие

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

Краткое содержание книги
Книга состоит из двух частей. В части I описываются основы многопоточного программирования в ОС Android, то есть на языке Java, 
в Linux, с применением различных обработчиков, и их влияние на 
разработку приложений. В части II основное внимание уделено практическому применению многопоточности с более глубоким исследованием асинхронных механизмов, предоставляемых в распоряжение 
любому приложению.
В части I рассказывается, как программы на Java работают с потоками. Программистам для ОС Android иногда приходится напрямую 
использовать библиотеки Java, поэтому понимание их возможностей 
важно для правильного применения конструкций более высокого 
уровня, описываемых в части II.
Глава 1 описывает устройство среды выполнения ОС Android и 
влияние различных компонентов типичного Android-приложения на 
использование многопоточности и параллельных вычислений.
Глава 2 рассматривает основы параллельного выполнения задач 
в языке Java.
Глава 3 обсуждает функционирование потоков в ОС Android и особенности выполнения  прикладных потоков в Linux, в том числе такие важные темы, как планирование и управление группами потоков, 
а также их влияние на время ответной реакции приложения.
Глава 4 рассматривает основные механизмы обмена данными между потоками, такие как совместно используемая (разделяемая) память, сигналы и широко применяемые сообщения ОС Android.
Глава 5 показывает, как ОС Android дополняет механизмы взаимодействия процессов, предоставляемые ядром Linux, такими механизмами, как вызов удаленных процедур (RPC) и обмен сообщениями.
Глава 6 объясняет, как предотвращать утечки памяти в приложениях, которые могут приводить к существенному ухудшению работы системы в целом, из-за чего пользователи, вероятнее всего, будут 
прос то избавляться от подобных приложений.
В части II рассматриваются библиотеки и программные конструкции более высокого уровня, которые делают программирование с использованием потоков более безопасным и простым.

Предисловие  15

Глава 7 описывает самую простую программную конструкцию поддержки асинхронного выполнения – java.lang.Thread, а также обработку различных проблем и нестандартных ситуаций, которые могут 
возникать при её использовании.
Глава 8 демонстрирует удобный способ последовательного запуска 
задач в фоновом режиме.
Глава 9 предлагает вниманию читателя методики планирования, 
обработки ошибок и некоторые другие аспекты использования потоков, такие как организация пулов потоков.
Глава 10 рассматривает AsyncTask, один из наиболее популярных 
механизмов асинхронного выполнения, а также правильное его применение и пути обхода потенциальных сложностей.
Глава 11 описывает важный компонент Service, особенно полезный 
для обеспечения сразу нескольких приложений одними и теми же 
функциональными возможностями или для поддержки приложения 
в рабочем состоянии во время выполнения в фоновом режиме.
Глава 12 продолжает последнюю тему предыдущей главы описанием эффективной методики запуска новых потоков выполнения из 
основного потока обслуживания графического пользовательского 
интерфейса.
Глава 13 посвящена механизму высокого уровня, упрощающему 
быстрый асинхронный доступ к компонентам типа ContentProvider.
Глава 14 объясняет, как обновлять компоненты графического 
пользовательского интерфейса с помощью загрузчиков (loaders) при 
асинхронной передаче новых данных вне зависимости от времени изменения их содержимого.
Глава 15 – итоговое резюме по всем методикам, описанным в книге, 
и обсуждение выбора приемов, наиболее подходящих для конкретного приложения. Здесь предлагаются правила и рекомендации, помогающие разработчику сделать правильный выбор.

Условные обозначения и соглашения, принятые 
в книге
В книге используются следующие типографские соглашения:
Курсив используется для выделения важных положений, новых 
терминов, имён команд и утилит, а также имён файлов и каталогов.
Моноширинный шрифт используется для обозначения имён переменных, функций, типов, объектов и других программных конструкций 
и элементов исходного кода.

 Предисловие

Моноширинный курсив используется для обозначения в исходном коде 
или в командах шаблонных меток-заполнителей, которые должны 
быть заменены фактическими значениями.

 Так обозначается совет, указание или примечание общего характера.

 Эта пиктограмма предупреждает о неочевидных и обычно скрытых от неопытного разработчика «подводных камнях» и «ловушках», которых следует 
избегать.

Примеры исходного кода

Дополнительные материалы (примеры исходного кода, учебные 
задания и т. п.) можно получить по ссылке https://github.com/
andersgoransson/eatbookexamples.
Эта книга написана для того, чтобы помочь программистам в работе. Вообще говоря, вы можете использовать код из данной книги 
в своих программах и в документации. Если вы копируете для собственных нужд фрагмент исходного кода незначительного размера, 
то нет необходимости обращаться к автору и издателям для получения разрешения на это. Например, при включении в свою программу 
нескольких небольших фрагментов кода из книги вам не потребуется 
какое-либо специальное разрешение. Но для продажи или распространения CD-диска с примерами из книг издательства O’Reilly необходимо будет получить официальное разрешение на подобные действия. При ответах на вопросы можно цитировать текст данной книги 
и приводить примеры кода из неё без дополнительных условий. При 
включении крупных фрагментов исходного кода из книги в документацию собственного программного продукта также потребуется официальное разрешение.
Мы будем благодарны за добавление библиографической ссылки 
на источник при цитировании. Обычно ссылка состоит из названия 
книги, имени автора, наименования издательства и номера по ISBNклассификации.
Если у вас возникли сомнения в легальности использования примеров исходного кода без получения специального разрешения при 
условиях, описанных выше, то без колебаний обращайтесь по адресу 
электронной почты: permissions@oreilly.com.
Примеры исходного кода из книги доступны по адресу: https://
github.com/andersgoransson/eatbookexamples.

Предисловие  17

Онлайн-сервис Safari® Books
Safari Books Online – это электронная библиотека, содержащая авторитетную информацию в виде книг и видеоматериалов, созданных ведущими специалистами в области технологий и бизнеса.
Профессионалы в различных областях техники, разработчики программного обеспечения, веб-дизайнеры, бизнесмены и творческие 
работники используют Safari Books Online как основной ресурс для 
научных исследований, решения профессиональных задач, обучения 
и подготовки к сертификационным испытаниям.
Библиотека Safari Books Online предлагает широкий выбор продуктов и тарифов для организаций, государственных учреждений и 
частных лиц. Подписчики получают доступ к поисковой базе данных, 
содержащей информацию о тысячах книг, видеоматериалов и ещё 
не опубликованных рукописей от таких известных издательств, как 
O’Reilly Media, Prentice Hall Professional, Addison-Wesley Professional, 
Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco Press, 
John Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt, 
Adobe Press, FT Press, Apress, Manning, New Riders, McGraw-Hill, 
Jones & Bartlett, Course Technology, и многих других. Более подробную информацию о Safari Books Online можно получить на сайте 
https://www.safaribooksonline.com/.

От издательства
Замечания, предложения и вопросы по этой книге отправляйте по 
адресу:

O’Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (звонок из США или Канады)
707-829-0515 (международный или местный звонок)
707-829-0104 (факс)

или по электронной почте:

bookquestions@oreilly.com

Cписок опечаток и ошибок, файлы с примерами и другую дополнительную информацию можно найти по адресу: http://bit.ly/efficientandroid-threading.

К покупке доступен более свежий выпуск Перейти