Skip to content

Advanced


Explain the Android Runtime (ART) internals and how to optimize it in production

beginner advanced internals android
View Answer

Android Runtime (ART) is the JVM executing app code, with JIT compilation, GC, and instrumentation built in.

In interviews, cover:

  • Compilation modes: Ahead-of-Time (AOT) at install, Just-in-Time (JIT) at runtime, Interpreter for cold code

  • GC behavior: concurrent mark-sweep, generational GC, GC pauses tune-able via heap size

  • Why it matters: slow app startup = bad UX; GC pauses = frame drops; incorrect GC tuning = memory crashes

  • Real tradeoff: larger heap reduces GC frequency but increases pause times; smaller heap = frequent GCs but faster pauses

  • Debug vs release: release uses AOT (faster, deterministic), debug uses Interpreter + JIT (flexible for iteration)

Strong answer tip:

  • Explain when you'd profile ART: startup time bottleneck? Use systrace to see compilation phases; frame drops? Check GC timings

πŸš€ See Full Deep Dive


Discuss Binder IPC design choices and scaling challenges in system_server

intermediate advanced internals binder
View Answer

Binder is Android's cross-process IPC: synchronous by default, uses shared memory, enforces capability-based security.

In interviews, cover:

  • Binder transaction limit: 1 MB buffer per process (shared pool); exceeding it β‰ˆ transaction failure without warning

  • Thread pool sizing: system_server has 32 binder threads; each thread blocks until response; too few = deadlock, too many = memory spike

  • Synchronous calls: Aβ†’Bβ†’C chain can deadlock if threads exhaust; common in framework (WindowManager, ActivityManager)

  • Real scale issue: batch transactions vs individual callsβ€”1000 individual calls = 1000 round trips; batch + parse = 1 round trip

  • Observable symptom: "binder thread exhausted" ANR; fix is either parallelize responses or batch callers

Strong answer tip:

  • Walk through why system_server can deadlock: A waits for B, B waits for A; how you'd detect this in tombstones

πŸš€ See Full Deep Dive


Explain Zygote's role in app startup and memory efficiency via Copy-on-Write

intermediate advanced internals zygote
View Answer

Zygote is a master process forking new app processes, sharing ART state + preloaded classes to speed startup.

In interviews, cover:

  • Preloading: Zygote loads ~4000 classes at boot (android., java., framework); fork copies memory, saves re-parsing + verification

  • COW (Copy-on-Write): forked app pages are initially shared with zygote; only written pages copied, saving memory footprint

  • Startup sequence: Zygote fork β†’ set UID/GID β†’ bindApplication() β†’ onCreate chain; each step measurable

  • Tradeoff: more preloading = faster cold start but larger zygote heap; less preloading = smaller zygote but slower apps

  • Real bottleneck: classes that can't be preloaded (have static state) must be loaded per-app; timing = startup latency

Strong answer tip:

  • Explain why your app's onCreate is slow: did you load large libraries or do I/O? Is it unpre-loadable? How you'd profile with perfetto

πŸš€ See Full Deep Dive


Walk through frame pipelining, triple buffering, and jank diagnosis on RenderThread

senior advanced internals renderthread
View Answer

RenderThread (RT) is a worker thread executing GPU commands decoupled from main thread; vsync synchronizes frame output.

In interviews, cover:

  • Frame pipelining: Main thread prepares frame N+1 while RT renders N; vsync fires every 16.67ms (60Hz), blocking present

  • Triple buffering: 3 buffers cycle to prevent tearing; if frame misses vsync, 16.67ms penalty (jank spike visible)

  • Synchronization: mainβ†’RT hand-off via sync tokens; if main slower than RT can consume, RT sits idle (GPU under-utilized)

  • Tradeoff: reduce redraw area = less GPU work but more CPU overhead computing bounds; full invalidate = GPU full-screen but simple CPU

  • Real bottleneck: RecyclerView item bind on main blocks RT; if bind takes 10ms, RT can't dequeue next work in 16.67ms window

Strong answer tip:

  • Walk through a jank frame: main thread slow due to GC pause β†’ RT sits idle β†’ next frame misses vsync β†’ dropped frame visible

πŸš€ See Full Deep Dive


What factors influence GC tuning decisions in ART and how do you measure them

advanced advanced internals memory
View Answer

ART heap is generational: young gen (fast collections) + old gen (full GC); tuning balances pause time vs memory bloat.

In interviews, cover:

  • Heap sizing: VM max heap in manifest; exceeding = OutOfMemoryError; too small = frequent GC; too large = long pause times

  • Generational GC: young gen holds short-lived objects (quick collections); old gen rare collections but longer pauses

  • Concurrent vs full: concurrent GC pauses ~5-20ms; full marks+sweeps stops world, can pause 100+ms (visible jank)

  • Tradeoff: increasing young gen ratio (more fast GCs) = lower pause time but more CPU; decreasing = fewer GCs but longer pauses

  • Real tuning: apps with bursts of allocation (JSON parsing, view inflation) benefit from larger young gen; steady-state apps not so much

Strong answer tip:

  • Profile with Logcat GC messages: if you see "Explicit concurrent copying" every 1s, young gen is too small; explain sizing strategy

πŸš€ See Full Deep Dive


Describe the AOSP layered architecture from kernel through apps and its constraints

beginner advanced internals aosp
View Answer

AOSP is layered: Linux kernel ← HAL ← Framework (Java services) ← Apps; each layer provides abstractions & isolation.

In interviews, cover:

  • Kernel: device drivers, scheduling, memory management; interface via syscalls

  • HAL: hardware abstraction layer (cameras, audio, sensors); allows ODMs to keep drivers closed-source

  • Framework: system_server with WindowManager, ActivityManager, PackageManager; orchestrates system policies

  • App layer: uses context + manager objects (getSystemService) to access framework

  • Why it matters: adding a feature (e.g., new sensor type) requires HAL module + framework service + app permissions

Strong answer tip:

  • Explain how an app accesses camera: Camera2 API β†’ framework β†’ HAL β†’ kernel driver; which layer encodes permissions?

πŸš€ See Full Deep Dive


Explain how system services enforce lifecycle and manage resource contention across apps

intermediate advanced internals system
View Answer

System services (WindowManager, ActivityManager, etc.) manage device resources & enforce lifecycle across apps via binder.

In interviews, cover:

  • System services: run in system_server process; app-facing APIs (e.g., startActivity) β†’ service impl β†’ enforces policy

  • Lifecycle callbacks: framework intercepts lifecycle (onPause, onDestroy); unwinds cleanly or ANRs if service unresponsive

  • Resource conflicts: two apps request same hardware (camera) β†’ framework arbitrates (focus, permissions); older app loses access

  • Tradeoff: strict lifecycle enforcement = predictable but may denying older apps; lenient = backward-compatible but leaky

  • Real issue: app never calls onDestroy properly β†’ services accumulate β†’ memory leak or resource starvation

Strong answer tip:

  • Explain how ActivityManager enforces lifecycle: if onPause doesn't complete in 5s, ANR; how you'd catch this in profiler

πŸš€ See Full Deep Dive


Discuss input dispatch routing and SurfaceFlinger composition; when do deadlocks occur

intermediate advanced internals input
View Answer

Input dispatch (touch events) is decoupled from rendering (SurfaceFlinger); WindowManager coordinates focus & gesture routing.

In interviews, cover:

  • Touch dispatch: InputManager receives kernel events β†’ WindowManager determines focus-owning window β†’ delivers to app

  • ANRs on input: if app's input handler blocks >5s (e.g., doing I/O in onTouchEvent), system kills it; deadlock risk

  • SurfaceFlinger: compositor combining app surfaces β†’ display buffer via vsync; runs on RT, not main thread

  • Tradeoff: synchronous dispatch = responsive but easy to deadlock; async = resilient but adds perceived latency

  • Real issue: modal dialogs consume all touch events; app behind modal is unresponsive; framework must timeout

Strong answer tip:

  • Walk through slow onTouchEvent: if it blocks, dispatch timeout fires, ANR dialog shown; how you'd detect in crash logs

πŸš€ See Full Deep Dive


Explain Android's defense-in-depth security layers and how privilege escalation exploits work

senior advanced internals android
View Answer

Android's security model is layered: Linux DAC (file permissions) + SELinux MAC (mandatory access control) + app-level permissions + capability dropping.

In interviews, cover:

  • Linux kernel layer: DAC (user/group IDs), process isolation, file permissions; why apps run as separate UIDs

  • SELinux (Mandatory Access Control): type enforcement (TE), role-based access control (RBAC); confines what even root can do

  • Android permission system: declared at install time, checked at runtime; coarse-grained (not a replacement for OS isolation)

  • Privilege separation: system_server, mediaserver, adbd each confined by SEPolicy; privilege escalation exploits chain multiple layers

  • Real example: why a compromised app can't read another app's private dir (UID isolation) AND can't escalate via kernel even if it gains root (SELinux)

Strong answer tip:

  • Explain how you'd investigate a privilege escalation exploit: which layers failed, what SEPolicy rule was missing, how you'd harden it

πŸš€ See Full Deep Dive


What is SEPolicy Type Enforcement and how do you debug sandboxing violations in production

advanced advanced internals sepolicy
View Answer

SEPolicy (SELinux policy) defines which domains (processes/services) can access which resources (files, sockets, devices) on Android.

In interviews, cover:

  • Type Enforcement (TE): every file/process has a type; rules allow/deny (type A can read type B); denials are logged

  • domains vs types: domains are types for processes (e.g., system_server, mediaserver); confines what that service can do

  • attribute grouping: allows policies to say "all untrusted_app* can read shared_libs but not credentials"

  • common sandboxing patterns:

  • untrusted_app: restricted, only sees its own app dir and shared data

  • priv_app: privileged system apps, elevated but still confined

  • system_server: all-powerful in absense of SEPolicy, now divided into modules

  • audit mode vs enforcing: audit logs all denials without blocking; enforcing blocks and logs; gradual rollout prevents breakage

  • real example: how adding a deny rule for a vendor daemon prevents it from reading /data/local/tmp without breaking functionality

Strong answer tip:

  • Walk through a policy rule: allow untrusted_app app_data_file:file {read write}; means untrusted apps can read/write their own data files; explain what blocks them from other paths

πŸš€ See Full Deep Dive


When should you use NDK and how do you measure performance gains vs complexity costs

beginner advanced internals native
View Answer

NDK allows writing performance-critical code in C/C++, interfaced via JNI; requires careful memory + thread management.

In interviews, cover:

  • JNI bridge: Java calls native code via nativeMethod(); native accesses Java heap via JNI env; marshalling has overhead

  • When to use: tight loops (crypto, image processing), leverage system libraries (OpenSSL), real-time requirements

  • When to avoid: simple business logic (extra complexity), frequent calls (marshalling overhead per call), string/array copying

  • Tradeoff: native = 2-10x faster for compute but harder to debug, memory crashes kill process, cross-platform complexity

  • Real issue: native leaking Java objects (missing DeleteLocalRef) β†’ GC can't collect β†’ memory bloat

Strong answer tip:

  • Estimate speedup: if 10% of CPU time in tight Java loop, native gains ~10% overall; if 50%, gains ~50%; understand which bottleneck

πŸš€ See Full Deep Dive


Explain JNI reference management, critical sections, and their GC interaction

intermediate advanced internals jni
View Answer

JNI has hidden costs: method lookup overhead, local/global reference management, GC interaction if referencing Java objects.

In interviews, cover:

  • Local vs global refs: locals valid only in current call scope; globals survive but must be freed or leak; each ref is 4 bytes memory

  • Critical sections: holding a reference to Java array while in native requires pinning (prevents GC relocation); heavyweight

  • GC interaction: if native holds Java refs, GC must scan native frames; large ref tables = slower GC

  • Marshalling cost: copying strings/arrays Javaβ†’native = O(n) per call; reuse buffers when possible

  • Tradeoff: minimize JNI crossings = fewer local-ref pressure but larger native batches; frequent calls = easier to audit but slower

Strong answer tip:

  • Explain why copying large arrays in each JNI call is bad: 1000 calls Γ— 1MB copy = 1GB moved; suggest GetDirectBufferAddress bypass

πŸš€ See Full Deep Dive


Design a background sync strategy balancing battery life, data freshness, and reliability

intermediate advanced internals power
View Answer

Doze and JobScheduler restrict background activity when battery low or screen off; tradeoff is reliability vs battery life.

In interviews, cover:

  • Doze mode: triggered after 1h+ idle; suspends most alarms & wakelocks; battery can last 2-5x longer but apps can't sync

  • JobScheduler: app declares work (network fetch, sync); OS schedules it optimally (batched, plugged in or low battery); adaptive

  • Foreground services: exempt from Doze (as long as notification shown); but users see "X is draining battery" warning

  • Tradeoff: strict Doze = battery longevity but users get stale data; lenient = responsive but battery drains; JobScheduler finds middle

  • Real issue: app waits for background sync but Doze deferred it 8 hours β†’ user perceives data is stale; poor UX

Strong answer tip:

  • Explain your app's sync strategy: if critical, use foreground service; if batch-OK, use JobScheduler; explain why to PM

πŸš€ See Full Deep Dive


Discuss scoped storage constraints and tradeoffs when migrating legacy apps

senior advanced internals storage
View Answer

Android storage spans internal (ext4/F2FS) + external (SD card); scoped storage restricts direct file access for privacy & security.

In interviews, cover:

  • Scoped Storage: app can only access its own directory (/data/data/pkg) + shared media via MediaStore; prevents privacy leaks

  • Filesystems: internal uses ext4 or F2FS (flash-friendly, no journal overhead); choice impacts performance & longevity

  • Tradeoff: scoped storage protects privacy but makes file sharing harder (MediaStore overhead); older targeting allows old perms but security risk

  • Real bottleneck: MediaStore queries are slow if index stale; batching large file ops can timeout or ANR the UI

  • Encryption: full-disk or file-level; full-disk cheaper but all-or-nothing; file-level flexible but slower I/O

Strong answer tip:

  • Explain why scoped storage forces apps to migrate: old apps had blanket READ_EXTERNAL_STORAGE access; now must use MediaStore or SAF

πŸš€ See Full Deep Dive


Analyze connection pooling, DNS caching, and radio state transitions for battery optimization

advanced advanced internals network
View Answer

Network stack handles TCP/IP, DNS, connection pooling; Android adds Doze restrictions, multi-SIM support, and radio state transitions.

In interviews, cover:

  • Connection pooling: HTTP/2 reuses TCP connections; creates new when old closed; Doze can close sockets β†’ connection churn

  • DNS resolution: blocking in many libraries; resolve off main thread or cache heavily; system resolver caches 10s

  • Radio state: cellular radio state transitions (activeβ†’idle) cost battery; batching requests into one window saves 90% radio energy

  • Tradeoff: aggressive connection reuse = faster but risks stale connections; eager new = resilient but connection ramp-up latency

  • Real issue: app makes 100 HTTP requests sequentially on main thread β†’ blocks 5+ seconds β†’ ANR / perceived unresponsiveness

Strong answer tip:

  • Explain radio state: app request triggers radio active state (~3s); each request = ~500ΞΌJ; batching 10 requests = 1 active window, ~50ΞΌJ per request

πŸš€ See Full Deep Dive


Explain the Android boot sequence and init.rc scripting language with failure modes

beginner advanced internals boot
View Answer

Android boot: bootloader β†’ kernel β†’ init (PID 1) β†’ property service + socket listeners β†’ Zygote β†’ system_server β†’ apps.

In interviews, cover:

  • init process: runs init.rc scripts (parsed DSL); spawns system_server, Zygote; manages socket listeners (recovery, property)

  • Property service: key-value system; "ro.build.fingerprint" immutable after boot, others writable; apps can't set ro.* properties

  • init.rc language: service blocks, on events (boot, charger), imports; allows platform customization pre-boot

  • Tradeoff: early script execution = control but complex debugging; late (in app) = simpler but less control

  • Real issue: modified init.rc breaks boot (infinite loop); recovery mode + adb shell only way out

Strong answer tip:

  • Explain why Zygote start happens in init: needs privileges (UIDs), isolation; app process fork inherits only heap (security boundary)

πŸš€ See Full Deep Dive


Compare systrace, Perfetto, and method tracing; when would you use each one

intermediate advanced internals instrumentation
View Answer

Android profiling spans kernel (ftrace) β†’ userspace samplers (Perfetto, systrace) β†’ runtime instrumentation (method tracing).

In interviews, cover:

  • Systrace: kernel + framework events; shows thread state, I/O, GC over time; 64ms buffering latency, low overhead

  • Perfetto: background service + data sink; captures CPU, memory, battery; more comprehensive but heavier than systrace

  • Method tracing: ART instrumentation; logs entry/exit per method; 100x slower than running code; use on 1% of sessions

  • Tradeoff: systrace = fast + direct but requires interpreting events; method tracing = explicit but overhead distorts measurements

  • Real usage: jank suspected β†’ run systrace (60s) β†’ view on perfeto UI β†’ see which frame missed vsync β†’ dig frame-by-frame

Strong answer tip:

  • Walk through a systrace: find vsync markers β†’ identify frames that miss deadline β†’ backtrace to main/RT thread doing work

πŸš€ See Full Deep Dive


Explain the CFS scheduler and Android thread priority model; when does starvation occur

intermediate advanced internals multithreading
View Answer

Linux kernel CFS scheduler allocates CPU share per-process/thread; Android adds priority boosting, task affinity, and cgroup limits.

In interviews, cover:

  • CFS (Completely Fair Scheduler): divides CPU time proportionally; nice level affects vruntime weight; lower nice = gets more CPU

  • Thread priorities: Looper threads set priority via Process.setThreadPriority(); main thread = THREAD_PRIORITY_DEFAULT (-4)

  • RT scheduling: realtime threads use FIFO/RR policy (not CFS); reserved for system_server components (not app code normally)

  • Tradeoff: high priority = lower latency but may starve normal work; background priority = battery but may miss deadlines

  • Real issue: custom background thread at default priority competes with main; if doing I/O, blocks main waiting for result

Strong answer tip:

  • Explain: if custom thread does heavy work (file I/O) at default priority, it may preempt main, reducing frame rate; use BACKGROUND priority

πŸš€ See Full Deep Dive


Design a modular app architecture; why is the dependency graph acyclic and what breaks

senior advanced internals modularization
View Answer

Modularization breaks monolithic app into feature modules; reduces build time, enables on-demand delivery, isolates teams & risk.

In interviews, cover:

  • Dynamic feature modules: delivered via Play Core; on-demand (user clicks purchase) or install-time (part of base APK)

  • Communication: modules talk via NavigationActivity intents or shared interfaces (extracted to :core module); avoids hard deps

  • Tradeoff: modularity = slower builds initially (more compile steps) but faster iteration (rebuild 1 module); also harder testing

  • Real scale issue: 500+ screens in monolith = 10min builds; split into 30 modules = 90s per module = parallel gains 3-5x speedup

  • Dependency cycles: Aβ†’Bβ†’A breaks modularization; force acyclic graph via code review or static analysis tool

Strong answer tip:

  • Explain module graph design: feature modules depend on :core (API), never on each other; core is thin (navigation, utils, models)

πŸš€ See Full Deep Dive


When facing an architectural decision, what factors matter most and how do you measure

advanced advanced internals advanced
View Answer

Advanced topics require balancing technical depth, team readiness, and business value; strong candidates articulate tradeoffs clearly.

In interviews, cover:

  • Technical depth vs pragmatism: why you'd NOT use native for everything (complexity, debugging, maintenance cost)

  • Team capability: if team unfamiliar with Binder, modularity overhead >> benefit; upskilling needed

  • Measurement discipline: never assumeβ€”profile systrace, measure before/after; anecdote β‰  data

  • Risk tolerance: Doze strict = safer battery but may break critical syncs; tiered approach (foreground service for critical) balances both

  • Interview signal: candidates who say "it depends" + explain factors (data size, team, release timeline) rank higher than "always use X"

Strong answer tip:

  • When asked "should we use NDK?", answer: "depends on CPU profile. If top-10 functions consume >50% time, measure speedup. If <20%, probably no."

πŸš€ See Full Deep Dive


Design a reactive architecture using RxJava; explain backpressure and operator fusion

senior advanced architecture reactive
View Answer

RxJava enables declarative, composable async data streams; backpressure prevents memory overflow when source outpaces consumer.

In interviews, cover:

  • Observable vs Flowable: Observable ignores backpressure (easy but risky); Flowable respects it (safe but overhead); choose by data volume

  • Backpressure strategies: drop (lose data), buffer (memory risk), latest (emit only newest), pause upstream (blocks producer)

  • Operator fusion: RxJava 2 inlines operators to avoid allocation; chaining mapβ†’filterβ†’map creates single loop, not 3 passes

  • Cancellation: Disposable prevents memory leaks; if subscription doesn't unsubscribe, resources leak; CompositeDisposable aggregates many

  • Tradeoff: reactive = elegant async but steeper learning curve; testing needs TestScheduler; imperative may be clearer for simple cases

Strong answer tip:

  • Explain OutOfMemoryError from unbackpressured Observable: if source emits 1M items/sec but consumer takes 1ms each, buffer grows infinitely

πŸš€ See Full Deep Dive


Architect dependency injection at scale using Dagger/Hilt; when do circular deps occur

senior advanced architecture di
View Answer

Dependency injection decouples code through inversion of control; Dagger generates code to wire dependencies at compile-time, preventing circular loops.

In interviews, cover:

  • Dagger dependency graph: DAG (directed acyclic) enforced at compile time; circular Aβ†’Bβ†’A fails with error (prevents runtime surprises)

  • Scopes: @Singleton caches instance lifetime (module level); @ActivityScoped per-activity; unscoped creates new instance each time

  • Lazy vs Provider: Lazy delays creation until .get(); Provider creates fresh on each .get(); tradeoff between memory and initialization

  • Real scale issue: monolith with 100+ modules β†’ Dagger build time can spike (seconds); need hierarchical graphs or library modules

  • Hilt auto-wiring: reduces boilerplate vs raw Dagger; but less explicit = harder to debug non-obvious wiring

Strong answer tip:

  • Why circular deps fail at compile-time (not runtime): A @Provides B, B @Provides A β†’ Dagger sees cycle, errors before code runs; detection is free

πŸš€ See Full Deep Dive


Explain Jetpack Compose recomposition triggers and how to prevent excessive redraws

senior advanced compose performance
View Answer

Compose tracks state changes; recomposition reruns composables when inputs change; smart structuring prevents cascading recomposes.

In interviews, cover:

  • State stability: if parameter immutable reference never changes, Compose skips recomposition of that subtree; use data classes with sensible equals

  • Recomposition scope: changes to State only recompose that scope downward (not entire tree); fine-grained control is key

  • Remember & derivedStateOf: remember caches computed value across recompositions; derivedStateOf skips child recompose if result unchanged

  • Key optimization: wrapping subtree in key { } forces recomposition only if key changes; prevents implicit dependency creep

  • Tradeoff: lazy recomposition = fast but requires careful state modeling; too much remember logic = hard to reason about data flow

Strong answer tip:

  • Explain LazyColumn jank: if item composition expensive (complex layout), 100 items β†’ lag on scroll; fix with content key + memoization

πŸš€ See Full Deep Dive


Walk through custom view measurement, layout, and drawing phases; how do invalidations cascade

intermediate advanced rendering performance
View Answer

Custom views must implement onMeasure (size negotiation), onLayout (position children), onDraw (render); invalidation triggers recalculation.

In interviews, cover:

  • Measure phase: parent offers constraints (width spec + height spec); child returns size (exact, at-most, or unspecified); two-pass negotiation

  • Layout phase: parent calls child.layout() with actual bounds; child positions children recursively; coordinates are relative to parent

  • Draw phase: onDraw() receives Canvas; drawing commands (drawRect, drawText) rendered to framebuffer; occluded areas still drawn (waste)

  • Invalidation cascade: calling invalidate() queues layout/draw pass; if called in onDraw (recursive), ANR possible; use Choreographer for throttling

  • Real bottleneck: measuring unspecified children in loop β†’ O(nΒ²) layouts; wrap in MeasureSpec.makeMeasureSpec to avoid re-negotiation

Strong answer tip:

  • Explain nested RecyclerView jank: parent measures child with unspecified height β†’ child measures all items β†’ O(n) per scroll frame

πŸš€ See Full Deep Dive


Explain Choreographer frame pacing callbacks and how animations sync to display vsync

intermediate advanced animation rendering
View Answer

Choreographer schedules frame callbacks synchronized to display refresh rate (60Hz = 16.67ms); callbacks orchestrate animation timing.

In interviews, cover:

  • Vsync sync: Choreographer.postFrameCallback() queued at next vsync pulse; all callbacks fired before frame deadline (preserves 60fps)

  • Three types: input callbacks (touch dispatch), animation callbacks (property animators), traversal callbacks (measure/layout/draw)

  • Frame pacing: if animation frame takes 18ms (exceeds 16.67ms), vsync drops frame β†’ visible stutter; Choreographer helps but can't fix slow code

  • Interpolation: animation value = interpolator.getInterpolation(elapsed/duration); linear (constant speed) vs ease-in (acceleration)

  • Tradeoff: tight animation loop (fast cadence) = smooth but CPU overhead; loose cadence (sparse callbacks) = jank but power efficient

Strong answer tip:

  • Why animated PropertyAnimator is smoother than custom Handler loop: Choreographer syncs with display; Handler has no vsync awareness, may miss deadlines

πŸš€ See Full Deep Dive


Design a Room database strategy avoiding N+1 queries and slow index misses

intermediate advanced database persistence
View Answer

Room provides compile-time SQL checking; N+1 (loading parent then looping children) kills performance; indices prevent table scans.

In interviews, cover:

  • N+1 pattern: query users (1 query) β†’ loop each user β†’ query their posts (N queries) = N+1 total; solution is JOIN in single query

  • Indices: @Index on frequently queried columns (user_id, timestamp) reduces table scan; adds write overhead, saves read time per query

  • RxJava integration: @Query returns Observable>; Room emits new list on any table change; can cause jank if listener does work

  • Transaction safety: @Transaction ensures query completes atomically; long transactions block other writes (lock contention); tune retention

  • Real scale: app with 1M posts, no index on user_id β†’ table scan reads all 1M rows for each query; adding index = O(log n) instead

Strong answer tip:

  • Explain slow queries: SELECT * FROM users WHERE user_id = ? without index β†’ full table scan (1M+ rows); add @Index(['user_id'])

πŸš€ See Full Deep Dive


Compare WorkManager, periodic JobScheduler, and foreground services; which guarantees work executes

intermediate advanced background reliability
View Answer

WorkManager provides work scheduling with persistence + retry; JobScheduler offers periodic jobs; foreground services stay alive but visible to user.

In interviews, cover:

  • WorkManager: survives app restart (persistent); retries with exponential backoff; respects Doze + battery saver constraints

  • Periodic jobs: JobScheduler schedules aligned to window (batched with others); WorkManager PeriodicWorkRequest can't guarantee exact timing

  • Foreground services: exempt from Doze & cachekill; but users see "X is draining battery" β†’ acceptable only for user-initiated work (music, nav)

  • Guarantee levels: WorkManager β‰ˆ "eventually" (may delay hours); JobScheduler "optimized timing" (batched); foreground = immediate but intrusive

  • Tradeoff: WorkManager = reliable but deferred; periodic jobs = adaptive but imprecise; foreground = immediate but battery impact + permission risk

Strong answer tip:

  • Why WorkManager over JobScheduler for analytics: flaky network β†’ WorkManager auto-retries across app restarts; JobScheduler discards on reboot

πŸš€ See Full Deep Dive


Design a Gradle plugin architecture; explain incremental tasks and caching for build performance

senior advanced build gradle
View Answer

Gradle plugins customize build with custom tasks; incremental tasks only process changed inputs; build caching skips unchanged task outputs.

In interviews, cover:

  • Custom tasks: extend DefaultTask, define @Input/@Output properties; Gradle uses these to detect stale inputs and skip re-execution

  • Incremental tasks: if only 1 file changed, process only that file (not whole source tree); incremental build = 10-100x faster than clean

  • Build caching: if task run was cached in CI, local build skips re-execution; but caching can silently hide bugs if cache keying wrong

  • Real scale issue: 500 modules Γ— 30s compile = 250min full build; parallel + incremental = 1-2min (100x speedup) but requires task isolation

  • Tradeoff: fine-grained @Input/@Output = proper caching but complex; coarse-grained = simple but cache misses, full rebuilds

Strong answer tip:

  • Explain build slowdown: all tasks rerun despite no source changes? Check task inputsβ€”probably reading system properties or time which aren't cached

πŸš€ See Full Deep Dive


Compare Protobuf vs JSON serialization; when do you use each and what are evolution risks

intermediate advanced serialization data
View Answer

Protobuf provides compact binary encoding with schema evolution; JSON is human-readable but larger; choice depends on bandwidth vs developer velocity.

In interviews, cover:

  • Protobuf size: binary encoding β‰ˆ 30% of JSON for same data; saves bandwidth on mobile (cellular cost β‰ˆ $5-10/GB in many regions)

  • Code generation: Protobuf generates setters/getters (strict); JSON uses reflection (slower but flexible); mismatch in field type catches at compile

  • Schema evolution: adding optional field to Protobuf is safe (old clients ignore new field); JSON schemas must be versioned (v1, v2 endpoints)

  • Developer experience: Protobuf = verbose schema + generated code; JSON = any editor but no schema validation until runtime

  • Real tradeoff: Protobuf = 3-5ms encode time + 3MB binary; JSON = 15-20ms encode + 10MB; for 1M requests/day, Protobuf saves 200 CPU-cores

Strong answer tip:

  • When to choose: high-frequency APIs (>1M req/day) β†’ Protobuf; internal tools/webhooks β†’ JSON; hybrid = Protobuf over-wire, JSON for logging

πŸš€ See Full Deep Dive


Architect Firebase offline sync; explain conflict resolution when device and cloud both write

senior advanced firebase sync
View Answer

Firebase Realtime Database syncs offline writes to cloud; conflicts arise when device and cloud both modify same record; resolution requires strategy.

In interviews, cover:

  • Offline writes: Firebase queues writes locally; syncs when reconnected; but if cloud wrote same field, last-write-wins (data loss risk)

  • Conflict strategies: last-write-wins (simple but lossy); timestamp comparison (clock skew risks); vector clocks (complex but safe)

  • Real issue: user edits note offline, cloud has newer version (another device), sync β†’ user's edit discarded silently

  • Firestore transactions: multi-document ACID semantics; but offline writes don't participateβ€”only synced at reconnect, breaking atomicity

  • Tradeoff: optimistic sync = responsive UX but conflict risk; pessimistic (require online) = safe but poor offline experience

Strong answer tip:

  • Explain CRDTs (conflict-free types): instead of last-write-wins, use append-only logs (all edits preserved); merge can reconcile divergent branches

πŸš€ See Full Deep Dive


Explain Android background restrictions - Doze, App Standby, and background start limits

intermediate android background doze workmanager battery
View Answer

Android progressively restricts background work to reduce battery drain; understanding the exact constraints per API level is critical for reliable background execution design.

In interviews, cover:

  • Doze mode (API 23+): when the device is stationary, unplugged, and screen off, Android disables network, wayfence alarms, and JobScheduler; apps get maintenance windows to run deferred work; FCM high-priority messages bypass Doze

  • App Standby (API 23+): apps not used recently are put in standby; their jobs run only during daily maintenance windows; active use resets the standby bucket

  • Background start restrictions (API 26+): apps in the background cannot start Activities; use Notifications with PendingIntent, they can start ForegroundService from background if targeting <API31

  • Android 12 (API 31+): exact alarms require SCHEDULE_EXACT_ALARM permission; foreground service start from background is further restricted

  • WorkManager is the single correct approach for deferrable guaranteed background work; it respects Doze, uses JobScheduler on API 23+ and AlarmManager with broadcast on older, and supports constraints

Strong answer tip:

  • distinguish: long-running active background work (media playback, navigation) belongs in a ForegroundService; deferred guaranteed work in WorkManager; high-priority data in high-priority FCM messages

πŸš€ See Full Deep Dive