Years of Experience: 2 YOE
Current Role: Android Engineer, Startup (SDE-III)
Result: Offer Received
Format: Online (MS Teams) · Duration: 1 hour
The round started with a brief introduction — my career journey so far and my open-source contribution. We then went deeper into my work at my organization: the overall team structure, the size of the Android team, and what a typical day looks like for me.
What is a singleton class? Followed by implementing a basic singleton in Java.
Thread safety of the implementation — how do we ensure the singleton is thread-safe?
Singletons in Kotlin — different approaches:
object declaration
Java-style singleton
synchronized block — we discussed that this is JVM-specific, and since Kotlin is multiplatform, platform-specific locking mechanisms would be needed for KMP targets.
class Database {
private static Database INSTANCE;
private Database() {}
static Database getInstance() {
if (INSTANCE == null) INSTANCE = new Database();
return INSTANCE;
}
}
object Database
How do we measure the performance of an Android app (or any app in general)?
Memory leaks
UI jank
ANR — main thread blocked for more than 5 seconds
Crashes
UX degradation from long-running work — slow DB queries, IO, heavy computation
How do we measure and diagnose these? Using tools like Crashlytics and App Inspection in Android Studio.
What is a memory leak? Detailed discussion with Android-specific examples.
Scenarios that commonly cause leaks, and coding patterns that lead to them.
How to prevent leaks in practice.
AndroidViewModel vs ViewModel — differences and when to use which.
What is a coroutine, and why do we need it? Brief discussion of threads vs coroutines.
Ways of launching a coroutine: runBlocking, launch, async — when to use which.
Scopes: What is a CoroutineScope? What are viewModelScope and lifecycleScope? Within these, we discussed coroutine cancellation, lifecycle awareness, and job cancellation.
async and Deferred: What does deferred mean? When does suspension happen, and when does the deferred block actually start executing?
A few more conceptual questions around coroutines to close out.
suspend fun init() {
val val1 = async { /* ... */ }
val val2 = async { /* ... */ }
// initialize something
val1.await()
val2.await()
}
Format: Offline (Adobe Office) · Platform: HackerRank live session
The round started with brief introductions — myself and the interviewer — along with some small talk about weather and general well-being. I was then asked to take out my laptop, and the interviewer shared a HackerRank live session link.
There were two DSA questions. I was asked to look at Problem 1 first and decide whether I wanted to attempt it or look at Problem 2 and start there. I looked at Problem 1 and decided to attempt it first.
The interviewer set an expectation upfront: brute force approaches would only be discussed, not coded, to save time. Once we arrived at the optimal solution, I could code it up.
Statement: A Nice Array is an array in which removing exactly one element makes the array strictly increasing. You are given a list of positive integers — find the number of elements that need to be removed to make the array a Nice Array.
I first discussed the brute force approach, followed by its time and space complexity.
We then discussed that we could do better, and I proposed the optimal solution.
I wrote pseudo-code on paper and did a dry run on the example input.
The interviewer was satisfied with the approach. We then discussed a few edge cases and follow-ups:
Duplicates — how would my approach change if duplicates were allowed (i.e., strictly increasing vs non-decreasing)?
Already sorted array — the trivial case.
I was then given a choice: code this problem now, or move to the next one and code later. I decided to move to the next question.
Statement: You are given a list of integers representing shops on a street. Each number is the amount I must spend at that shop before moving to the next. You are given n queries of the form [Sᵢ, Capᵢ], where Sᵢ is the starting shop and Capᵢ is the amount I have. For each query, return the farthest shop I can reach.
I initially proposed the brute force approach using linear search per query.
I then arrived at the optimal solution: cummulative sums + binary search per query.
The interviewer was okay with the approach and asked me to code either of the two problems.
I chose to code Problem 2. A few test cases failed — I walked the interviewer through the edge cases where my solution was failing and how I would fix them, but we ran out of time.
Format: Offline (Adobe Office) · Duration: 1 hour
The round started with a basic introduction, after which the interviewer set expectations for the interview. He said he would throw very broad, open questions at me and we'd discuss around them. He expected a certain depth in each concept — once he found the depth he was looking for, we'd immediately move to the next topic. My goal should be to state the facts I know immediately, so he could extract as much information from me as possible.
Started with what is dependency injection and the need for it.
Began with the basic motivation for DI, then circled to the Android-specific need — activities and other components are instantiated by the system, not by constructor calls.
Discussed Dagger and Hilt — how Hilt simplifies things, whether we can use Dagger without Hilt, and what the challenges are.
Discussed scoping of objects — what "scope" means in the context of Hilt.
Problem: Implement a search bar that provides suggestions, with the data sitting on the backend and search firing a network call.
Follow-up discussion:
Caching vs no caching - we steered towards caching.
Debouncing the search input.
Data sources and mediation between the two sources (local cache and backend).
Prefix search - and the data structures suited for it.
Problem: Implement an infinite scroll.
Follow-up discussion:
When to fetch the next page.
Handling dynamic data — e.g., new rows inserted into an already-fetched page.
Fetching approach — page numbers vs cursors.
Storage and mediation — how I would store the data and mediate it to the upper layers.
How I would layer/structure the app — what logic lives where.
Ensuring performance checks and good practices — e.g., network calls on the IO dispatcher.
Repository vs use cases — when to use what.
ViewModel and coroutines — what is viewModelScope, then what is a CoroutineScope. The interviewer expected depth around lifecycle management here; once I answered that, we moved to UI.
LazyColumn vs a normal Column — how performance is affected in each. I discussed recomposition, disposing of composables, and related behavior.
The interview ended a bit earlier than expected. The interviewer said he was done and I could ask anything I wanted. I was a bit confused, since I expected more questions — and I told him so. He said, "let's keep going for a few more minutes," and we discussed some challenging work I've done in my career before ending the interview.
Format: Offline (Adobe Office) · Duration: 1–1.5 hours
This was the final round, with the hiring manager. The interview started with basic introductions — the interviewer introduced himself and asked me to introduce myself and walk through my career experience. During the walkthrough, he asked questions in between — less technical, more around how I approach problems in general and how I manage a team.
Once I was done with my walkthrough, he said we'd do some basic cultural fit questions and then move to open discussion. These were standard hiring manager questions, such as:
Why do you want to join Adobe?
What's the most challenging thing you've done in your career?
As a team lead, how do you ensure alignment when some team members are misaligned?
Describe a scenario where you overcame a hurdle.
Describe a scenario where you solved a really challenging task.
Describe a scenario where you demonstrated teamwork.
Describe a scenario where you demonstrated leadership spirit.
Overall there were 12–15 cultural fit questions along these lines.
I was then given a problem statement to design a "Promo Processor" (my name for it — the interviewer just described the problem).
Context: Multiple promos/dialogs currently appear in the Acrobat Reader app — banners, dialogs, ads, feature promotions. Users complain a lot about this. The idea is to design a promotion processor infrastructure that handles when a promotion should be displayed. Its only job is to tell the UI layer when to display which promotion.
My approach:
Discussed the problem with the interviewer and listed down all the required features.
Discussed the high-level design in brief.
Moved into low-level design and discussed around that.
This round ran over its allocated time. It ended with my questions about the org structure, the project, and when I would be staffed.