Quite a lot of time has passed since 2007, when Android became available for developers. Almost 18 years have passed since then. During this time, almost everything has changed: hardware has become more powerful, the Internet faster, IDEs smarter, and projects more complex. I suggest looking back and seeing what exactly has changed for developers since then.
Early Android 2007 - 2014
Ant build system
The first thing that would catch the eye of a modern developer is the absence of Gradle for assembly. Ant was used instead . At that time, one could even think that XML driven development was used for Android. All the layout in XML, the build script in XML, strings and resources in XML, and even saved settings (SharedPreferences) under the hood are all the same XML. Some of the XML usage, of course, has survived to this day, but we still managed to get rid of some of it, thanks to that.
Using Ant looked something like this: the project had a build.xml, and all the build tasks (called targets in Ant) had to be written in it:
<?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>
In general, it was, of course, functional, but very verbose. At the same time, it was possible to write your own, even complex targets, and all the basic targets for assembling an Android application were included “in the box”.
IDE - Eclipse and NetBeans
Of course, we didn’t come to a modern development environment right away. Before Android Studio, based on Intellij IDEA, all development for Android (as well as for Java, by the way) was done either in Eclipse or NetBeans. Both IDEs can still work quite successfully with Android projects, of course, provided that they are developed in Java — Kotlin plugins are no longer supported. The difference between them was about the same as between Coca Cola and Pepsi — a matter of taste. NetBeans provided a slightly more user-friendly and uncluttered interface, Eclipse had more plugins. The downsides of these IDEs are the lack of good tools like smart refactoring, a code analyzer, and direct Google support.
Fun Fact
The first versions of Intellij IDEA and Eclipse appeared in 2001. It took Intellij IDEA 7 years, not without the help of Google support, to overtake Eclipse in popularity for Android development. And 15 years to overtake Eclipse in popularity in Java.
RoboGuice
RoboGuice was, in fact, the first popular framework for Dependency Injection before Dagger and even complied with JSR-330 in some places. It worked with View, resources and custom classes. But it looked extremely strange:
@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 );
}
}
Yes, it saved some lines of code, but only due to code generation. It turned out that for small projects it added savings on matches - saving 1-2 lines for each entity, which is quite insignificant. But for large projects RoboGuice added minutes of cold assembly. It’s scary to think what would happen now in the case of projects consisting of many hundreds of screens. In general, by 2013 the project effectively ceased to be supported, and soon ceased to be used.
ActionBarSherlock
In ancient times, when Android 4 did not exist yet, it was quite difficult to make a decent Action Bar. In general, ActionBarSherlock solved this problem and at the same time used the native ActionBar in Android 4, so at some point it was used almost everywhere.
AsyncTask
AsyncTask was part of the standard SDK, but a truly legendary part. The main problem was that due to the abundance of inexperienced developers, the dominance of Async Task in all sorts of courses and guides on Android development, as well as the lack of good architectural practices for separating layers (hello Clean Architecture), Async Tasks themselves were created, like everything else - right in the Activity code. This led to the creation of anonymous inner classes with an implicit reference to Activity and further memory leaks or even application crashes. For example, in the case of screen rotation. It looked like this:
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");
}
}
As soon as the screen was turned over, textView ceased to exist, but the Async Task itself continued to work and when trying to change the text, it immediately brought the entire application to its knees.
The scale of the madness was so massive that for a long time this case was included in the interview questions for Android developers. Fortunately, with 30 APIs already Deprecated.
EventBus
Due to the lack of adequate, relatively simple and convenient UI update mechanisms, the EventBus library was born. The concept was as simple as a cork:
It was only necessary to register the reception of the event:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
// Do something
}
Register message reception:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
And anywhere you can pull its sending:
EventBus.getDefault().post(new MessageEvent());
Convenient? Yes, quite. At that moment, it even seemed like a breath of fresh air. But then problems started. As the projects grew, it soon turned out that the number of such events could be in the tens and hundreds. They could fly in from different parts of the application and lead to bugs that were not so easy to detect. Testing this was also not very convenient. As a result, the use of this library was quickly discontinued, and in the community it began to be automatically associated with noodles in the code.
MVC architecture
If we don’t take into account the projects with God Object Activity, which were responsible for almost everything in general, then the main architecture was MVC. In terms of layering, it looked like this:
Model - logic, data, etc.
View — XML with markup
Controller - Activity / Fragment
Obviously, with this approach, Activity was overloaded with responsibility and at the same time, due to the tight binding to the Android framework, it complicated testing with unit tests.
The era of turbulent changes 2014 - 2018
Android Studio
Android Studio appeared in 2014 and is still actively used. Yes, the first versions were so crooked and slanted that after a few days of use I had to switch to Eclipse. However, six months after the release, the problems were fixed, and the position of the main IDE for Android development became unshakable.
Gradle
The first version of Gradle appeared back in 2009. Groovy was chosen as the main language, since it is, on the one hand, tied to the JVM, and on the other hand, it has dynamic typing, which is convenient for scripts and quite understandable for Java developers. This was especially important, considering that it was more difficult for an ordinary developer to work with Ant and Maven. In addition, Groovy was a very fashionable language at that time, so the classic hype driven development took place.
For Android, the plugin was released in 2013, but quickly gained popularity in 2014, after which Ant was almost completely forgotten.
Material Design
Few people remember, but before Material Design there was HOLO, which focused on minimalism and looked something like this:
At that time, you could often hear something like “Apple cares about developers and users, they made a normal design for default components, and on Android everything looks like 90s websites.” And to some extent, this was true. Before Material Design, styles were extremely simple and ugly. Of course, there were technical reasons for this — dark colors contributed to slightly longer battery life, and the lack of bells and whistles had a positive effect on the speed of rendering elements. Material Design appeared when phones were already more or less able to cope with a greater load. Although at first, some elements of Material Design, such as shadows, could provoke a reduction in frames. Material Design looked like this back then:
RxJava
Asynchronous requests did not decrease year after year. It got to the point that some companies managed to encounter the GCD limit of 64 threads on iOS. Considering that default mechanisms such as AsyncTasks, Threads, Loaders were quite flawed, the concepts of reactive programming and its Java implementation — RxJava — eventually came into being.
At that time, RxJava essentially solved all the problems. It conveniently built sequential requests, parallel requests, easily made a chain of several calls and modifications between them. But sometimes even relatively simple things looked unreadable, for example:
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);
}
});
}
});
}
This Stackoverflow piece simply concatenated the result of 2 observables into 1. Yes, it would definitely look different now, but that’s how it was back then.
The first problem with RxJava was obvious — the entry threshold was quite high.
The second problem was no better — some developers, having learned the essence of RxJava, managed to use it almost everywhere. As the proverb says: “When you learn to use a hammer, everything turns into a nail.” I personally observed how one developer wrote the code of a messenger in RxJava so that consecutive calls took up a meter and a half, and the variety and number of operators was such that any decent call center would envy.
One way or another, the era of RxJava has passed, and the main reason for this is that it is too complex for a popular library.
Volley
The Volley library, in my opinion, was eternal, always number two after Retrofit. Yes, it was definitely better than HttpUrlConnection, but it did not reach Retrofit. There are two main reasons: problems with the performance of heavy queries and not the best API. It did not support working with RxJava and coroutines, nor the convenience of writing complex queries, and at some point most queries became exactly like that.
Retrofit
The main tool for working with network requests was and remains Retrofit. It appeared back in 2013, but gained popularity a few years later and became the industry standard. The reasons for its popularity are OkHTTP under the hood, which also became the standard. Thanks to it, Retrofit successfully handled network requests of any volume, and in general there was a convenient and simple API with parsing directly into POJO, support for RxJava and coroutines.
MVP/MVVM architectures
As the size of applications grew, it became obvious that MVC was extremely poor at building an extensible architecture. So since 2015, the community began looking for new options. First, there was Model View Presenter with Mosby , and then with Moxy . Architecture talks filled all conferences, and up until 2018 inclusive, you could come and listen for the 100,500th time how someone moved to a new architecture in %company_name%
.
In 2017, Google Architecture Components with LiveData and ViewModel appeared on Google IO. There were significantly fewer problems and fuss with them than with MVP. And everyone happily moved to Model View ViewModel.
Dagger 2
Dagger 2 was released back in 2015 and quickly gained popularity, becoming the standard for many years. Yes, the entry threshold was quite high. Yes, the project’s code generation was eating up more and more build time. But all this was more than compensated by the graph correctness check during compilation and a good level of expertise in the community. Alternatives appeared periodically - first Toothpick, and with the advent of Kotlin, also Koin and Kodein, but they never became something as widespread.
Kotlin
Kotlin, generally speaking, appeared earlier, in 2011, but it began to be fully used on Android in 2015-2016. Google IO in 2017 only cemented its status, making it the third officially supported language on Android (after Java and C++). And in 2019, Google even announced a Kotlin-first approach. The reasons are clear to everyone - Kotlin is more convenient, in short, it develops much faster, while Java is bogged down in supporting backward compatibility of legacy projects and a rather conservative community.
For the most part, Android developers have switched to Kotlin en masse since 2017. However, in the “bloody enterprise” the process was slower - first they assessed the risks, then developed a process for training employees. In some places, you even had to pass an exam to be allowed to write in Kotlin. One way or another, by now the absolute majority of Android projects use this language.
Modernity 2018 - 2025
Coroutines + Flow
Coroutines themselves appeared back in 2017, but the transition from RxJava was difficult due to the lack of all the necessary operators. However, a little later, with the advent of Flow, all these problems disappeared.
The benefits of migration were obvious to everyone.
Firstly, it was easier to work with coroutines and Flow, there was a lower entry threshold.
Secondly, because of the kotlin-first approach at Google, many system calls began to support coroutines out of the box. And coroutines themselves were essentially part of the language.
Around 2020, a massive migration from RxJava to Coroutines began and since then, coroutines have become an unbreakable standard.
Gradle Kotlin DSL
Until 2018, Groovy reigned supreme in Gradle. Yes, some parts could be written in Java, for example, plugins, but this was rather a rarity and an exception to the rule.
I think Kotlin DSL support in Gradle is the most underrated point of all that is here. Judge for yourself, if before a developer, getting into build.gradle, saw Groovy there, as well as a complete lack of motivation to dig into the intricacies of an unfamiliar language, now Kotlin is the same language in which the code is written. This is important, since development is not getting easier, and the situation in which the work of a typical Android developer with Gradle was built on the principle of cargo cult (whatever he googled on the topic, he copied, without thinking about how it works at all) is finally fading away.
Jetpack Compose
Announced literally a month before SwiftUI (Coincidence? I don’t think so!) in May 2019, Jetpack Compose reached production ready status only in 2021. However, the ease of implementation and development, as well as support for ancient versions of Android did their job, and implementation went quite quickly, primarily due to new projects in startups and outsourcing, as well as progressive large companies.
The mass migration to Compose is still ongoing. There are quite a few candidates on the market who have either never worked with native layout or simply do not want to work with native layout. Jetpack Compose questions are already popping up in technical interviews, so in the next couple of years it will establish itself as the industry standard without alternatives.
MVI architecture
The one-way Model View Intent architecture actually appeared back in 2016, but it didn’t find widespread use. It was quite complex to use, with fairly narrow cases of effective use. Basically, its use was limited to complex screens like chats, where there are quite a lot of UI updates, as well as many data sources, although some companies like Badoo built their entire application on MVI.
With the release of Jetpack Compose, the MVI architecture made a little more sense. If only because the State entity already existed explicitly anyway. Overall, with increasingly complex screens and more and more states on them, the architecture makes more and more sense now.
Results
Android development has now stabilized. This is no longer a young field, where approaches to development often change, new frameworks and libraries are often released that change everything. Judge for yourself - from 2014 to 2018, there were many times more changes than in the previous 7 years. To some extent, we can say that we have come to stagnation - existing technologies are somehow developing, but nothing fundamentally new is happening. In order not to miss new trends and not drown in the routine of painting buttons, come to AppsConf 2025. Together we will see if everything is as bad as it seems, and whether AI can change anything in the current paradigm.