[Видео] Доклады с митапа Android Paranoid

    Android почти исполнилось десять лет.

    Мы решили отметить это праздничным чаепитием со всеми, кто пришел в питерский офис Яндекса на второй митап Android Paranoid. Сказано — сделано. К нашему сожалению, маршмеллоу, шоколадное печенье и желейные бобы закончились еще 28 марта.



    Вместо них — доклады, записанные на видео, и короткая выжимка полезной информации для Android-разработчиков. Под катом о том,

    • что происходит после нажатия на иконку приложения;
    • как перевести приложение на Kotlin и уместиться в 300 строк кода;
    • как менялись инструменты фоновой работы в Android;
    • как быстро получить анимации в RecyclerView.

    Про анимации в RecyclerView


    Данил Терновых из Яндекс.Денег рассказал о том, как быстро и без затрат получить анимации в RecyclerView.

    Для тех, кто хочет попробовать их в работе — демо на GitHub. Подробности реализации — в видео.


    «Что происходит после тапа на иконку приложения,


    И даже чуть раньше?», — рассказал Владимир Теблоев из Сбербанк-Технологий. Рекомендуем посмотреть видео если вы думали, что вся жизнь в Android ограничивается вьюхами и активити, и ничего не знали про работу ядра, загрузчика, далвик, и все время задавались вопросом — зачем андроиду зигота? Для заинтересовавшихся — выжимка в трех эпизодах.

    Эпизод 1 — Уровни системы и Zygote

    Давным-давно инженеры молодой мобильной ОС спроектировали четыре уровня работы системы:

    • ядро с драйверами и Binder;
    • корневые библиотеки, библиотеки ОС и Dalvik;
    • Application Framework, неизменяемые компоненты системы — контент-провайдеры, активити-менеджеры и т.д.;
    • Пользовательские приложения.



    Чуть менее давно Dalvik превратился в Android Runtime, но сути процесса это не изменило — после тапа на иконку Launcher получает сигнал, передает его в менеджер активностей, тот передается в Zygote, а она создает новое приложение.

    Zygote — демон, который запускается при старте системы и инициализирует первичную виртуальную машину. Zygote позволяет создавать процессы для любых приложений в Android, клонируя себя и корневые библиотеки, которые необходимы для запуска всех приложений. Так экономятся время и память, потому что в первичном экземпляре Zygote уже инициализированы все нужные библиотеки. Останется только использовать Copy-on-Write и изменить ProcessID.


    Слева — первичный экземпляр, в середине — Zygote, отвечающая за компоненты Android, справа — наше приложение.

    Эпизод 2 — Жизненный цикл и взаимодействие процессов

    В Android существуют пять типов процессов и три приоритета, которые им назначаются.

    Критический приоритет назначается активным процессам — тем, с которыми пользователь взаимодействует прямо сейчас. Это может быть открытая активити или музыкальный плеер в UI.

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

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

    Основной способ взаимодействия процессов — IPC через Binder. Это драйвер, через который работают все корневые структуры Android. Модель взаимодействий — на схеме ниже.



    Предположим, что активити в процессе А должна получить данные из другого процесса. Метод Foo() обращается к Binder, который, в свою очередь, сериализует и упаковывает входные данные и передает целевому процессу для обработки. Затем нужные данные десериализуются, процесс Б что-то с ними делает и выполняет операции в обратном порядке.

    Отдельно Владимир рассказал обо всех этапах создания активностей в Android. Все детали — в видео.


    «Пользователь хочет 60 FPS,


    Для этого и нужна фоновая работа».

    Владимир Иванов из EPAM уже семь лет пишет под Android и iOS и успел похоронить Windows Phone. Владимир рассказал об эволюции инструментов для выполнения задач вне главного потока на Android. Речь о цепочке — AsyncTask, Loaders, Executors, EventBus, RxJava, Coroutines в Kotlin.

    В докладе очень много примеров, здесь — малая часть.

    Итерация 1 — AsyncTask

    Допустим, мы пишем приложение, которое показывает прогноз погоды. Последовательность действий примерно следующая:

    1. Определяем метод DoInBackground();
    2. С помощью http-клиента делаем запрос на сервер;
    3. Получаем и парсим ответ;
    4. Показываем пользователю.

    На последнем этапе возникает сложность — мы не можем просто взять и вернуть ответ с фона на UI-поток. Если UI одновременно использует другие потоки, приходится продумывать костыли и сложные блокировки. Чтобы этого не делать, разработчики интерфейсов рекомендуют обновляться только с UI-потока.

    Соответственно, нужен способ выполнить что-то на UI. В AsyncTask для этого используется метод onPostExecute, его и используем.

    public class LoadWeatherForecastTask
    extends AsyncTask < String,
    Integer,
    Forecast > {
    	public void onPostExecute(Forecast forecast) {
    		mTemperatureView.setText(forecast.getTemp());
    	}
    }
    

    У этого подхода одна большая проблема и несколько минусов — AsyncTask умерли, за исключением production-проектов, которым больше пяти лет.

    А минусы такие:

    1) Много кода для сетевых запросов;
    2) AsyncTask не знают ничего про жизненный цикл активностей и потенциально ведут к утечкам памяти;
    3) При смене конфигурации на лету (например, экран перевернулся) нужно перевыполнить запрос.

    Итерация 2 — Loaders

    С Android 3.0 пришли Loaders — команда Android придумала их, чтобы решить проблемы AsyncTask.

    class WeatherForecastLoader(context: Context) : AsyncTaskLoader < WeatherForecast > (context) {
    	override fun loadInBackground() : WeatherForecast {
    		try {
    			Thread.sleep(5000)
    		} catch(e: InterruptedException) {
    			return WeatherForecast("", 0F, "")
    		}
    		return WeatherForecast("Saint-Petersburg", 20F, "Sunny")
    	}
    }
    

    В частности, речь о повторном использовании результата при смене конфигурации. Проблема решается так:

    1) LoaderManager, привязанный к активности, хранит ссылки на несколько Loader в специальной структуре;
    2) Активность сохраняет все LoaderManager внутри NonConfigurationInstances;
    3) При создании новой активности система передает в нее данные из NonConfigurationInstances;
    4) Активность восстанавливает LoaderManager со всеми Loader.

    Минусы:
    1) Все еще много кода;
    2) Интерфейсы все еще сложные, а классы все еще абстрактные;
    3) Loaders — платформенный API Android, а значит, их нельзя переиспользовать на чистой Java.

    Итерация 3 — EventBus и ThreadPoolExecutors

    С появлением ThreadPoolExecutors передача данных с фона на UI стала выглядеть так:

    1) Заводим класс Background, а в нем — переменную Service;
    2) Инициализируем этот класс в ScheduledThreadPoolExecutor с нужным нам размером;
    3) Пишем вспомогательные методы, которые делают класс runnable или callable.

    public class Background {
    	private val mService = ScheduledThreadPoolExecutor(5)
    	fun execute(runnable: Runnable) : Future < *>{
    		return mService.submit(runnable)
    	}
    	fun < T > submit(runnable: Callable < T > ) : Future < T > {
    		return mService.submit(runnable)
    	}
    }
    

    Кроме выполнения на фоне, все еще нужно возвращать результат на UI. Для этого пишем handler и метод, который что-то постит на UI-треде.

    public class Background {…private lateinit
    	var mUiHandler: Handler
    	public fun postOnUiThread(runnable: Runnable) {
    		mUiHandler.post(runnable)
    	}
    }
    

    Не весь UI должен знать, что какой-то конкретный метод выполнился. Чтобы разделить ответственность, придумали EventBus. Это способ передачи событий из фонового потока на UI, при котором на общую шину подключены несколько слушателей, которые и обрабатывают эти события.



    Есть несколько готовых реализаций EventBus. Некоторые из них — Google Guava, Otto и GreenBot Eventbus.

    Из минусов:

    1. Источник данных о событии ничего не знает о том, как оно должно обрабатываться;
    2. По опыту докладчика, через некоторое время код с EventBus становится невозможно поддерживать.

    Итерация четвертая — RxJava, или «Хватит это терпеть!»

    Кто-то должен был придумать удобный инструмент для фоновой работы. В итоге у нас есть RxJava — большой фреймворк для работы с потоками событий.

    Предположим, мы пишем код, который должен авторизовываться на GitHub. Нужно завести по методу на каждую нужную операцию (в нашем случае — логин и получение списка репозиториев).

    interface ApiClientRx {
    	fun login(auth: Authorization) 
    	    : Single < GithubUser > 
    	fun getRepositories(reposUrl: String, auth: Authorization) 
    	    : Single < List < GithubRepository >>
    }
    

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

    Минусы:

    1. Крутая кривая обучения, учить долго и сложно;
    2. Много операторов, разницу между которыми сложно понять;
    3. На простой код из двух запросов и двух операторов создается около 20 объектов, что ведет к избыточному использованию памяти;
    4. Нерелевантные stacktrace, из 30 строк только одна может относиться к вашему коду.

    Плюсы:

    1. RxJava — стандарт де-факто. Владимир провел опрос в твиттере и выяснил, что 65% разработчиков в новых проектах будут использовать RxJava;
    2. Мощный API;
    3. RxJava — фреймворк с открытым исходным кодом, у которого есть большое сообщество;
    4. Код на RxJava легко покрывается юнит-тестами.

    Итерация пятая — Coroutines

    Coroutines — библиотека для фоновой работы с поддержкой внутри языка Kotlin.

    Ее плюсы:

    1. Не блокирующий подход — основной поток выполняется во время фоновой работы и встраивает в себя результаты по мере выполнения;
    2. Асинхронный код в синхронном стиле

    private fun attemptLogin() {
    	launch(UI) {
    		val auth = BasicAuthorization(login, pass) try {
    			showProgress(true) val userInfo = login(auth).await() val repoUrl = userInfo.repos_url val list = getRepositories(repoUrl, auth).await() showRepositories(this@LoginActivity, list.map {
    				it - >it.full_name
    			})
    		} catch(e: RuntimeException) {
    			Toast.makeText(this@LoginActivity, e.message, LENGTH_LONG).show()
    		} finally {
    			showProgress(false)
    		}
    	}
    }
    

    3) Средства языка вместо операторов;
    4) Просто изучать — кривая обучения почти не кривая;
    5) После небольшого обдумывания юнит-тесты становятся почти такими же, как для синхронного кода.

    Минусы:

    1) Недавно вышли из статуса экспериментальных;
    2) Это не часть языка, а библиотека;
    3) Не для всего есть адаптеры;
    4) Coroutines — не замена RxJava. Они не сильно помогут в сложных случаях со сложным потоком событий.

    Остальное про Coroutines (включая примеры) лучше послушать в докладе:


    Как уместить код в 300 строк, программируя на Kotlin


    Год назад, на Google IO 2017 анонсировали то, что Kotlin стал официальным языком Android. Доклад Юрия Чечеткина из Альфа-Банка о том, как начать переезжать на новый язык, сократить классы до 300 строк и не сойти с ума.

    Доклад — практический ликбез по тому, как писать компактно и красиво. Он ориентирован на продвинутую аудиторию, которая знает основные особенности Kotlin.

    В докладе очень много примеров использования и сравнений кода на двух языках, поэтому здесь приведем только интересные факты и выводы.

    Основные проблемы с миграцией на Kotlin:

    1. Legacy-код на Java. Большие классы на несколько тысяч строк кода очень сложны для конвертации средствами среды разработки;
    2. Зависимости — Lombok, Stream API и т.д.;
    3. Завышенные требования к коду внутри команды. Проводятся регулярные автоматические проверки кода на ограничение в 300 строк и code review;
    4. Kotlin — новый язык, и сложно сформулировать требования, пока нет единых конвенций;
    5. Kotlin компилируется дольше;
    6. Синтаксический сахар — «большая сила, но большая ответственность».



    Выводы:

    • Спустя год использования Kotlin стало меньше кода — он стал чище, стало удобнее делать code review;
    • Нет старых методов Java, нет лишних зависимостей, только стандартные возможности языка;
    • Меньше костылей и багов;
    • Больше возможностей — некоторые вещи, реализуемые на Kotlin, невозможно написать на Java.

    Остальное — в видео.


    Следите за мероприятиями и буднями команды Я.Денег в ВК, фейсбуке и инстаграме.
    Все конференции и митапы Яндекса — на Я.Встречах.

    Яндекс.Деньги

    97,94

    Об электронных платежах и устройстве Я.Денег

    Поделиться публикацией
    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое