Ideas Engineered for Tomorrow
We Engineer Services & Solutions for Your Business Needs
Consulting Services Hire Book Consulting

Hire Android Developers in India

Kotlin is the easy part. The job is making one binary behave the same on a two-year-old budget phone with an aggressive battery manager as it does on the reviewer's Pixel. This page is the working brief for that hire: what to test, what to ignore, and where offshore genuinely costs you something.

Jump to Screening

Companies that set out to hire Android developers in India usually describe the role in terms of features. Build the onboarding flow, add the payment screen, integrate the SDK. All of that is real work, and none of it is where the money goes. The money goes into the gap between the phone your engineer holds and the phones your customers hold, because on Android that gap is enormous and nobody controls it.

One platform, dozens of manufacturers, a decade of live OS versions, and vendor layers that quietly change how background work, notifications and app termination behave. Your code is the same everywhere. The runtime is not. An engineer who has only shipped to Pixels and recent Samsungs has been working with the difficulty turned down, and you will discover that around week six, in a crash cluster from a country you did not know you had users in.

So the sections below are organised around the things that actually go wrong: fragmentation as a budget line rather than a talking point, the Kotlin and Compose migration you are probably halfway through, six screening areas that map to real production failures, the background execution rules that make your alarm fire late on purpose, and how a Play Console release differs from every other release you have run. There is also a section on when hiring native Android is the wrong answer, because sometimes it is.

Why Does “Works on My Pixel” Tell You Nothing?

This is the structural difference between Android and every other platform you might be hiring for, and it is the one that shows up in your budget rather than in your architecture diagram.

Fragmentation has three axes, and only one of them gets discussed

The first axis is the API level. Google supports a long tail of releases and your users sit across many of them, which means the code path a candidate learned last year may simply not exist on a third of your install base. Every capability discussion turns into a version discussion: this permission model arrived here, that storage behaviour changed there, this notification requirement applies only above a certain level. A competent engineer keeps a mental map of which behaviours are gated by the device's OS version and which are gated by your app's target level, and those are two different things that get confused constantly.

The second axis is hardware. Screen sizes from small phones to tablets to foldables that change size while your Activity is alive, RAM from half a gigabyte on entry-level devices to more than a laptop, storage speed, thermal behaviour, camera pipelines that differ per vendor, and CPUs where a mid-range chip from four years ago is still a current phone in half your markets. Memory is the one that bites hardest: the system kills your process to reclaim RAM, and on a low-memory device it does so far more often, which turns process death from a theoretical edge case into your users' normal experience.

The third axis is the one people skip, and it is the expensive one. Manufacturers ship their own layer on top of AOSP, and that layer changes runtime behaviour in ways the public SDK does not describe.

OEM skins are behavioural forks, not themes

It is tempting to treat One UI, MIUI, ColorOS, Funtouch and the rest as skins, meaning colours and icons. They are not. They modify permission dialogs, notification grouping and priority, default launcher behaviour, deep link handling, split-screen rules, autostart policy and, worst of all, when your process gets killed. There is a community-run site, dontkillmyapp.com, whose entire purpose is documenting which manufacturer breaks background work in which way and which toggle a user has to hunt down to stop it. That such a site has to exist is the clearest statement of the problem I can offer you.

What this means for hiring is concrete. Ask a candidate what they do differently for devices where the vendor kills backgrounded apps aggressively. A weak answer talks about foreground services as though declaring one solves it. A strong answer accepts that some of this is unwinnable in code, and describes the mitigations that actually help: make the work resumable so a kill costs progress rather than correctness, use the server to re-trigger rather than trusting a local schedule, detect the affected vendor and surface an in-app explanation with a deep link into the relevant settings screen, and instrument completion rates per manufacturer so you can see the problem instead of arguing about it.

The test matrix you can actually afford

Nobody tests on everything. The point is to choose deliberately rather than by which phones the team happens to own. Pull your own analytics and rank devices by install base, then by revenue, then by crash concentration. Those three lists rarely agree, and the disagreement is the interesting part: the device that generates the most crashes is frequently not the device that generates the most money, and knowing which one you are optimising for is a product decision your engineer should be asking you about.

Practically, a small physical set covers most of it. One recent flagship, one mid-range device that is two or three years old, one low-RAM device, one tablet or foldable if you support large screens, and one device from whichever manufacturer dominates your largest market. Everything beyond that goes to a cloud device farm. Firebase Test Lab runs your instrumented tests and a crawler on real hardware, and Google Play's pre-launch report does something similar automatically for every build you upload to a testing track, which is free coverage most teams never look at. Deeper matrix work belongs with a dedicated function; our mobile testing services in India page covers how a real device matrix is run and reported.

What fragmentation costs, stated plainly

Budget for it as a recurring percentage of engineering time rather than a one-off hardening sprint. Every feature that touches storage, permissions, notifications, background execution, the camera or the file picker needs verification on more than one vendor, and the annual OS release adds a compatibility pass whether or not you planned one. Teams that treat this as overhead to be eliminated end up shipping features that work for the subset of users who happen to look like the development team. Teams that treat it as the job hire differently and estimate more honestly.

What an Android Engineer Actually Does All Week

Job specs list frameworks. Calendars tell the truth. On a live Android product, this is where the hours go.

Turning API responses into state that survives

Model the payload, decode it, put it somewhere that outlives a rotation and ideally outlives the process, then render loading, empty, error and offline. The Android-specific twist is that your screen can be destroyed and rebuilt at any moment for reasons the user never sees, so state ownership is a design decision rather than a detail. Getting it wrong produces bugs that only appear on someone else's phone.

Chasing jank to its source

A dropped frame is a budget overrun on the main thread. Finding it means recording a trace on a real device rather than guessing, reading where the time went during the stutter, and separating a slow first draw from a slow scroll from a slow startup, because those three have different causes and different fixes. Then proving the number moved.

Arguing with Gradle

Build variants, product flavours, dependency conflicts, annotation processing, minification rules and CI runtimes. It is unglamorous and it eats real days, particularly on multi-module codebases where a badly placed dependency turns a one-line change into a full rebuild. Engineers who have modularised a large app before are worth disproportionately more here than their interview performance suggests.

Reading crashes and ANRs that nobody can reproduce

Group by root cause rather than by stack trace, check whether the cluster is concentrated in one OS version or one manufacturer, and reproduce before fixing. ANRs are the ones teams neglect, because they surface as user reports about the app hanging rather than as clean crash reports, and their causes sit in main-thread work that looked harmless in review.

Keeping the release train moving

Version codes, signed bundles, release notes, screenshots for the form factors the listing requires, the data safety declaration, the testing tracks, then a staged rollout that somebody watches. On a healthy team this is automated and dull. On an unhealthy one it lives in one person's Android Studio, which is a business risk rather than a preference.

Absorbing the annual platform change

Every year brings a new release, new behaviour changes that apply once you raise your target level, and a deadline for raising it. Somebody has to read the behaviour change list, test against the developer preview, and fix what broke before the requirement bites. This is recurring, predictable work that belongs on the roadmap rather than in a panic each autumn.

Kotlin, Compose and the View System: What Your Codebase Says About the Hire

Open the repository in front of a candidate before you ask them anything. The first thing they notice, and the first question they ask about it, is worth more than a prepared answer to a prepared question.

A View-system codebase

XML layouts, Activities and Fragments, view binding or the older findViewById, RecyclerView with adapters and view holders, and the Navigation component or a hand-rolled equivalent. This is not legacy in the pejorative sense. It is what the majority of large shipping Android apps still are, it is well understood, and its performance characteristics are known. An engineer fluent here can read a Fragment lifecycle without looking it up, knows why a view holder is recycled and what happens when you forget, and can spot the difference between the Fragment lifecycle and the Fragment's view lifecycle, which is one of the most reliable seniority tells on the platform.

What it implies about the hire: this codebase has accumulated architecture, probably more than one pattern applied at different times, and possibly a screen or two that grew to a couple of thousand lines. That is normal. The person you want has a plan for extracting logic incrementally behind tests, not a proposal to rewrite the screen in Compose because it would be cleaner.

A Compose codebase

Compose is declarative. You describe the interface for a given state and the runtime works out what to redraw. It reached its first stable release in 2021 and it is where Google is putting its UI investment, so a candidate with no Compose exposure at all will hold you back within a year even if your app is entirely XML today.

What to probe is recomposition, because that is where Compose apps go wrong. Ask what causes a composable to recompose and how they would find out that one is recomposing far more often than it should. Ask about state hoisting: which state a composable owns, which is passed in, and which is held somewhere that survives the composition entirely. Ask what happens when they pass an unstable type as a parameter and why that stops the compiler from skipping. Ask how they give items in a lazy list a stable key and what breaks when they do not. And ask whether they have used a baseline profile, because startup and first-scroll performance on mid-range hardware is the single most common complaint about Compose apps and profiles are the standard answer.

One honest caveat worth hearing from a candidate: Compose is not automatically faster than Views, and on very dense scrolling surfaces a well-tuned RecyclerView can still win. Somebody who tells you Compose is faster full stop has read marketing rather than traces.

The half-migrated app, which is most of them

Almost every real Android codebase in 2026 is partway between the two systems, and interop is therefore a genuine skill rather than a curiosity. Compose inside the View world means hosting a composable in a ComposeView placed in an XML layout or created in a Fragment, and getting the composition strategy right so it is disposed when the view goes away. Views inside Compose means wrapping them so that measurement and updates flow correctly. Both directions have sharp edges around sizing, lifecycle mismatch and theme bridging, because your Material theme in XML and your Compose theme are separate things that somebody has to keep in agreement.

Then there is navigation, which is where half-migrated apps hurt most. You end up with the Fragment-based navigation graph and a Compose navigation host in the same app, and a decision to make about which one owns the back stack. There is no elegant answer. There is a decision, made deliberately and written down, or an accident that costs you a week every time somebody adds a screen. Ask a candidate which side they put in charge on their last mixed app and why. If they have never faced it, they have not worked on a migration.

My blunt advice on migration itself: do it screen by screen, driven by which screens you are already changing, and never as a standalone project with its own budget. A rewrite that delivers the same features on a different toolkit is very hard to justify to whoever funds it, and it is the class of project that gets cancelled at sixty percent, leaving you with two half-systems and nobody who remembers the plan.

The Java that is still in there

Google made Kotlin its preferred Android language in 2019, and new APIs, samples and Jetpack libraries have been Kotlin-first ever since. Compose is Kotlin only, because it depends on a compiler plugin. So new code is Kotlin, and that is not really a debate any more.

Old code is another matter. Plenty of production apps still contain substantial Java, and the interop boundary is where the bugs live. Java has no compile-time nullability, so a value crossing into Kotlin as a platform type will happily be null and throw somewhere unrelated to where the mistake was made. Somebody who annotates the Java side first, before writing a line of Kotlin against it, has clearly been bitten by this already. Convert a file when you have a reason to open it anyway. Converting for neatness is how regressions get introduced into code that had been sitting there working for six years.

One more thing to listen for: Kotlin fluency is not Kotlin syntax. Somebody writing Java in Kotlin clothing produces null checks everywhere instead of modelling absence, classes where a sealed hierarchy would remove a whole category of bug, and callbacks where a flow belongs. It reads fine and it ages badly.

The Android Libraries and Tools We Hire Against

No engineer is deep in every one of these, and a list this long is a menu rather than a requirement. Ask a candidate to mark which ones they have shipped with, which they have only prototyped, and which they know by reputation alone. The willingness to draw that line is itself a signal.

Kotlin Java Jetpack Compose View System / XML Coroutines Flow / StateFlow ViewModel SavedStateHandle Hilt / Dagger Room DataStore Retrofit / OkHttp Paging 3 WorkManager Navigation CameraX Media3 / ExoPlayer Coil / Glide Firebase Cloud Messaging Play Integrity R8 / ProGuard Baseline Profiles Macrobenchmark JUnit / MockK / Turbine Espresso / Compose UI Test LeakCanary Perfetto Gradle / KSP Firebase Test Lab Fastlane

What Should You Actually Test in an Android Screening Call?

Six subjects, ten minutes apiece, and not one algorithm puzzle among them. Every subject here corresponds to a bug category that has cost somebody money after a release went out. Take them in the order given: how a candidate handles the first two tells you whether the last four need pressing hard or waving through.

Lifecycle and configuration changes

Start here because it is foundational and because a surprising number of candidates have only memorised the diagram. Ask what happens when the user rotates the phone. The baseline answer is that the Activity is destroyed and recreated by default. The better answer notes that rotation is only one of the triggers, and that switching to dark mode, changing the system font size, changing locale, plugging in a keyboard, or unfolding a foldable all do the same thing.

Then ask about the escape hatch, because everybody eventually meets it. Declaring configuration changes as handled by the Activity stops the recreation, which sounds attractive and is usually a mistake: you have now taken responsibility for updating every resource yourself, and the bugs that follow are subtle ones where the layout is right but a dimension or a string is stale. There are legitimate uses, mostly around media playback and camera sessions. A candidate who reaches for it as a general solution to state loss has treated the symptom.

Finish with the modern answer: a ViewModel survives configuration change because it is scoped above the Activity instance, so the network request in flight is not thrown away and restarted. Ask what else they put there and what they deliberately keep out. Anything holding a reference to a view or an Activity does not belong in a ViewModel, and knowing why leads straight into the memory section below.

Process death and saved state, which is the bug you cannot reproduce

This is my favourite question on the platform because the failure is invisible in normal development and obvious to users. Ask the candidate to explain the difference between a configuration change and process death, and what survives each.

The answer you want: a ViewModel survives a configuration change and does not survive process death, because process death takes the whole process with it. When the user backgrounds your app and the system reclaims memory, your process is killed. When they come back through the recent apps list, Android recreates the task and hands your Activity back the saved instance state Bundle, and everything not in that Bundle is gone. So the search query is empty, the multi-step form has reset to page one, and the user is looking at a screen that has forgotten what they were doing. The correct mechanism is a saved state handle inside the ViewModel, or the saveable variants of remember in Compose, holding the small amount of data needed to reconstruct where the user was. Small is the operative word: the Bundle goes through a transaction with a size limit, and stuffing a list of results into it will throw rather than degrade.

Then ask the question that separates people who know the theory from people who have shipped: how do you test it? The good answer names the developer options toggle that destroys activities as soon as they go into the background, and better still the background process limit setting, which forces the real thing. The best answer mentions that this needs doing on a physical device with the app genuinely backgrounded, because the failure mode is one that never appears on a development machine where the app is always in the foreground and always attached to the debugger. If a candidate has never deliberately induced process death, they have shipped this bug and not known it.

Coroutines and structured concurrency

Ask what structured concurrency buys them over launching work and hoping. The answer should be about scope: a child coroutine cannot outlive its parent scope, so cancelling the scope cancels everything under it, and a screen that goes away takes its in-flight work with it rather than leaking a callback that fires into a dead view.

Then push on cancellation, which is where most people are vague. Cancellation in Kotlin is cooperative. A coroutine sitting in a suspending call from the standard library gets cancelled promptly, but a tight computational loop with no suspension point will keep running to completion after cancellation has been requested, and the candidate should know to check for it explicitly. Ask what happens to code in a finally block during cancellation, and whether a suspending cleanup call inside it will actually run.

The Android-specific follow-up is worth more than either. Ask how they collect a flow in a UI layer. Launching a collection tied to the wrong lifecycle keeps it running while the app is in the background, which means your app is still processing location updates or holding a socket open when the user thinks they have left. The current answer is to restart collection when the lifecycle reaches a started state and stop it when it falls below, and there is a well-known idiom for exposing a hot state flow with a short subscription timeout so that a rotation does not tear down and rebuild the upstream. A candidate who reaches for that idiom unprompted has debugged this in production. Ask also where they choose to move work off the main thread and what belongs on which dispatcher, and listen for whether the answer is a rule they memorised or a decision they make per call.

Memory leaks, and the Context that outlived its screen

Android hands every component a Context, and the difference between the application Context and an Activity Context is the source of a whole family of leaks. An Activity Context is tied to a screen with a view hierarchy attached. Holding it in something that lives longer than the screen keeps the entire hierarchy in memory, and the user experiences that as memory growth on rotation followed by the system killing the app.

Ask where they last leaked one. The shapes are recognisable and hard to invent: a singleton or a companion object holding an Activity Context that was passed in during initialisation, a listener or broadcast receiver registered and never unregistered, a handler posting a delayed message from an inner class that implicitly holds its outer, a thread or timer started in a screen and never stopped, a static reference to a view held for convenience. In Fragments there is a specific and very common one worth naming: a binding reference kept as a property and not cleared when the view is destroyed, because a Fragment can outlive its own view and the binding drags the whole hierarchy along with it.

Then ask how they found it. LeakCanary during development, the memory profiler and a heap dump when they need the retention path, and repeated allocation over a specific user journey rather than staring at a graph. Also worth asking: what they would do about a leak coming out of a third-party SDK, which is a diplomacy problem as much as an engineering one. Note that a leak on Android is not always a crash; often it is a slow degradation that ends in the system terminating the process, which is recorded separately from a crash and therefore missing from the number most teams watch.

Why the list is janky, which is almost always the adapter

Describe a symptom rather than a solution. The feed stutters when scrolled quickly, it is worse on a three-year-old mid-range device, and it looks fine on the team's phones. Ask what they check first. A candidate who immediately starts listing optimisations has failed the question; the right instinct is to record a trace on the affected device and look at what the main thread was doing during the dropped frames.

When they get to causes, the honest answer is that the drawing is rarely the problem. The problem is nearly always in the binding step, and the offenders repeat: an image decoded on the main thread instead of handed to an image loader, a date or currency formatter constructed once per row rather than once, a database read or a preferences read happening inside binding, layout inflation triggered repeatedly because view types churn, an entire list refreshed with a blanket change notification instead of a diffed update so every visible row rebinds, and nested scrolling containers arranged so that recycling is disabled entirely and every row is inflated at once. That last one is worth asking about directly, because it is common and it is invisible in code review.

The Compose equivalent has the same shape and different names: unstable parameters defeating skipping, missing keys so items lose identity, state read at too high a level so a whole screen recomposes when one row changed, and a first scroll that is slow because the code has not been profile-guided. Whichever toolkit your app uses, the signal is method rather than vocabulary. Ask what number they measured, what single change they made, and what the number was afterwards.

Release ownership on Play

Save the closing stretch of the call for this and let it decide any candidate you are considering above mid-level. Have they set up app signing on a project from scratch, and do they understand the difference between the upload key and the signing key that Google holds? Have they run a staged rollout and defined in advance what would make them halt it? Have they had a release rejected or an app pulled for a policy reason, and what did they change? Have they uploaded a mapping file and then read a deobfuscated stack trace from a release build? Have they ever shipped a build that crashed only in release because a minification rule stripped something reflection depended on?

These are yes-or-no questions with stories attached, and the stories contain details nobody invents. It is also the most trustworthy part of assessing somebody at a distance, since a release history leaves evidence and a self-assessment does not.

Why Did the Background Job Never Run on the User's Phone?

Nine times out of ten nothing is broken. The platform is doing exactly what it was designed to do, and the app was written as though it were still 2014. This is the single largest category of Android bug reports that turn out not to be bugs.

Doze, App Standby and the tightening of the screws

Android has spent a decade progressively taking background execution away from apps, because unrestricted background work is what ruins battery life. Doze arrived in Android 6.0 and defers network access, alarms and jobs when a device has been sitting unused, batching them into periodic maintenance windows instead. App Standby Buckets arrived in Android 9 and put each app into a bucket based on how recently and how often the user opens it, with quotas on jobs and alarms that tighten as you fall down the list. An app the user opens daily is treated generously. An app they installed once and forgot is barely allowed to run at all, and there is a restricted bucket at the bottom for the worst offenders.

The consequence for your product is that scheduled work is scheduled in the sense of eventually, not in the sense of at 3am. If your candidate promises exact timing for periodic background work, they are describing an Android that stopped existing years ago. Exact alarms still exist for genuine alarm-clock and calendar-reminder cases, and Google has progressively restricted them behind a dedicated permission and a policy declaration precisely because they were being abused for sync. Using one to poll a server is both a policy risk and a battery complaint waiting to happen.

WorkManager, and what it does and does not promise

WorkManager is the right tool for deferrable work that must eventually happen: uploads, syncs, cache maintenance, log shipping. It persists work across process death and reboot, applies constraints such as network availability or charging state, handles retry with backoff, and gives you unique work so you cannot accidentally queue the same sync twice. That persistence is the real value. A naive implementation loses the work when the process dies, and on a low-memory phone the process dies constantly.

What WorkManager does not give you is a guarantee about when. It respects the same Doze and bucket rules as everything else, because it delegates to the platform scheduler. It has an expedited path for work the user is waiting on, and that path is metered and can be refused. And it cannot save you from a manufacturer that removes your app from its autostart list. If your architecture assumes work will run within a fixed number of minutes on every device, the architecture is the bug.

Foreground services and the type you now have to declare

A foreground service is how you tell the system that work is ongoing and user-visible, backed by a persistent notification the user can see and act on. It is the correct tool for navigation, media playback, active fitness tracking, an in-progress call, or a large upload the user initiated. It is not a general way to keep your app alive.

The rules here have tightened repeatedly. Starting a foreground service from the background was blocked, with a defined list of exemptions, so a broadcast receiver that wakes up and tries to start one will throw rather than work. More recently each foreground service must declare its type in the manifest, hold the matching permission, and in some categories justify the use to Google Play with a description and a video. Users can also see and stop foreground-service apps directly from a system affordance. A candidate who has shipped through these changes will tell you which of their services survived and which had to be redesigned. That is a much better signal than a list of API names.

The manufacturer battery managers nobody warns you about

Above all the documented platform rules sits an undocumented layer. Several major manufacturers add their own process management: apps not on an allow-list get frozen or killed after the screen has been off for a while, autostart after reboot is disabled by default, and alarms and jobs are silently dropped. None of this is in the SDK documentation, none of it is consistent between vendors or between versions of the same vendor's skin, and none of it can be fully worked around in code.

What you can do is honest engineering. Instrument the completion rate of background work and break it down by manufacturer and OS version, so the discussion is about data instead of anecdote. Make the work idempotent and resumable so that being killed halfway costs progress rather than correctness. Detect the vendors known to be aggressive and, at the moment it matters to the user, explain in plain language what to change and deep link into the settings screen rather than describing a path through five menus. And where the timing genuinely matters, stop relying on the device.

Push as the trigger, not as the payload

Firebase Cloud Messaging is the way out of most of this, and it is routinely misused. A high-priority message can wake a device out of Doze, which is exactly what you want for a genuine user-facing event, and it is quota-managed so an app that sends every sync at high priority will find itself deprioritised. Normal-priority messages are deferred and batched, which is fine for anything that can wait.

Two design points a senior engineer will raise unprompted. First, delivery is best effort. Messages can be dropped, collapsed or delayed, so a client that treats a push as the only way it will ever learn about a change will eventually show a stale screen. Treat the push as a hint that says come and look, then have the client fetch. Second, the difference between a notification-style message and a data-only message decides whether your code runs at all when the app is backgrounded, and getting that wrong produces the classic report where notifications work perfectly in development and never trigger the app's logic in production.

Play Console Reality: Rollouts, Vitals and the Target API Treadmill

Google Play lets you move faster than Apple does and holds you to account differently. Understanding the trade is most of what separates a developer who ships on Android from one who writes Android code.

Staged rollout, and what halting actually does

You can release an update to a percentage of users and increase it over days while you watch. This is genuinely the best safety mechanism on either mobile platform, and it is free. Use it for anything that is not a trivial copy change, start small enough that a bad build reaches a survivable number of people, and decide the halt criteria before you press the button rather than while the graph is moving.

Halt criteria should be numbers, agreed in advance, that a person on call can act on without a meeting. A crash rate for the new version above an agreed multiple of the previous version's. An ANR rate above its baseline. A drop in a core conversion event. A spike in one-star reviews or in support tickets mentioning a specific screen. Without written criteria, what happens instead is a debate about whether the increase is significant, conducted while the rollout continues.

Be clear about the limits. Halting stops further distribution, and it does not take the build off phones that already have it. Getting those users onto something safe means shipping a fixed build with a higher version code and rolling that out. The good news, and this is the real Android advantage, is that you can do that in a timeframe measured in hours rather than waiting in a review queue. That difference is why Android teams can afford a slightly more aggressive release cadence than their iOS counterparts, and why the same team should not assume the two platforms need the same release process.

Android vitals, and enforcement that is not a human

Play measures user-perceived crash rate and user-perceived ANR rate from real installs and compares them against published bad-behaviour thresholds. Exceed them and the consequences are not a conversation: your store listing can carry a warning, and your discoverability in search and recommendations can be reduced. There are also technical quality standards that affect whether you are surfaced on tablets, foldables and Chromebooks. The exact thresholds and the exact treatment change over time, so read the current Play Console documentation rather than trusting a figure quoted anywhere, including here.

The practical instruction for a hiring manager is simpler than the policy. Ask a candidate what their crash-free and ANR-free rates were on the last app they owned, per version rather than in aggregate, and how they knew. Aggregate figures flatter you, because a healthy old version props up the average while the version you just shipped quietly gets worse. Ask separately about memory terminations, since a process killed for using too much memory is not recorded as a crash and an app can therefore show excellent crash figures while being force-closed constantly on cheaper devices. Users describe that as the app shutting itself down, and they say so in reviews.

The target API level requirement, and why the number is not printed here

Google Play requires that new apps and updates target a recent API level, and that requirement moves upward on an annual cycle. Apps that fall behind stop being available to new users on newer devices, which is a slow, quiet loss rather than a dramatic removal. I am deliberately not printing the current level or the current deadline, because both change every year and a stale number on a web page is worse than no number. Check the current requirement in the Play Console documentation at the time you plan the work.

The mechanism is what you should hire against. Raising your target level opts your app into that release's behaviour changes, all at once, and some of those will break you. Historically these have included storage access, background execution, permission handling, exact alarm access, notification permission and foreground service declarations. So the annual compliance pass is not a manifest edit; it is a read of the behaviour change list, a set of code changes, and a testing round on devices running that OS version. Teams that treat it as paperwork discover in the last fortnight that a core flow no longer works. Ask a candidate to describe the last target level bump they did and what it broke. If the answer is nothing, they either had a very simple app or they were not the one doing it.

Signing, bundles and the key you must not lose

Publishing on Play means shipping an Android App Bundle rather than an APK, and Play generates the device-specific artifacts. Play App Signing means Google holds the key used to sign what users install, and you hold an upload key used only to authenticate what you send. This is a meaningful safety improvement, because an upload key can be replaced through support if it is compromised, whereas a self-managed signing key that gets lost ends the app permanently: there is no route to publishing an update that users can install over the old one.

The related trap is minification. R8 shrinks and obfuscates your release build, which means release builds can crash in ways debug builds never do, typically where reflection or serialisation depends on names that were renamed. The mapping file produced by each build is what turns a release stack trace back into readable code, and it has to be uploaded and retained per version. Ask what a candidate does when a top crash comes back unreadable. A shrug there means your first serious production incident is diagnosed by guesswork.

Tracks, testing and the review that is lighter than you think

Play stacks its distribution tracks: an internal one for the team, a closed one for an invited group, an open one anybody can join, then production. Each stage is worth using, and the pre-launch report that runs uploaded builds on real hardware is free feedback most teams ignore. Google has also added pre-launch testing requirements for some categories of new developer account, so check the current rules before you assume you can publish on day one.

Review itself is lighter and faster than Apple's, and that is a trap rather than a gift. Enforcement on Play is largely automated and frequently retroactive: policy sweeps can pull down an app that has been live for years, declarations about permissions and data can be audited later, and enforcement can reach the developer account rather than only the app. The worst outcome on Android is not a rejection before launch. It is a suspension after launch, with your users unable to install, while you work out which SDK caused it. That risk is why the dependency inventory work described below is not optional.

Permissions, Scoped Storage and the Data Safety Declaration

Three areas where the platform has changed repeatedly, where the old approach still compiles, and where a candidate's answer tells you exactly how recently they shipped.

Permissions drift with every release

Runtime permissions arrived in Android 6.0 and the model has been refined every year since. Location split into approximate and precise, and background location became a separate grant that the user makes in system settings rather than in your dialog. Bluetooth scanning got its own permissions instead of borrowing location. Notifications became a runtime permission, which caught out every app that had assumed it could simply post one. The system also revokes permissions from apps the user has not opened in a while, so an app can lose access it previously had without anyone doing anything.

The engineering consequence is that every permission has at least three states you must handle, not two: granted, denied, and denied in a way the system will no longer prompt for. That third state is the one that produces support tickets, because the user taps the button, nothing happens, and there is no dialog to explain why. The correct handling is to detect it and send the user into the app's settings page directly. Ask a candidate how they handle a permanently denied permission. It is a small question that reliably separates people who have shipped to real users.

Sequencing is the product half of the same problem. Requesting location, notifications and contacts in the first few seconds of a first launch is the reliable way to have all three refused forever. Ask at the moment the reason is obvious, explain in your own screen before triggering the system dialog so the dialog is a confirmation rather than an ambush, and make sure the app still does something useful for the person who says no.

Scoped storage, and the shortcut that no longer exists

The old model, where an app with a storage permission could read and write anywhere on shared storage, is gone. Apps now write freely to their own directories without any permission, use the media store for images, video and audio, and use the document picker for arbitrary files the user chooses. Broad access to all files is a special permission that requires a justification to Google Play and is refused for most app categories, so an architecture that depends on it is an architecture that will not ship.

The migration pain is real and specific. Code that stored absolute file paths in a database now has to work with content URIs and persisted permission grants. Anything sharing files with another app has to go through a file provider instead of handing over a path. And apps that used the temporary legacy flag to defer the work found it stopped applying once they raised their target level, which brings this straight back to the annual requirement above. If your app handles documents, photos or downloads and has not been through this properly, say so during hiring; it changes which candidate you want.

Data safety, and the SDKs you did not write

Your Play listing carries a declaration of what data the app collects and shares, whether it is linked to the user, and whether it is used for tracking. It must reflect what the app actually does, including what every third-party library in the binary does, and a mismatch is a policy problem rather than a paperwork problem.

This makes dependency inventory a real task with a name attached. Somebody should be able to list every SDK in the release build, say why it is there, when it was last updated, and what it can see. Analytics, advertising, attribution, crash reporting and chat libraries are the usual sources of surprise, and an abandoned SDK that nobody can justify is exactly the kind of thing that turns up in a policy sweep two years after the engineer who added it has left. As for whether your data handling satisfies GDPR, the UK regime, Australian privacy law or a sector rule, that is a question for your own counsel. Your engineer's job is to hand counsel an accurate map of what is collected, where it goes and what is stored on the device. Treat any supplier who offers you a compliance opinion, or claims a certification in passing, with caution.

When Hiring Native Android Is the Wrong Answer

Better to say this now than four months into an engagement. There are common situations where a native Android hire is an expensive answer to a question you have not asked precisely enough.

Screens over an API, on both platforms, with a small team

If the app is lists, forms, detail views, sign-in and a checkout, and you need it on Android and iOS, then hiring natively for each means two engineers, two codebases, two release processes and every bug fixed twice. One cross-platform engineer will usually deliver more product per month, and on that class of app the quality difference is much smaller than platform partisans claim. The threshold question is what proportion of your app is genuinely platform-specific rather than what proportion feels special.

Where Flutter earns its keep, and what it costs

Flutter paints every control itself instead of handing that job to the platform, which is why an app looks identical on both stores and why it can feel a little foreign on Android if nobody tunes it. That is a design cost you can manage. The real costs sit elsewhere: when Android ships a new system behaviour or a new UI convention you wait for the framework rather than adopting it immediately, anything platform-specific means writing a plugin in Kotlin anyway, and system surfaces such as home screen widgets and rich notification handling are native work regardless of what the main app is written in. If your product is mostly content and forms and you want both stores served by one team, that trade is usually worth taking. Our Flutter developers in India page argues the same trade from the other direction.

Where React Native fits, and where the bill arrives

React Native is the strongest choice when you already employ React engineers, because the language, the tooling, the state management and much of the business logic carry across, and that transfer is worth a great deal in practice. Two costs to plan for. Major version upgrades have historically been more work than teams expect, especially with a long tail of community native modules that each have to catch up. And when performance degrades, it usually degrades where JavaScript meets the native layer, which happens to be the one place a JavaScript specialist can see least. You end up buying Android expertise for the hardest problems regardless. If that describes your situation, the React Native developers in India page is the more useful read.

The list that settles the argument

Write down every feature that touches hardware, background execution, a system integration, the notification tray, a home screen widget, or a permission that needs explaining to the user. If the list is short, go cross-platform and hire native later for the specific pieces. If it is long, you were always going to need this hire, and starting cross-platform only defers the cost to the point where it is most expensive to pay. Most products that get large enough end up mixed anyway, and choosing that deliberately at the start is far cheaper than arriving there after two rewrites.

The Seniority Ladder for Android, and What Each Level Can Own

A title tells you what one company decided to call somebody. What transfers between employers is the list of things a person can be handed and left alone with. Read down these four and find the one that matches your situation, because paying for a level you do not need and buying one below what you need are both expensive mistakes.

Junior: writes screens, stays away from the release

Implements a designed screen against a defined API, writes a test, follows the patterns already in the codebase, and fixes a well-described bug. Needs review on anything involving concurrency, lifecycle or persistence. Should never be the sole person able to produce a signed bundle, which is a statement about your exposure and not about their competence. Hire at this level when a senior shares the codebase and the backlog is already broken into well-described tickets.

Mid-level: owns features, wants a second opinion on architecture

Takes a feature from a loose description to something shipped, including error states, offline behaviour and tests. Can record a trace when pointed at a problem. Understands lifecycle well enough not to leak, and coroutines well enough to keep the main thread clear. Usually still learning where Play policy and the annual target level bite. Best value per unit of cost when someone senior is setting direction.

Senior: owns the release, the vitals and the device strategy

Runs the pipeline, holds the signing setup, decides the minimum SDK against your analytics rather than against preference, defines halt criteria, and reads a deobfuscated stack trace to a root cause without a reproduction handed over. Has raised a target level and cleaned up what it broke. Will refuse a change that has no remote switch behind it. If nobody else on your side writes Android, this is the level the situation requires.

Lead: owns the shape of the work itself

Draws module boundaries, settles what gets tested and at which layer, and says what a code review is obliged to catch. Makes the Compose and View boundary a decision rather than an accident. Owns build times, which on a large Android codebase is a productivity lever rather than a nicety. Justified once you have three or more Android engineers, or once the structure of the codebase is what limits how fast anything ships. Under that headcount you are paying coordination overhead for a job a capable senior already covers.

A caution about years. Android has had several large shifts, and an engineer with nine years of experience may have spent seven of them in a Java and View-system codebase that never adopted coroutines, never modularised, and never went near Compose. Put that person on a legacy app and they are worth a great deal; put them on a greenfield one and both of you will be unhappy by month two. So ask what they shipped in the last eighteen months and on which toolkit, rather than how long the title has been on their profile.

Four Positions Companies Arrive In, and What Each One Should Be Briefed As

Each of these is a composite, assembled out of the kinds of request that reach us rather than an account of any named client. Pick whichever of the four sits closest to your own position.

The app that only misbehaves in one country

Your crash and support data cluster around one region, and when you look closer they cluster around two or three manufacturers that barely register in your home market. Notifications arrive hours late or not at all, the overnight sync has silently stopped for a slice of users, and nobody can reproduce any of it on the phones in the office.

This is a fragmentation and background execution engagement rather than a feature one. The first deliverable is measurement: completion rate for background work and notification delivery, broken down by manufacturer, OS version and standby bucket, so that the conversation stops being anecdotal. Then the mitigations, in order of cheapness: move timing-critical triggers to push, make the work resumable, add a vendor-aware in-app explanation that deep links into the right settings screen, and only then consider redesigning the scheduling. Anyone who proposes a foreground service as the whole answer has not read the last few years of platform changes.

The Play Console with a warning on it

Vitals are above threshold, or a policy declaration is out of date, or the target API deadline is close enough that somebody has started forwarding the email. Installs are drifting down and nobody has connected the two.

The work here is triage in a specific order. Read the actual enforcement notice rather than the summary somebody pasted into Slack. Separate crash clusters from ANR clusters, because they have different causes and ANRs are usually main-thread work or a lock nobody has looked at. Confirm mapping files are being uploaded, since half the time the crashes are unreadable and the team has been guessing. Then bump the target level properly: read the behaviour changes, fix what breaks, test on that OS version, and roll out in stages with halt criteria written down. This is a senior engagement of a few weeks, and the outcome is measurable, which makes it one of the easier ones to scope honestly.

The View-system app somebody wants rewritten in Compose

An app that works, a team that has read about Compose, and a proposal to rewrite the UI layer. Sometimes this is right. Usually the honest answer is that a rewrite delivering the same features on a different toolkit is very hard to justify to whoever signs it off, and it is the classic project that gets cancelled partway, leaving two half-systems and nobody who remembers the plan.

What we would rather do is a migration that pays as it goes. Establish the interop boundary and the theme bridge properly, decide once who owns the back stack, and convert only screens that are already being changed for product reasons. Extract logic out of the giant Activities into testable pieces first, since that work is valuable whichever toolkit wins. The engineer for this is a senior who is fluent in both systems and comfortable saying that a particular screen should stay as it is, which is a harder thing to hire for than it sounds.

The white-label app that ships to a hundred customers

One codebase, many branded builds, each with its own package name, assets, feature toggles, endpoints and store listing. The engineering questions are all about the build system: how flavours and variants are organised, how assets and configuration are supplied per brand, how CI produces and signs the whole set without a person clicking anything, and how a change is verified across brands without testing a hundred apps by hand.

This is where a candidate's Gradle experience stops being a footnote. Ask how they would keep build times sane as the variant count grows, how they detect that a change to shared code broke exactly one brand, and how they manage a hundred sets of signing credentials and listings without it becoming one person's private knowledge. It is unglamorous work and it is the difference between a product line that scales and one where every new customer costs a week.

Running an Android Team Out of India: the Honest Version

Timezones are a sum, not a sentiment, so here is the sum. India Standard Time sits five and a half hours in front of UTC and ignores daylight saving completely. That has an odd consequence worth stating: the gap between your office and ours widens and narrows twice a year, entirely because of decisions made in your country, while nothing here changes at all.

The overlap, worked out

Half past nine in the morning here is 04:00 UTC. Half past six in the evening is 13:00 UTC. Push those two moments into your own local clock and the answer drops out with no interpretation required. Below it is done for the cities our clients sit in, and you should check it, because the whole calculation is one subtraction.

Your city Your clock at the start and end of the Indian day Shared desk hours against a 09:00 to 17:00 day
London (BST, UTC+1) 05:00 until 14:00 Five, running from your arrival to early afternoon
London and Dublin (GMT, UTC+0) 04:00 until 13:00 Four, finishing at one o’clock
Berlin and Amsterdam (CEST, UTC+2) 06:00 until 15:00 Six, your whole morning plus part of the afternoon
New York and Toronto (EDT, UTC-4) Midnight until 09:00 None whatsoever; India clocks off as you sit down
New York and Toronto (EST, UTC-5) 23:00 until 08:00 None, plus an hour of dead air behind it
Chicago (CDT, UTC-5) 23:00 until 08:00 None, missed by a full hour
San Francisco and Seattle (PDT, UTC-7) 21:00 until 06:00 None, and the gap runs three hours wide
Sydney (AEST, UTC+10) 14:00 until 23:00 Three, at the tail of your afternoon
Auckland (NZST, UTC+12) 16:00 until 01:00 One, immediately before you leave

Read that honestly. Britain, Ireland, continental Europe and the Gulf get a shared afternoon thrown in for nothing. Australia and New Zealand get a narrow slot at the close of their day, enough for a standup and a decision, not enough for pairing. Neither American coast gets a single minute on an ordinary Indian schedule. A supplier who claims otherwise is either redefining office hours or betting you will not do the subtraction.

What a US overlap costs, in the only currency that matters

Perfectly achievable, and it is a staffing decision rather than a favour. To sit with New York for four hours, the Indian day has to start in the early afternoon and run to about half past ten at night, putting the engineer online from 04:00 to 13:00 Eastern in summer so that you share 09:00 until 13:00. Reaching San Francisco for three hours pushes the same day two hours later again, finishing after midnight in India for a window that closes at noon Pacific. Those are evening and night shifts for the person working them. They change who accepts the role, how long they last, and what the work feels like by month nine.

Our position is to name the shift before anyone starts, agree the window as part of the engagement, and hold it steady. The failure we are trying to avoid is the one where nobody names the schedule, people quietly rotate through it, and the engagement degrades without anyone being able to point at the reason. Continuous coverage is not free either: a genuine follow-the-sun rotation needs several people, deliberate written handover, and more coordination overhead than most products of this size can justify. If somebody offers it as a bundled extra, ask who is awake and what happens when they leave.

The Android-specific problem the distance creates

Here is one that generic offshore advice misses entirely. Your device matrix is physical, and it sits wherever the engineers sit. When a bug reproduces only on a specific manufacturer's phone running a specific OS version, the person holding that phone is in India and the person who can reproduce your customer's exact configuration might be in your office instead. Neither can hand the device across the gap.

The fix is to plan for it rather than improvise. Agree which devices live where, and keep a short list in each location covering the vendors that dominate each market you sell into. Use a cloud device farm for the long tail so that anyone in either timezone can run a build on a specific model without owning it. Record traces and bug reports rather than describing symptoms, because a trace file crosses a timezone and a description does not. And when a bug is device-specific, put a video and a full bug report in the ticket as a matter of routine, since the alternative is a full day lost per round trip of clarifying questions.

Working practices that make the distance survivable

Written first, without exception

Whatever gets decided on a call is written into the ticket before that day ends. Not paperwork for its own sake: it is the only mechanism by which a person who was asleep while you talked can act on the outcome without you scheduling a second call. It also leaves a trail, which matters six months later when somebody asks why a thing was built the way it was.

Small pull requests against your rules

Opened in your repository, under your review standards, sized so a reviewer can hold the whole change in their head. Nobody several time zones away can lean across a desk for a quick clarification, so the written description has to do that job on its own. The classic way remote review collapses is a single enormous branch arriving on the last day of a sprint, when there is neither time nor appetite to read it properly.

A device set drawn from your analytics

Not from what is newest and not from what the team already owns. Rank your install base, your revenue and your crash concentration, then choose against those lists knowingly. Emulators are useful for layout and useless for thermal behaviour, memory pressure, vendor battery managers and real network conditions.

Your Play account, your keys, your repository

The Google Play developer account sits in your company's name, engineers are added with defined roles, and every commit lands in a repository you own. Access gets granted rather than handed back later, so nothing has to be transferred when an engagement ends.

Done includes the rollout

Merging is not the finish line. The work is done once it sits in a build on a testing track, has been exercised on the agreed devices, and has had its crash, ANR and startup numbers compared with the version before it. Settle that definition on day one and you avoid a far worse discussion in month four.

English assessed on writing, not accent

What matters at this distance is whether someone can write an unambiguous ticket update, a pull request description that explains the reasoning, and a question that does not need a follow-up. That is what gets tested. Confident speech paired with woolly writing has sunk more distributed teams than a heavy accent ever did.

For the parts of this that are not specific to Android, meaning vetting, contracting and reporting as they work across every skill we staff, start at the hire developers in India hub instead. If you also need the Apple side of a two-platform product, the iOS developers in India page deals with the very different constraints of shipping through App Review.

Engagement Models for Android Work

Which shape fits depends on whether you already have an Android engineer and whether the work has an ending. We settle commercial terms with you before anybody begins, rather than expect you to infer them from a web page. Get the match wrong and a different Android engineer reaches you inside 48 hours, and you are choosing from more than one candidate rather than being sent a single name to accept or reject.

Embedded engineer

One or more Android engineers joining your board, your standup and your review process, picking up your conventions rather than importing ours. The right shape when you have a continuing roadmap and somebody on your side to set direction, and when you want the working relationship to be with your engineering lead directly.

Work that has an ending

One outcome, with an agreed test for whether it arrived: a target API level bump with the behaviour changes handled, a vitals recovery with a measured target, a background reliability investigation across manufacturers, or a first release to production. Fits when what you want is an outcome instead of a seat filled, and when you can describe what finished looks like.

Maintenance and release ownership

For a live app with no active feature roadmap that still needs somebody to absorb each annual platform change, keep dependencies and SDKs current, watch vitals, triage crashes and ANRs, and push staged rollouts safely. More companies ask us for this than for anything else, and it is the least expensive way to stop a shipped app rotting between funded projects.

Why this is faster than recruiting the same person yourself

Companies rarely come to us because Android capability is scarce. They come because of the calendar. Hiring an Android engineer yourself, here or in your own city, runs through sourcing, screening, a technical round and an offer, and only then do you meet whatever notice period the person still owes the employer they are leaving. That last part is what catches overseas employers out, because notice here is contractual rather than customary, and it is frequently long enough to shift a start date by an entire quarter. Establish that number with any candidate before you build a plan on top of them. A person you settle on in March is very often not at a desk until June, and if your problem is an ANR rate climbing this week or a target API deadline in six weeks, June is not an answer to anything.

We do it the other way around. The engineers are already on our payroll and between projects, so there is no search phase and no leaving date to negotiate with anybody. That is the entire reason names reach you inside 48 hours of your brief and the person you pick can be committing to your repository 7 days later. There is nothing clever behind those two numbers.

None of that substitutes for your own judgement. Interview them properly, put the process death and release ownership questions to them, and turn down anyone who does not convince you. Arriving quickly is worth nothing if the wrong person arrives.

Frequently Asked Questions

Why does our app work on the office test phones and break on a customer's Xiaomi?

Because a Pixel runs close to stock Android and most of the world does not. Manufacturer skins change how background work, notifications, permissions and app killing behave, and those changes are not part of the public SDK. The fix is a device set chosen from your own install-base analytics, plus a cloud device farm for the models you cannot buy.

What should I ask an Android developer to prove they are senior?

Ask what survives a configuration change and what survives process death, and how they tested the difference. Ask where they last leaked an Activity Context. Ask how cancellation reaches a running coroutine. Then ask about the last release they rolled out on Play and what their halt criteria were. Feature work is teachable; release judgement takes a bad launch to acquire.

Is Jetpack Compose experience essential, or is the View system still fine?

Both matter, because most shipping apps contain both. Compose is where new Android UI work is going and a candidate with no exposure to it will slow you down within a year. Somebody who cannot read an XML layout, a RecyclerView adapter or a Fragment lifecycle will stall on the eighty percent of your app that predates the migration.

Can an Android developer in India work our US Eastern hours?

Not on an ordinary Indian day. The clock here sits five and a half hours in front of UTC, every day of the year, so an office day in India has finished by 13:00 UTC, which is nine in the morning for you in summer. Genuine overlap with either American coast means an Indian evening shift, and that is a staffing choice with a price, settled in writing before anybody starts.

Why did our scheduled background sync stop running on real phones?

Probably nothing broke. Doze and App Standby put rarely used apps into buckets with tightening quotas, so jobs and alarms get batched into maintenance windows rather than fired on your schedule. On top of that, several manufacturers add their own battery managers that stop apps outright. Design for eventual execution and let the server push when timing genuinely matters.

How is shipping on Google Play different from shipping on the App Store?

Play lets you move faster and punishes you later. Review is lighter, you can push a staged rollout to a small share of users and halt it the same day, and you control the pace. In exchange, enforcement is automated and retroactive: policy sweeps, Android vitals thresholds and an annually rising target API requirement can all pull a live app down.

Should we hire native Android or go cross-platform instead?

Count the features that touch hardware, background execution, system integration or the notification tray. A short list means Flutter or React Native will get you both platforms for less. A long list means native skill was always in your future, and beginning cross-platform only postpones that bill to the moment it costs most.

Tell us what state your Android app is in

A first release, a vitals problem nobody has read properly, a target level deadline, or background work that has quietly stopped running for a third of your users. Describe what is actually happening and we will come back with the seniority the problem calls for, an honest view of how long it takes, and a straight answer on whether native Android is the right route at all.