Many Android developers ask: “What do I need to know to become good Android developer?”. Sometimes they phrase it a bit differently, but, either way, it boils down to the fact that developers look for a list of useful skills to learn.
I think this question is legit. Android is big and dynamic ecosystem, and you can spend weeks learning about specific tools or concepts, only to discover that either they aren’t that important, or shouldn’t be used at all. Therefore, in this post, I’ll share my list of skills that Android developers should have in a hope that it’ll help you to concentrate your efforts on the important stuff.
Evidently, the more experience you have as an Android developer, the more you’ll be expected to know. However, some concepts and ideas are too advanced and require too much preliminary knowledge to grasp when you don’t have much experience.
Therefore, I can’t just list all the skills I’d expect expert Android developers to know because it would be overwhelming for newer developers. Instead, I’ll organize the recommended skills according to different experience levels. However, keep in mind that it’s not an exact science and treat the below experience levels just as general ballpark figures.
You Don’t Know How to Write a Simple App
If you only consider becoming Android developer and haven’t written your first simple application yet, then it’s a bit too early for you to read this post. It’s intended for either professional developers, or people who already made some progress towards becoming one.
However, I’m not going to leave you empty-handed and I’ve got an advice for you too. In this post I summarized the steps that you need to take to become a professional Android developer in the shortest period of time. So, read that article, and come back here once you get some hands-on experience.
In the rest of this post I’ll assume that you’ve already wrote a simple Android app and put its source code on GitHub.
Android Developers With 0-2 Years of Experience
Android is complex framework that has very steep learning curve. Part of that complexity is essential to native mobile development in general, but another part is accidental complexity associated with Android’s quirks.
When you start your path in the world of professional Android development, your main goal, aside from delivering working software, should be to learn Android framework itself. Forget about languages, architectures, fancy libraries and buzz-words. Instead, concentrate on the core concepts and explore them in depth.
Specifically, I recommend giving as much attention as you can to the following topics.
Android Memory Management and Process Death
One of the most complex aspects of Android development is making your application work reliably and conveniently in the face of Low Memory Killer. Long story short: the entire process of your app can be destroyed the moment users switch to another app. However, when users open your app at a later time, they expect it to behave as though process death never happened.
I won’t go into more details about memory management here, but you can find a very detailed discussion of this topic in this article.
If I’d need to name the main source of complexity and bugs in Android applications, I’d immediately scream “lifecycles” (then cover my face and cry).
Application, Activity, Fragment, Service, BroadcastReceiver, ContentProvider and, probably, more core components of Android framework, have complex and unique lifecycles. And if that’s not enough, Google constantly rolls out new libraries and frameworks that have complex and unique lifecycles too. Loader, ViewModel and LiveData immediately come to mind in this context.
One very interesting thing about lifecycles is that I’ve never seen a definition of the term “lifecycle”. We all use it, but how do we know what it means? As far as I can tell, at some point you simply construct an intuitive image of what lifecycle is in your head, and then roll with it. I myself thought and wrote about lifecycles quite a bit, so I’ll give it a shot and try to define this term now.
Lifecycle of a component is an abstract Final State Machine. Abstract here means that the states of this state machine are predefined, as well as the conditions under which it transitions between them, but it’s still incomplete and you should fill in the missing parts to make it fully-functional. These missing parts correspond to a subset of component’s method, which we call “lifecycle callbacks”. In addition to the FSM itself, there are implicit limitations and constraints on what you can do in specific lifecycle callbacks. Some of these limitations are reflected in the documentation, but some of them aren’t.
How complex can lifecycle become? Well, this diagram shows the lifecycles of Activities and Fragments (it’s incomplete and already a bit outdated). It’s scary, I know, but don’t be too stressed about that because most Android developers don’t understand it either. In fact, even googlers who build Android don’t understand it. For example, when they released LifecycleOwner framework, they introduced a serious bug in context of Fragment’s lifecycle and had to hack around it with ViewLifecycleOwner at a later stage.
Now, while you don’t need to understand Android lifecycles completely, you do need to understand them in sufficient details. Otherwise, your code will become a mess and you’ll plant very serious and tricky bugs there. I did my best to describe practical approaches to Activity lifecycle here and to Fragment lifecycle here, so you can read these articles if you aren’t sure when to use onStart() versus onResume().
Once you get your head around the basics of lifecycles, read this outstanding article about 10 “cardinal sins” of Android development by Gabor Varadi. These “sins” are mistakes that most Android developers do. Not surprisingly, most of them are related to lifecycles.
By the way, questions about lifecycles are very common on job interviews, so that’s another good reason to learn them properly.
At the foundation of every Android application lie one or more Context objects.
Just like with lifecycles, it’s difficult to explain what Context is because Contexts are God Classes that have too many responsibilities and know too much. Therefore, it’s simply impossible to describe them in one or two sentences. Nevertheless, it’s important to understand the responsibilities of Context class and what’s the difference between different Contexts.
User Interface (UI) Thread Responsiveness
Each Android application has a special thread called UI thread. This thread is responsible for drawing application’s user interface on device’s screen. If you mistreat this thread, or overload it with work, your application might become janky and unresponsive. In extreme cases, mistakes with UI thread can make your app stuck (or, at least, to appear as such).
So, you need to understand the mechanics behind UI thread and treat it with respect. In this video I explain the basics of multithreading in Android, including the details and the potential pitfalls related to UI thread.
Android framework isn’t exactly “clean code”, to say the least. It’s littered with God Classes having thousands of lines of code in them, and forces us to extend many of these classes in our apps. The end result is that you’ll have all the incentives to write big classes that do lots of things at once. And that’s indeed what happens in many cases. The usual suspects are Application, Activities, Fragments and Services.
However, even though it’s very common, this approach is not good for the long-term maintainability of your app and your own sanity. Therefore, I recommend being constantly aware of this problem and looking for opportunities to decompose your logic into standalone classes.
Now, to be honest, I don’t think that new developers can really do much about Android architectural and design limitations. It takes a bit more experience to be able to see where you can extract meaningful classes in your app that properly encapsulate functional logic. However, even though I wouldn’t expect a new developer to write truly decomposed code, you can still make some progress and, at least, avoid the worst outcomes (like Activities having 5000+ lines of code in them).
As a starting point, you can try to decompose Context-related functionalities as described in this article.
Android Developers With 2-4 Years of Experience
At this point in your career you’re an experienced Android developer. You should have a good understanding of Android framework and be able to implement any non-specialized feature in a reasonable time, even if it involves some research and learning. What’s next for you?
In my opinion, now, that you’re comfortable with Android framework, it’s time to develop higher-level skills. These are general software development skills which aren’t necessarily limited to Android world. Specifically, I recommend looking into the following topics.
Dependency Injection (DI) is an architectural pattern which embodies Separation of Concerns principle. The main goal of DI is to separate two major concerns in your application: the core functionality of the application, and the interconnection between the components which implement that core functionality.
In some sense, codebases which implement proper DI are similar to personal computers: DI infrastructure acts like a motherboard, while functional components act like CPU, memory, peripherals, etc. Once DI is integrated into your codebase, it allows you to plug in new functional components which can then easily reuse functionality of other components, and other components can easily reuse the functionality of the new component. Now, be careful not to take this analogy too far, but it does reflect the core idea behind DI pretty well in my opinion.
Unfortunately, the topic of Dependency Injection is surrounded with many misconceptions which create distraction and noise. Therefore, if you want to learn DI, you can start by reading this article where I analyzed the most widespread DI “myths”.
Decoupling of User Interface Logic
The architecture of Android framework itself promotes and encourages tight coupling between user interface logic (UI logic) and the rest of your application. That’s how pretty much all new Android developers structure their code. This coupling manifests itself as big classes which “draw” app’s UI and also handle networking, multithreading, integration with device, contain business rules, etc.
In my experience, this kind of coupling of UI logic to the rest of the code is the main reason for long-term maintainability problems in Android applications. Sooner or later, code written this way becomes very hard to understand and even small changes in functionality lead to cascade of unexpected side effects.
Separation of UI logic from the rest of your app is the main goal behind Model-View-<something> architectural patterns. Model-View-Controller (MVC), Model-View-Presenter (MVP), Model-View-ViewModel (MVVM) belong to this family, alongside many others. When I want to refer to all these patterns in aggregate (basically refer to the entire family of patterns), I call them MVx.
One thing to keep in mind when you learn about MVx is that these aren’t architectures, but architectural patterns. Furthermore, these architectural patterns apply only to presentation logic inside your apps. Therefore, you don’t get “good” architecture in your app just by adopting some MVx. It takes much more effort than that.
Experienced Android developers should know the main concepts of multithreading in Android, and understand how they affect their apps. You might think to yourself now: “I’m proficient in AsyncTask/RxJava/Coroutines/etc., so I’m alright”, but it’s not what I mean. Using a multithreading framework is not the same as understanding multithreading.
For example, many experienced Android developers believe that usage of AsyncTask automatically leads to memory leaks. This idea is even reflected in the official AsyncTask documentation and default lint rules inside AndroidStudio. Since it’s so common and widespread, this idea must necessarily be correct, right? Unfortunately, it’s wrong. I won’t go into details here, but you can read a very extensive discussion of AsyncTask in this article.
To understand multithreading, in my opinion, is to be able to write correct concurrent code using any multithreading framework, or even bare Thread class. To achieve this goal, you need to know much more than just the API of your favorite multithreading library. Libraries are great, but if you don’t understand multithreading at the more fundamental level, it’s only a matter of time before you plant multithreading bugs into your apps.
If you want to learn Android multithreading, you can start with this video. It contains structured explanation of the fundamentals that I’d expect all experienced Android developers to know.
In my experience, most Android projects still don’t use any kind of automated testing. Among the ones that do, most automation is often handled by QA folks using tools like Appium. It’s sad that it’s the state of our industry and this issue dates back to the origins of Android and the fact that Google ignored automated testing story for third-party apps for a very long time.
Nevertheless, developers who have experience with either unit testing or UI testing are in very high demand. Even if you interview at a company which doesn’t have any automated tests, if you state that you can unit test your code , it’ll give you bonus points. The contrary is also true -iIf you interview at a company which makes extensive use of automated testing, not having that skill might put you at disadvantage compared to other candidates.
Therefore, while not mandatory, I think it’s a good idea for an experienced Android developer to learn the basics of automated testing. I, personally, prefer unit testing and don’t do much UI testing, but many other developers do the opposite. So, just choose whatever technique looks more interesting to you and give it a try.
Android Developers With 4+ Years of Experience
If you have this amount of experience with Android, it’s time to obtain some “meta” skills, as well as specialize in specific areas. In my opinion, the following skills are very desirable for expert developers.
If you haven’t already, it’s time to realize that pretty much every important technical decision you’ll ever make is a trade-off. Sometimes the trade-offs are evident and immediate, but it’s not always the case. Usually, the bigger the scope of a decision, the more trade-offs will be involved which are more abstract and more distant, in addition to the evident and immediate ones.
At this level of experience, you’re expected to be able to at least identify the trade-offs. It would be absolutely great if you could evaluate them and provide useful recommendations, but it’s much more complex and involved skill. Usually, if you can identify the trade-offs, you can then evaluate them in a discussion with other developers, managers or even staff from other departments. Therefore, the ability to identify the trade-offs is sufficient in most cases.
Now, what do I mean by “identify the trade-offs” exactly? It’s really hard to specify, to be honest. However, I can provide several statements that I heard as counter-examples and explain why they don’t fit what I’d expect expert developers to do.
“We should migrate to RxJava instead of AsyncTask because the latter leads to memory leaks”: as I mentioned above, AsyncTask doesn’t automatically lead to memory leaks, so this statement is simply incorrect. Developer who said that didn’t investigate the topic of multithreading in enough details. In addition, there is no mention of downsides of RxJava. At the very least, a steep learning curve for every developer on the project should’ve been stated.
“We should write unit tests for all new code and set a specific long-term coverage goal to ensure sufficient quality”: this statement contains several presuppositions. First, it’s impossible to start unit testing “now”. At the very least, developers need to invest time into learning this technique. It would also be wise to make sure that you’ve got CI that actually executes the tests you’re going to write. Then it’s impossible to write unit tests for all the code because there are always some untestable pieces. Lastly, reaching specific coverage goals doesn’t automatically lead to any kind of “quality”. In this case, developer didn’t understand unit testing at all, so they couldn’t identify any of the many trade-offs involved in adoption of unit testing on a project.
I can go on and on, but, hopefully, you’ve got the general idea. If you need to make an important decision and you don’t see a complex net of trade-offs, chances are that you haven’t performed all the required due diligence yet. Some of these trade-offs might even fall outside the technical scope, so you need to think about the impact of your decisions on other departments as well.
What technical skills do expert developers have that distinguish them from other developers? Is it just the net amount of concepts that you’re familiar with? I don’t think so. In my opinion, the main distinction in technical context is the depth of the knowledge.
As an expert developer, you should have one or more specializations. These are the areas that you understand in great details, much better than an average developer out there. You should be investing ongoing effort to stay updated about developments in your areas of specialization, so you should rarely be surprised by new tools and techniques. In addition, you should analyze the trade-offs associated with concepts within your area of specialization, even if you don’t use any of them in any of your projects.
Now, specialization is hard to achieve. It’s not something you pick from blog posts. It’s not even something you learn from reading the best books and completing the best courses. All of that can help, of course, but the only way to specialize in something is to be actively involved in it and gain a lot of experience.
I really like this statement by the great physicist Niels Bohr:
An expert is a man who has made all the mistakes which can be made, in a narrow field.
Now, ladies, don’t take offense. Bohr lived in different times, but you can be sure that he’d say “person” instead of “man” today.
What are the areas one can specialize in? Well, practically anything. Really, there is almost no area of software development too small to become specialization target. Some ideas from the top of my head:
- User interfaces
- Build systems
- Offline work
- Continuous Integration
- Project management
- Business domain knowledge
and many, many more.
Note that some of the specializations I listed above fall outside strictly technical domain. That’s right: you can specialize in whatever it is that you like, as long as it’s something that brings value to your employer.
I would say that expert Android developers should have at least 2-3 specializations. For example, mine are: architecture, unit testing, concurrency, dependency injection. I think I’ll also feel comfortable adding legacy codebase refactoring to this list soon.
So, if you’re an Android developer with 4+ years of experience, what are your areas of specialization?
Well, that’s the list of skills I’d recommend you to acquire as a professional Android developer.
One interesting fact you might’ve noticed is that, despite its title, nothing in this article is specific to 2020. I could write this article a year or two ago and it would be as relevant as it is today. In my opinion, that’s only reasonable because the fundamental and important concepts don’t change that often.
Now, if you’re curious about the latest developments in Android ecosystem, you can read this post where I summarized the state of native Android development at the end of 2019. However, I’d like to repeat that if you really want to become outstanding Android developer, concentrate on fundamental and important stuff.
As usual, thanks for reading. You can leave comments and ask questions below.