Android Job Interview – Take-Home Test Cheat Sheet

Learn the steps needed in a typical Android take home test. Giving you the tools and knowledge you need to ace this part of any Android interview process.

Android Job Interview – Take-Home Test Cheat Sheet

This blog post will explain the steps needed in a typical Android take home test. Giving you the tools and knowledge you need to ace this part of any Android interview process.

First up, if you want some background on the Android job interview process, and some tips of what is expected in a take-home exercise. Please read my other blog Proven Prepping for Android Interviews. This has an intro section on take-home exercises.

The Android take home test (also know as a practice app, coding challenge, tech assessment, take-home exercise), is typically done near the start of the interview process. The idea being it can gate keep the interview pipeline ensuring only “serious” candidates are funnelled in and that subset then use up the time of the interviewers/reviewers.

The intention of this interview step is to prove you are capable of Android programming and can demonstrate your day to day abilities whilst having some knowledge of all the moving parts in an end to end Android system. It’s flexible enough to create multiple discussion points and wide enough that individuals can add their own “personal preference/area of expertise” on top of the standard successful submission.

Recommendation #1 Use the suggested practices from developer.android.com

You may have learnt patterns and processes from previous companies, this is great knowledge to have and forms part of your skills and expertise as an individual. You can leverage this when discussing advantages, disadvantages and alternatives.

However for the interview process and in the Android general population – it is best to learn and understand the best practices and recommendations from developer.android.com documentation. Learning what is the latest and greatest by the official source is always good for your own learning.

Doing so will give you a submission that is most likely to match your interviewers expectations. Interviewers do not have enough time to properly understand your unique patterns (even if they are superior). Also if you where collaborating in a team, using official best practices lowers the learning curve for anyone else wanting to contribute.

In 2023 what is Google recommending:

All of the above have official documentation (linked) and official training exercises for you to get started and dive into using them. This is also a good reason that you can mention when asked why you used them, again lowering the learning curve for anyone collaborating.

Getting Started

Some companies will give you a template or basic application as a starting point, others will ask you to create an app from scratch. In both scenario’s it is your prerogative to take a look at the Gradle version and dependencies included and ensure they are up-to-date. Don’t think that because they gave you a certain version of a dependency they expect you to use it, it is more likely they have just not had the time to update their template themselves. (Even when you create an app from an Android Studio template the dependencies are out of date…)

Recommendation #2 Update Dependencies

Use the latest version of Gradle, use the latest version of Kotlin, use the latest version of all Jetpack libraries. (You can even get away with using alpha versions of libraries, though do this consciously and be prepared to have the conversation that in a production application this may not be viable.) Obviously don’t use versions with known major bugs or compatibility issues.

Using the latest dependencies allows you to showcase the latest best practices and recommendations of the community but more importantly allows you to follow the latest recommendations from developer.android.com. This is key to your submission having a high chance of being successful.

When you have updated the dependencies once, you can copy and paste your changes every-time you need to do this, so there may be some initial pain in understanding what you need to bump, but do it once, then keep a small note for yourself on this, and copy paste from then on. (This also helps in a live coding exercise, you can quickly grab the block of dependencies you need, and mention “this is what I typically work against”).

Obeying the Rubric

The typical ask for a take home test, is something along the lines of the below:

“Create an application that loads data from endpoint X, displays that data in a list, any item in the list can be touched to show a further details screen. Bonus points for animations.”

This is the minimum you would get for the rubric, sometimes companies go into more detail, explaining:

“It should support offline mode, it should handle configuration changes, it should show an error if there is no data, it should have day/night mode.”

You should do ALL of those non-functional requirements on every app regardless of if they mention them.

Ensure you add a README.md to your project, paste the rubric in that file. Break the rubric down into bullet points and write next to each bullet when you have completed that part.

You can also use this file to discuss anything you didn’t do, or reasons behind choices or mention extra things you did. You can then copy paste this files content in the email going to the recruiter when you submit (ensuring all readers get an option to read your copy).

As a summary a typical rubric breaks down to:

  • Pull data from an endpoint
  • Store that data locally
  • Show that data in a list when online
  • Still show that data in a list when offline
  • Animate something (the images in the list loading)
  • Do not pull data on a config change (rotation)
  • Have an empty state for the list
  • Navigate to list item details when a list item is clicked
  • Show item details (avoiding network request if possible)
  • Ensure day/night mode works

Rubric to Architecture

Let’s cover what an architecture for this app would look like and the main points needed to cover all Android development bases:

Arguably optional but sometimes expected so best to do them:

Attempting to put this into a conversational explanation:

You want to create an application that pulls data from the backend, stores this data for cached/offline use (allowing the app to use less data and less battery power).

The pulled data is stored in an app database (allowing local refetching of the data and for navigation to pass database row id references instead of heavy data objects or more network calls). Abstract all this from the frontend that is displaying the data using the repository pattern (architecture pattern, decouple your system parts, abstracting complexity).

The frontend will use an MVVM or MVI UI architecture whichever you are most comfortable with. Using compose & MVVM/MVI for UI allows us to write minimal code and keep our UI separate from our business logic.

Supporting material3 and the latest compose dependencies will give us consistent theming and day/night mode out the box.

Recommendation #3 The Repository Pattern

Due to the nature of this take home test, there is not much space for impactful architectural choices. However you get one shot when including the repository pattern. Ensure you state the reason for including the pattern and its advantages.

the repository will serve as a single source of truth for the app’s data, and abstract the source of the data (network, cache, etc.) [Decoupling your system for easier testing and keeping data access in one place.]

https://developer.android.com/
  • Use a NetworkDataSource class to fetch data from the internet (your back end).
    • Use Retrofit for type safe REST http calls
  • Use a LocalDataSource class to fetch data from your database.
    • Use Room as an abstraction over SQLite, for compile time SQL validation and annotations reducing database boilerplate code
  • Your repository has logic to decide if it fetches and saves data to and from the Network and Local datasources. Typically you will pull data from the Network when online and save it to the Local for later offline access, or in a more complex case, first attempt to load the data from the Local source whilst asking the Network for its latest data and load that if/when it arrives & is newer than we already had.
  • The repository is potentially doing long running work and reading/writing from the network or disk, therefore in typical Android fashion you should be doing it on a background thread. Use coroutines for lightweight threading with built in cancel support. A CoroutineDispatcher passed to your repository will help you with threading as well as give you enough control for testing.
  • As hinted previously, your repository can return data more than once. For instance returning locally cached data and then updating from the network and returning fresh data. In coroutines, a flow is a type that can emit multiple values sequentially, as opposed to suspend functions that return only a single value. Perfect for this job. Leverage flow’s in your repository.
  • Your repository helps you support offline mode. Catch network issues & errors and at that point if local data is available return it from your repository.

Recommendation #4 MVVM / MVI

The UI layer is the pipeline that converts application data changes to a form that the UI can present and then displays it.

https://developer.android.com

Decide if you are doing MVVM, MVI or another pattern. Understand the differences and any potential advantages of one of the other. Implement it consistently for the list and the details pages.

  • MVVM & MVI are examples of unidirectional data flow. MVP is bidirectional. Unidirectional data flow is less error prone, gives you more control & is easier to debug.
  • MVI typically has a single observable vs MVVM which has multiple observables. A single observable can allow the code to be simpler to follow, although multiple observables can sometimes allow for more flexibility.
  • Separate your data models used for the domain logic & those used for the UI. This separation allows flexibility when future changes are requested. The UI and domain logic can evolve independently. This means the object you parse from an API is not the same object the UI renders.
  • Leverage Android Viewmodel. Its principal advantage is that it caches state and persists it through configuration changes.
  • Use Flow for your observable states. A flow is conceptually a stream of data that can be computed asynchronously. Following on from the repository recommendation, this is consistent.

Recommendation #5 JetPack Compose

  • Use LazyColumn for your ListView. So much boiler plate code removed! items() is used to draw each item of the list and item() can be used for headers and footers (such as a pagination indicator).
  • Android’s Jetpack library has the method collectAsStateWithLifecycle for you to use in Compose. This allows your app to be lifecycle aware and save app resources when not needed, such as when the app is in the background. (Further reading)
  • Use UI models. Separate your data models. (see recommendation #4)
  • Navigation. The Navigation Compose library is available, giving you a navigator and best practices for navigating between the list and details screens.
  • Animations. Most compose api’s now have support for animations. For example, it can be as simple as crossfade(true) in Coil’s ImageLoader
  • Theming (night mode). In the AndroidStudio compose templates, you will be generated a Theme.kt. This gives you a dark and light theme for free. If you are starting from scratch, ensure you use these templates, otherwise copy in a Theme class. Mention this feature in your done tasks as an added extra if not in the rubric already.

Recommendation #6 Simple DI

Dependency injection (DI) is a technique widely used in programming and well suited to Android development. By following the principles of DI, you lay the groundwork for good app architecture.

Implementing dependency injection provides you with the following advantages:

  • Reusability of code
  • Ease of refactoring
  • Ease of testing
developer.android.com
  • Step 1 (which everyone does anyway): invert your dependencies. Have your high level classes not aware of the details of the low level classes. i.e. pass collaborators through constructors. This is even possible with the Android ViewModel.
  • Use Hilt. Each Android module should have a .di. package, inside that package you have a XModule.kt class. Declaring your @Module. Tie them all together with @HiltAndroidApp. Job done, nothing fancy – just a DI framework.
  • Use Hilt singletons. You can use @Singleton to have your object be a Singleton. This is quite good for things like your Retrofit/OkHttp instance to ensure every user shares the same cache.

Recommendation #7 Simple Modularisation

Modularisation is an abstraction technique to keep unrelated parts separated, to keep systems simple, concepts easier to deal with, and to ensure multiple developers can work together without stepping on each other (too much).

Obviously you are the only developer working on the code base. However imagine you are putting in a framework for all the work to come, and want to set out the way it will work going forward.

  • A layered separation can be the simplest to achieve in a test exercise. Split your folders into: app, features, subsystems. You will only have 1 app so that’s your app module. Inside the features folder, have modules for the “list feature” and the “details feature”, try to use words from the domain such as “Pokedex” or “explorer”. The subsystems folder would then have modules inside that correspond to shared underlying concepts, such as: network, database, logging.
  • Use Android modules only. Whilst you may have practiced having Java only modules when possible for very reasonable reasons, it’s just not worth the time in a test scenario. You could add comments mentioning this in the relevant build Gradle file.
  • You will likely need to share UI elements (such as Theming). Create a “shared-ui” module in your ‘features’ folder, every other feature module will depend on this module.

In this simple modularisation scenario. The features layer is all about the frontend and how the feature is interacted with by the user. Any business logic to do with this feature should reside here. The repository will be here. The subsystems layer is about components used to interact with the peripherals of your environment, such as api’s, databases. Its typically shared components used by multiple features. In all my interviews – nobody asked me to justify my modularisation, they seemed happy enough that they could see multiple modules.

Recommendation #8 Simple Testing

  • You can unit test the viewmodel. You would want to test the interactions. However this is one of the places I typically save time. Make the test file XViewModelTests.kt and write a comment in it stating you would test scenario’s here.
  • Add tests for your XRepository, use all the real collaborator classes you can (for NetworkDataSource etc) and mock or fake the boundary of your code (this gives you more code coverage for less tests). You want to test the different data loading scenario’s, such as when online/offline with/without cached data.
  • Reminder; its ok to comment where further tests could go if you want to save time

Conclusion

Understanding the basic elements of a simple Android architecture is the key to successful take home tests. Getting to grips with fetching data, local caching, the repository pattern, MVVM/MVI, Compose, Coroutines & Flow will give you consistent successful results.

Remembering your Android quirks (such as activity rotation etc) and understanding how & why your architecture successfully supports & deals with these is the final piece needed to be able to confidently discuss and advocate for the application you have just built.

This architecture once learnt, can be reused for most take home tests, adapted for in-person pairing sessions, and also be a solid start of any Android application you wish to make yourself.

I hope this breakdown gives you some food for thought for how to prep for the take-home interview and wish you luck on your Android interviewing journey!

If you would like to ask me any questions about “Android Job Interview – Take-home Test cheat sheet” or discuss about any upcoming interview you have, you can find me on:
Twitter @Blundell_apps
BlueSky @Blundell

Further Consideration

The advice given here is specifically for the Android take home interview process. Do not take this advice as any way to directly create a production application. You are showcasing your Android development capabilities and whilst this could also be the way a production app is created, you need to be aware of the difference.

Differences such as:

  • the need to support a lower minSDK
  • legacy code that already exists
  • the target market and their data plans / processor power
  • existing contracts with vendors
  • the skill level of the current team

Having these in mind when discussing your code choices is very important, you want a strong opinion about what you have done but hold it weakly in the face of counter arguments as previously mentioned.

One thought on “Android Job Interview – Take-Home Test Cheat Sheet

  1. Your insights have given me a new perspective on this subject. I can’t wait to learn more about it.

Leave a Reply

Your email address will not be published. Required fields are marked *