Меня зовут Андрей Данилов, под Android начал разрабатывать в далеком 2012 году. Успел поработать примерно везде — в крошечном стартапе, маленькой продуктовой компании, аутсорсах и крупных компаниях, таких как Авито, Ситимобил, Яндекс. Успел выступить примерно на десятке митапов и конференций. Сейчас член Программного комитета конференции Apps conf X. В данный момент руковожу парой команд в Т-Банке.
Развитие Android разработки глазами разработчика
Довольно много времени прошло с 2007 года, когда Android стал доступен для разработчиков. С тех пор прошло почти 18 лет. За это время изменилось примерно все: железо стало мощнее, интернет быстрее, IDE умнее, а проекты сложнее. Предлагаю оглянуться назад и посмотреть, что же именно изменилось с тех пор для разработчиков.
![](https://habrastorage.org/getpro/habr/upload_files/1af/ce5/44c/1afce544cedbf9922a2d5aba8c6e3005.jpeg)
Ранний Android 2007 - 2014
Система сборки Ant
![](https://habrastorage.org/getpro/habr/upload_files/f16/f53/908/f16f53908bb687ba4a07e153c0ed6a04.png)
Первое, что бросилось бы в глаза современному разработчику — это отсутствие Gradle для сборки. Вместо нее использовался Ant. В те времена вообще можно было подумать, что для Android используется XML driven development. Вся верстка в XML, скрипт сборки в XML, строки и ресурсы в XML и даже сохраненные настройки (SharedPreferences) под капотом все тот же XML. Часть использования XML, конечно, дожила и до наших дней, но от части все же удалось избавиться, спасибо и на этом.
Использование Ant выглядело примерно так — в проекте был build.xml, и все задачи сборки (в Ant они называются targets) нужно было прописывать в нем:
<?xml version="1.0" encoding="UTF-8"?>
<project name="HelloWorld" default="help">
<!-- Определяем свойства проекта -->
<property name="sdk.dir" location="/path/to/android/sdk" />
<property name="build.target" value="android-19" />
<!-- Настраиваем путь к исходным файлам -->
<path id="source.path">
<fileset dir="${src.dir}">
<include name="**/*.java" />
</fileset>
</path>
<!-- Путь к ресурсам -->
<path id="resource.path">
<fileset dir="${res.dir}">
<include name="**/*" />
</fileset>
</path>
<!-- Включаем библиотеки и внешние JAR файлы -->
<path id="lib.path">
<pathelement path="${sdk.dir}/platforms/${build.target}/android.jar" />
</path>
<!-- Задача для компиляции исходного кода -->
<target name="compile">
<javac srcdir="${src.dir}" destdir="${bin.dir}" includeantruntime="false">
<classpath>
<path refid="lib.path" />
</classpath>
</javac>
</target>
<!-- Задача для создания ресурсов -->
<target name="create-resources">
<aapt executable="${sdk.dir}/build-tools/${build.target}/aapt.exe"
ignoreAssets="true" verbose="true">
<res path="${res.dir}" />
</aapt>
</target>
<!-- Задача для упаковки APK файла -->
<target name="package-apk">
<jar jarfile="${out.dir}/${ant.project.name}.apk" basedir="${bin.dir}"/>
<zipalign executable="${sdk.dir}/build-tools/${build.target}/zipalign.exe"
verbose="true" outfile="${out.dir}/${ant.project.name}-aligned.apk"
inputfile="${out.dir}/${ant.project.name}.apk"/>
</target>
</project>
В целом было, конечно, функционально, но уж очень многословно. При этом была возможность написания собственных, даже сложных таргетов, а все базовые таргеты для сборки Android приложения шли «в коробке».
IDE - Eclipse and NetBeans
![](https://habrastorage.org/getpro/habr/upload_files/349/479/15a/34947915ab301a1efa74bd7d7c7f8767.jpg)
Конечно, к современной среде разработки пришли не сразу. До Android Studio, сделанной на базе Intellij IDEA, вся разработка под Android (так же, как и под Java, к слову) велась либо в Eclipse, либо в NetBeans. Обе IDE вполне успешно могут работать с Android проектами до сих пор, конечно же, при условии разработки на Java — Kotlin-плагины уже не поддерживаются. Разница между ними была примерно как между Coca Cola и Pepsi — чисто вкусовщина. NetBeans предоставлял чуть более дружелюбный и не перегруженный интерфейс, Eclipse имел большее количество плагинов. Из минусов этих IDE — отсутствие хороших инструментов вроде умного рефакторинга, анализатора кода, а также прямой поддержки Google.
Fun Fact
Первые версии Intellij IDEA и Eclipse появились в 2001 году. Чтобы для Android разработки обойти по популярности Eclipse, Intellij IDEA понадобилось 7 лет, причем не без помощи поддержки Google. И 15 лет, чтобы обойти Eclipse по популярности в Java.
RoboGuice
![](https://habrastorage.org/getpro/habr/upload_files/9a0/c9d/58a/9a0c9d58a074ee9f09e7a848392c5588.jpg)
RoboGuice был, по сути, первым по популярности фрэймворком для Dependency Injection до Dagger и даже местами соответствовал JSR-330. Работал с View, ресурсами и кастомными классами. Но выглядел он максимально странно:
@ContentView(R.layout.main)
class RoboWay extends RoboActivity {
@InjectView(R.id.name) TextView name;
@InjectView(R.id.thumbnail) ImageView thumbnail;
@InjectResource(R.drawable.icon) Drawable icon;
@InjectResource(R.string.app_name) String myName;
@Inject LocationManager loc;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
name.setText( "Hello, " + myName );
}
}
Да, какие-то строчки кода это экономило, но только за счет кодогенерации. Получалось, что для маленьких проектов он добавлял экономию на спичках — экономию 1-2 строки для каждой сущности, что совсем незначительно. Зато для крупных проектов RoboGuice добавлял минуты холодной сборки. Страшно подумать, что было бы сейчас в случае с проектами, состоящими из многих сотен экранов. В общем, уже к 2013 году проект по факту перестал поддерживаться, а вскоре и использоваться.
ActionBarSherlock
![](https://habrastorage.org/getpro/habr/upload_files/0ab/c78/e99/0abc78e99e36ac0f3f8a355efbd34df9.jpg)
В древние времена, когда 4 андроида еще не существовало, сделать приличный Action Bar было довольно тяжко. В общем-то, ActionBarSherlock решал эту проблему и при этом использовал нативный ActionBar в 4 андроиде, так что в какой-то момент использовался почти повсеместно.
AsyncTask
![](https://habrastorage.org/getpro/habr/upload_files/175/a45/50f/175a4550f04188c4d02fd1d23c0fc31a.jpeg)
AsyncTask был частью стандартного SDK, но поистине легендарной частью. Основная проблема была в том, что из-за обилия неопытных разработчиков, засилья именно Async Task во всевозможных курсах и гайдах по андроид разработке, а также отсутствия хороших архитектурных практик по разделению слоев (привет Clean Architecture) сами Async Tasks создавались, как и все остальное — прямо в коде Activity. Это приводило к созданию анонимных внутренних классов с неявной ссылкой на Activity и дальнейшим утечкам памяти или даже падениям приложения. Например, в случае переворота экрана. Выглядело это так:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
textView.setText(“Progress ” + progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Стоило перевернуть экран — textView переставал существовать, но сам Async Task продолжал работу и при попытке поменять текст сразу клал все приложение на лопатки.
Масштабы безумия были столь массовыми, что на долгое время этот кейс внесли в вопросы на собеседованиях Android разработчиков. К счастью, с 30 API уже Deprecated.
EventBus
![](https://habrastorage.org/getpro/habr/upload_files/5f2/618/db2/5f2618db273e3047992e9061b109816c.jpg)
Из-за отсутствия адекватных, сравнительно простых и удобных механизмов обновления UI появилась библиотека EventBus. Концепция была простой, как пробка:
Надо было просто зарегистрировать прием события:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
// Do something
}
Зарегистрировать прием сообщений:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
И в любом месте дергать его отправку:
EventBus.getDefault().post(new MessageEvent());
Удобно? Да вполне. На тот момент даже показалось глотком свежего воздуха. Но позже начались проблемы. По мере роста проектов довольно скоро оказалось, что количество таких событий может исчисляться десятками и сотнями. Они могут прилетать из разных частей приложения и приводить к багам, которые так просто не вычислить. Тестировать такое тоже было не очень-то удобно. Как итог, использование этой библиотеки довольно быстро было прекращено, а в комьюнити стало автоматически ассоциироваться с лапшой в коде.
Архитектура MVC
![](https://habrastorage.org/getpro/habr/upload_files/c82/d1a/489/c82d1a48973d7d4b1ded88e7764a6f72.png)
Если не брать в расчёт проекты с God Object Activity, которые отвечали примерно за все вообще, то основной архитектурой был MVC. По разбивке на слои выглядел он так:
Model — логика, данные и т.д.
View — XML с версткой
Controller — Activity / Fragment
Очевидно, при этом подходе Activity было перегружено ответственностью и при этом из-за жесткой привязки к Android фрэймворку усложняло тестирование unit тестами.
Эра бурных перемен 2014 - 2018
Android Studio
![](https://habrastorage.org/getpro/habr/upload_files/58f/488/d66/58f488d6644400a7b44df9608bb2cea8.jpg)
Android Studio появилась в 2014 году и используется активно до сих пор. Да, первые версии были настолько кривыми и косыми, что после нескольких дней использования мне пришлось пересесть на Eclipse. Тем не менее спустя полгода после релиза проблемы были починены, а позиция основной IDE для Android разработки стала нерушимой.
Gradle
![](https://habrastorage.org/getpro/habr/upload_files/501/dd1/76b/501dd176bf9b25fca092b57355a770b0.jpg)
Первая версия Gradle появилась еще в 2009. Основным языком выбрали Groovy, так как он, с одной стороны, привязан к JVM, с другой стороны — с динамической типизацией, что удобно для скриптов и довольно понятно Java-разработчикам. Это было особенно важно, учитывая, что для рядового разработчика с Ant и Maven работать было сложнее. Помимо этого, Groovy на тот момент был весьма модным языком, так что классический hype driven development имел место быть.
Для Android плагин вышел в 2013, но быстро приобрел популярность уже в 2014 году, после чего про Ant практически полностью забыли.
Material Design
![](https://habrastorage.org/getpro/habr/upload_files/245/92e/0ee/24592e0eedc6ad75d80db233c2db6316.jpeg)
Уже мало кто помнит, но до Material Design был HOLO, который был сосредоточен на минимализме и выглядел примерно так:
![](https://habrastorage.org/getpro/habr/upload_files/d0c/007/ddf/d0c007ddf10681b592b08d94909e7e70.jpg)
В те времена очень часто можно было услышать что-то типа «вот Apple заботится о разработчиках и пользователях, сделали нормальный дизайн дефолтных компонентов, а на Android все выглядит в стиле сайтов 90х». И в какой-то степени это было правдой. До Material Design стили были максимально простыми и некрасивыми. Разумеется, в этом были и технические причины — темные цвета способствовали чуть более долгой работе аккумулятора, а отсутствие наворотов положительно сказывалось на скорости рендеринга элементов. Material Design появился, когда телефоны уже худо-бедно стали справляться с большей нагрузкой. Хотя первое время некоторые элементы Material Design, например, тени, могли провоцировать сокращение фрэймов. Выглядел Material Design тогда так:
![](https://habrastorage.org/getpro/habr/upload_files/6cc/9d3/47d/6cc9d347dade67b699c1fdd5e182c122.jpg)
RxJava
![](https://habrastorage.org/getpro/habr/upload_files/8e1/5cf/c44/8e15cfc441f3a1d572c9a478f195a2f7.png)
Асинхронных запросов год к году меньше не становилось. Дошло до того, что в некоторых компаниях умудрились столкнуться с ограничением GCD в 64 треда на iOS. Учитывая, что дефолтные механизмы, такие как AsyncTasks, Threads, Loaders были довольно ущербными в итоге пришли концепции реактивного программирования и ее Java имплементации — RxJava.
На тот момент RxJava, по сути, решала все проблемы. Она удобно выстраивала последовательные запросы, параллельные запросы, легко делала цепочку из нескольких вызовов и модификаций между ними. Но иногда даже относительно простые вещи выглядели нечитаемо, например:
public static <A, B> Observable<CombinedResult<A, B>> combineObservablesParallel(Observable<A> observable1, Observable<B> observable2) {
return observable1.flatMap(new Function<A, ObservableSource<CombinedResult<A,B>>>() {
@Override
public ObservableSource<CombinedResult<A,B>> apply(A a) throws Exception {
return Observable.just(a).zipWith(observable2, new BiFunction<A, B, CombinedResult<A, B>>() {
@Override
public CombinedResult<A, B> apply(A a, B b) throws Exception {
return new CombinedResult<>(a,b);
}
});
}
});
}
Этот кусок со Stackoverflow просто объединял результат 2 observables в 1. Да, сейчас он бы точно выглядел иначе, но тогда это было так.
Первая проблема RxJava была очевидной — порог входа был довольно высок.
Вторая проблема была не лучше — некоторые разработчики, познав всю суть RxJava, умудрялись использовать ее примерно везде. Как в пословице: «Когда научился пользоваться молотком — все превращается в гвоздь». Лично наблюдал, как один разработчик написал код мессенджера на RxJava так, что последовательные вызовы занимали метр-полтора, а разнообразие и количество операторов было таким, что позавидовал бы любой приличный колл-центр.
Так или иначе, эпоха RxJava прошла, и основная причина этому — излишняя для популярной библиотеки сложность.
Volley
![](https://habrastorage.org/getpro/habr/upload_files/3a3/2fb/75a/3a32fb75a1e650dfcf2bdb8e936bb936.jpg)
Библиотека Volley, на мой взгляд, была вечным, всегда вторым номером после Retrofit. Да, это было точно лучше, чем HttpUrlConnection, но до Retrofit не дотягивала. Основных причин две: проблемы с производительностью тяжелых запросов и не самый удачный API. В нем не было ни поддержки работы с RxJava и корутинами, ни удобства написания сложных запросов, а в какой-то момент большинство запросов стали именно такими.
Retrofit
![](https://habrastorage.org/getpro/habr/upload_files/d1f/736/eb7/d1f736eb77d2469c38fb603523b5730a.jpg)
Основным инструментом для работы с сетевыми запросами как был, так и остается Retrofit. Появился он еще в далеком 2013 году, но популярность приобрел спустя несколько лет и стал стандартом индустрии. Причины популярности — OkHTTP под капотом, который также стал стандартом. Благодаря ему Retrofit успешно справлялся с сетевыми запросами любого объема, а также в целом был удобный и простой API с парсингом сразу в POJO, поддержкой RxJava и корутин.
Архитектуры MVP / MVVM
![](https://habrastorage.org/getpro/habr/upload_files/ece/1a5/d74/ece1a5d74c18975fe1c86730e219c7da.jpg)
![](https://habrastorage.org/getpro/habr/upload_files/185/201/8e8/1852018e8923931c7163805c3dc8e8fc.jpg)
После роста размера приложений стало очевидно, что MVC крайне печально справляется с задачей построения расширяемой архитектуры. Так что с 2015 комьюнити стало искать новые варианты. Сначала был Model View Presenter с Mosby, а потом с Moxy. Доклады про архитектуру заполонили все конференции, и вплоть до 2018 года включительно можно было прийти и в 100500 раз послушать, как кто-то переехал на новую архитектуру в %company_name%
.
В 2017 году на Google IO появились Google Architecture Components с LiveData и ViewModel. Проблем и возни с ними было значительно меньше, чем с MVP. И все радостно переехали на Model View ViewModel.
Dagger 2
![](https://habrastorage.org/getpro/habr/upload_files/6ea/1d8/dad/6ea1d8dad071917d01942bdc1fc056d7.jpg)
Dagger 2 вышел еще в далеком 2015 году и довольно быстро завоевал популярность, став стандартом на многие годы. Да, порог входа был довольно высоким. Да, кодогенерация проекта отжирала все больше времени сборки. Но все это с лихвой компенсировалось проверкой корректности графа во время компиляции и хорошим уровнем экспертизы в комьюнити. Периодически появлялись альтернативы — сначала Toothpick, а с приходом Kotlin еще Koin и Kodein, но чем-то настолько же массовым, они так и не стали.
Kotlin
![](https://habrastorage.org/getpro/habr/upload_files/e75/06f/b62/e7506fb62ba1569d650c5b682cea8053.jpg)
Kotlin, вообще говоря, появился раньше, в 2011, но полноценно использовать под Android его стали с 2015-2016. Google IO 2017 года только закрепил его статус, сделав третьим официально поддерживаемым языком на Android (после Java и С++). А в 2019 году Google даже объявили Kotlin-first подход. Причины всем понятны — Kotlin удобнее, короче, гораздо быстрее развивается, тогда как Java погрязла в поддержке обратной совместимости легаси проектов и довольно консервативном комьюнити.
По большей части Android разработчики массово перешли на Kotlin с 2017. Однако в «кровавом энтерпрайзе» процесс был медленнее — сначала оценивали риски, потом вырабатывали процесс обучения сотрудников. Кое-где даже надо было сдать экзамен, чтобы разрешили писать на Kotlin. Так или иначе, к настоящему времени абсолютное большинство Android проектов использует именно этот язык.
Современность 2018 - 2025
Coroutines + Flow
![](https://habrastorage.org/getpro/habr/upload_files/004/f2e/ca1/004f2eca11e288ae03eb7f478dc06339.jpg)
Сами по себе корутины появились еще в 2017, но съезд с RxJava был затруднен из-за отсутствия всех необходимых операторов. Однако чуть позже, с появлением Flow, все эти проблемы сошли на нет.
Плюсы миграции были всем очевидны.
Во-первых, с корутинами и Flow было легче работать, был меньше порог входа.
Во-вторых, из-за kotlin-first подхода в Google многие системные вызовы стали из коробки поддерживать корутины. Да и сами корутины, по сути, были частью языка.
Примерно с 2020 началась массовая миграция с RxJava на Coroutines и с тех пор корутины стали нерушимым стандартом.
Gradle Kotlin DSL
![](https://habrastorage.org/getpro/habr/upload_files/3c7/720/e97/3c7720e975c0f467266e1883209b8894.jpg)
До 2018 года в Gradle безраздельно властвовал Groovy. Да, можно было какие-то части написать на Java, например, плагины, но это было скорее редкостью и исключением из правил.
Я думаю, поддержка Kotlin DSL в Gradle — это самый недооцененный пункт из всех, что здесь есть. Посудите сами, если раньше разработчик, залезая в build.gradle, наблюдал там Groovy, а также полное отсутствие мотивации лишний раз копаться в премудростях незнакомого для себя языка, то сейчас там Kotlin — тот же самый язык, на котором пишется код. Это важно, так как разработка не становится проще, а ситуация, в которой работа типичного Android разработчика с Gradle строилась по принципу карго-культа (что нагуглил по теме, то и скопировал, не задумываясь, как оно вообще работает), наконец-то сходит на нет.
Jetpack Compose
![](https://habrastorage.org/getpro/habr/upload_files/c36/57a/2e5/c3657a2e580fe09abd7f8ba9e975436f.jpg)
Анонсированный буквально за месяц до SwiftUI (Совпадение? Не думаю!) в мае 2019 года Jetpack Compose доехал до состояния production ready только в 2021 году. Однако простота внедрения и разработки, а также поддержка древних версий Android сделали свое дело, и внедрение пошло довольно быстро, в первую очередь за счет новых проектов в стартапах и аутсорсе, а также прогрессивных крупных компаний.
Массовая миграция на Compose сейчас все еще продолжается. На рынке довольно много кандидатов, которые либо никогда не работали с нативной версткой, либо уже просто не хотят работать с нативной версткой. Вопросы по Jetpack Compose уже вовсю попадают в технические собеседования, так что в ближайшие пару лет он утвердится как безальтернативный стандарт индустрии.
Архитектура MVI
![](https://habrastorage.org/getpro/habr/upload_files/d63/f5d/430/d63f5d43063f243b5f9eae23231a9f6d.jpg)
Однонаправленная архитектура Model View Intent по факту появилась еще в 2016, но массового применения не нашла. Она была довольно сложной в применении, с довольно узкими кейсами эффективного использования. В основном использование ограничивалось сложными экранами вроде чатов, где довольно много обновления UI, а также много источников данных, хотя некоторые компании вроде Badoo строили на MVI всё приложение целиком.
С выходом Jetpack Compose архитектура MVI приобрела чуть больше смысла. Хотя бы потому, что сущность State уже в любом случае существовала в явном виде. В целом из-за все более сложных экранов и все большего количества состояний на них архитектура в данный момент имеет все больше смысла.
Итоги
Сейчас Android разработка стабилизировалась. Это уже далеко не молодая область, в которой часто меняются подходы к разработке, часто выходят новые фреймворки и библиотеки, меняющие всё. Сами посудите — с 2014 по 2018 год изменений было кратно больше, чем за предыдущие 7 лет. В какой-то мере можно сказать, что мы пришли к стагнации — имеющиеся технологии как-то развиваются, но ничего принципиально нового не происходит. Чтоб не пропустить новые тренды и не утонуть в рутине покраски кнопок, приходите на AppsConf 2025. Вместе посмотрим, все ли настолько плохо как кажется, и сможет ли AI что-то изменить в текущей парадигме.
dsoastro
Самое неприятное изменение - это Гугл, закручивающий гайки и постоянно что-то меняющий. Желание программировать на его Андройде пропало окончательно
FirsofMaxim
А вечные alpha/beta в jetpack либах? Или стала например stable и через несколько релизов - deprecated.