Skip to content
All question banks

Mobile Development

Android Questions

Comprehensive guide covering Android Fundamentals, Lifecycle, Architecture Patterns, Jetpack, Kotlin, and Security.

112 of 112 questions

Android Basics11

Android is an open-source, Linux-based operating system designed primarily for touchscreen mobile devices like smartphones and tablets. It is developed by the Open Handset Alliance, led by Google. As of late 2025, the latest major version is Android 16 (codenamed Baklava), which introduced features like desktop windowing and Material 3 Expressive design.

The Android SDK (Software Development Kit) is a collection of libraries and development tools required to build, test, and debug Android applications. It includes the SDK Manager for API levels, the Android Emulator, the ADB (Android Debug Bridge) command-line tool, and essential build tools like Gradle and ProGuard.

Android architecture consists of a stack of layers: 1. Linux Kernel (hardware abstraction/memory), 2. Hardware Abstraction Layer (HAL), 3. Android Runtime (ART) and Native C++ Libraries (SQLite, WebKit), 4. Java API Framework (Managers for Activity, Resource, etc.), and 5. System Apps. Each layer provides abstraction and services to the layer directly above it.

The AndroidManifest.xml is a mandatory configuration file in every Android project. It declares the application's package name, components like Activities and Services, required hardware and software features, and critical security permissions (e.g., Camera, Internet) that the app needs to function and interact with the OS.

Context is a handle to the system that provides access to resources, databases, and class loaders. There are two primary types: 1. Application Context, which is tied to the app's lifecycle and used for long-lived tasks, and 2. Activity Context, which is tied to a specific UI screen and used for UI-related operations like showing dialogs.

A 'Cold Start' happens when the system creates the app's process from scratch. A 'Warm Start' occurs when the process is in memory but the Activity needs to be recreated. A 'Hot Start' is the fastest, simply bringing the existing Activity to the foreground without process or UI recreation, such as returning from the home screen.

Dalvik was the process virtual machine used in earlier Android versions to execute .dex files. It was designed to be memory-efficient for mobile devices using a register-based architecture. Unlike a standard stack-based JVM, Dalvik allowed multiple VM instances to run simultaneously with minimal memory overhead across the system.

Dalvik utilized Just-In-Time (JIT) compilation, translating code during every execution, which could cause lag. ART (Android Runtime) introduced Ahead-Of-Time (AOT) compilation, translating bytecode into machine language during installation. ART provides faster startup times, improved garbage collection, and better battery efficiency compared to the legacy Dalvik VM architecture.

The process involves: 1. Compiling source code (.java/.kt) and resources into DEX files via D8/R8. 2. Packaging DEX, resources, and manifest into an .APK or .AAB using AAPT2. 3. Signing the package with a private key/keystore. 4. Installing the signed package on a device where ART executes the machine-optimized code.

The Native Development Kit (NDK) is a toolset that allows developers to write parts of their app using C or C++. It is useful for implementing performance-critical sections like physics engines, signal processing, or legacy library integration where the overhead of the Java/Kotlin API framework might be too high for requirements.

AIDL (Android Interface Definition Language) allows developers to define a programming interface for Inter-Process Communication (IPC). It breaks down complex objects into primitives that the Android OS can understand and pass across different processes, enabling a client (app) to securely communicate with a background service in a separate process.

Activity & Fragment Lifecycle10

An Activity represents a single, focused screen with a user interface. It is the entry point for user interaction. Every activity is a class inheriting from Activity or AppCompatActivity and must be declared in the AndroidManifest. It manages its own window and handles user input events through its lifecycle callbacks.

The Activity lifecycle is a set of callbacks triggered as an activity moves through states: onCreate (init), onStart (visible), onResume (active/interactive), onPause (partially obscured), onStop (hidden), and onDestroy (terminated). These states allow developers to manage resources like database connections and UI updates efficiently based on the user's visibility and interaction.

onCreate is called once when the activity is first initialized (used for static setup). onStart makes the activity visible to the user but not yet interactive. onResume is called when the activity starts interacting with the user (foreground state). They are paired with onDestroy, onStop, and onPause respectively in the teardown process.

setContentView() is the method that inflates the XML layout and attaches the View hierarchy to the Activity's window. Without calling it, the activity would remain a blank screen with no UI components for the user to interact with. It establishes the bridge between the XML design and the Activity's logic.

A Fragment is a modular, reusable portion of a user interface that exists within an Activity. It has its own lifecycle but is always hosted by an activity. Fragments are used to create flexible UIs that adapt to different screen sizes, such as a multi-pane layout on tablets vs. a single-pane layout on phones.

Fragment lifecycle includes standard activity states plus attachment phases: onAttach, onCreate, onCreateView (UI inflation), onViewCreated, onStart, onResume, onPause, onStop, onDestroyView (UI teardown), onDestroy, and onDetach. It is more complex than the Activity lifecycle because it depends on the host activity's state while maintaining its own internal UI flow.

Activities are standalone entry points for the OS, while Fragments are modular UI pieces managed by an activity. Use Activities to represent major screens and navigation flows. Use Fragments for reusable components, multi-pane UI (master-detail), or when you need to switch sub-sections of a screen without rebuilding the entire activity context.

An Activity is an independent component declared in the Manifest and managed by the OS. A Fragment is a dependent UI component managed by a FragmentManager within an Activity. Activities can exist alone; Fragments must be attached to a host. Activities handle heavy window-level tasks, while Fragments handle granular UI and lifecycle logic.

Orientation changes trigger activity destruction and recreation. To handle this, you can: 1. Use ViewModels to persist UI data across the change. 2. Save small data amounts via onSaveInstanceState Bundles. 3. Retain fragments with setRetainInstance(true) (deprecated). 4. Declare android:configChanges in the Manifest to manually handle the change in onConfigurationChanged().

AppLifecycleState (conceptually in Android through ProcessLifecycleOwner) tracks the state of the entire application rather than just one activity. It helps detect if the app is in the Foreground (visible to user), Background (not visible), or Dead (process killed). This is essential for pausing network syncs or analytics when the user leaves the application entirely.

Android Components10

There are four primary components: 1. Activities (UI screens), 2. Services (Background tasks), 3. Broadcast Receivers (Event listeners), and 4. Content Providers (Data sharing). Each component has a distinct lifecycle and purpose, and they are orchestrated by the OS through the Android System Server based on user and system needs.

Part of Android Jetpack, these components include ViewModel (UI data management), LiveData/Flow (reactive data observation), Room (SQLite abstraction), Navigation (flow management), and WorkManager (deferrable background tasks). They provide a standard, testable architecture that helps developers handle lifecycles automatically and reduce common errors like memory leaks and database crashes.

An Intent is a messaging object used to request an action from another component. An Intent Filter is a declaration in the AndroidManifest.xml that specifies the types of intents a component (like an Activity) is willing to handle. If an Intent matches an Intent Filter, the system knows that component can be launched.

Explicit Intents specify the exact class name of the target component (e.g., Internal navigation). Implicit Intents declare a general action to perform (e.g., 'Open this URL') without naming a specific app. The system then queries all Intent Filters to find suitable candidates and prompts the user if multiple apps match.

A Service is a component that performs long-running operations in the background without a UI. There are three types: 1. Foreground Services (visible to the user via a persistent notification), 2. Background Services (unseen, restricted in newer Android versions), and 3. Bound Services (which allow components to interact and exchange data through an interface).

Broadcast Receivers are components that allow an app to listen for and respond to system-wide announcements or custom events from other apps (e.g., device booting, battery low, or network changes). Once an event occurs, the system delivers it to all registered receivers, which can then trigger specific logic or background tasks.

Content Providers manage access to a structured set of data. They encapsulate the data and provide security through URI-based access. They act as the standard interface that connects data in one process with code running in another process, frequently used for accessing shared data like Contacts, Media, and SMS databases.

The primary purpose is to securely share data between different applications without exposing the internal storage mechanism (like raw SQLite files). It allows apps to expose data to others with controlled permissions and provides a consistent interface for performing CRUD operations on data stored either locally or remotely.

A PendingIntent is a wrapper around a regular Intent that grants another application (like the Notification Manager or Alarm Manager) the permission to execute the contained Intent as if it were the originating app. It is used to trigger future actions that run even after the original app process has been killed.

A Bundle is a type-safe key-value map used primarily to pass primitive data between activities or save state. Parcelable is an interface that allows complex classes to be serialized so their instances can be put into a Bundle. Parcelable is optimized for Android performance and is significantly faster than standard Java Serialization.

Architecture Patterns7

MVVM (Model-View-ViewModel) is an architecture pattern. The Model handles data, the View displays the UI, and the ViewModel acts as a bridge. The ViewModel holds UI-related data and survives configuration changes. The View observes the ViewModel (via LiveData or Flow), automatically updating the UI when the data state changes, decoupling logic from layout.

MVVM is a design pattern recommended by Google. The Model handles business logic and data (Room/Retrofit). The View (Activity/Fragment) is responsible for the UI. The ViewModel is lifecycle-aware and prepares data for the View. It uses 'data-binding' or 'observables' to update the UI without the View knowing about the Model's internal workings.

In MVP (Model-View-Presenter), the Presenter has a 1-to-1 reference to the View and explicitly tells it what to display. In MVVM, the ViewModel has no reference to the View; the View simply observes the ViewModel's data streams. MVVM is better at handling lifecycle issues and reduces boilerplate code through reactive patterns.

MVC often leads to 'Massive Activities' as logic is mixed with UI. MVP decouples logic but creates heavy interfaces. MVVM is currently the best choice for Android because it is lifecycle-aware and integrates with Jetpack components to handle state changes seamlessly. Choose MVVM for most modern Android apps to ensure scalability and testability.

The Repository acts as a mediator between different data sources (e.g., local Room DB and remote API via Retrofit). It provides a clean API to the ViewModel and decides whether to fetch data from the cache or the network. This abstracts the data origin and simplifies the ViewModel's logic.

Clean Architecture separates code into layers (Presentation, Domain, Data) where dependencies only point inward. It is important because it makes the 'core' business logic (Domain/Use Cases) independent of external frameworks like UI or Databases. This ensures the app is highly testable, maintainable, and adaptable to framework changes over time.

Behavioral question template: 'My last app utilized MVVM with Clean Architecture. I used Hilt for Dependency Injection, Retrofit for networking, and Room for local caching. The ViewModel interacted with Use Cases in the Domain layer, which fetched data through a Repository, ensuring a clear separation of concerns and easy unit testing.'

Dependency Injection4

Dependency Injection (DI) is a design pattern where an object's requirements are provided by an external source rather than created internally. It works by passing dependencies through constructors or fields. This increases modularity, makes the code easier to test (using mocks), and decouples the creation of objects from their actual usage logic.

Hilt (a wrapper for Dagger) is implemented by annotating the Application class with @HiltAndroidApp. Activities/Fragments are marked with @AndroidEntryPoint. Dependencies are provided using @Inject on constructors or @Module classes for external libraries. Hilt then automatically manages the lifecycle and generation of the dependency graph at compile-time.

Dagger is a fully static, compile-time dependency injection framework. You should use it (or Hilt) in complex applications where managing object lifecycles manually becomes prone to errors. It reduces boilerplate, ensures all dependencies are satisfied before runtime, and allows for clean code architecture that is easy to scale and unit test.

Implementation typically follows Google's recommendation: using Hilt. I define @Modules for third-party classes (like Retrofit/Room), use @Inject for my own classes' constructors, and ensure the Application class starts the Hilt process. This setup ensures that every component receives its required instances automatically throughout its specific lifecycle (Activity, ViewModel, etc.).

UI Components & RecyclerView5

XML is used for a declarative separation of UI from business logic. It allows the OS to handle view inflation and optimization separately from code execution. It also supports 'resource qualifiers,' enabling the app to automatically load different layouts for different screen sizes, densities, and languages without changing the underlying Java/Kotlin code.

RecyclerView is an advanced UI component for displaying large collections of data. It works by 'recycling' view objects as items scroll off-screen, reusing them for new data coming into view. It uses an Adapter to bind data and a ViewHolder to store view references, significantly reducing memory churn and improving scrolling performance.

Optimization techniques include: 1. Use 'setHasFixedSize(true)' if item dimensions are static. 2. Use 'DiffUtil' to calculate only changed items. 3. Optimize the layout hierarchy to be flat. 4. Avoid heavy logic or object creation in 'onBindViewHolder'. 5. Implement 'setRecycledViewPool' for nested RecyclerViews to share common view types.

Usage involves: 1. Adding the RecyclerView widget to XML. 2. Creating a layout for individual items. 3. Implementing a ViewHolder to hold view references. 4. Creating an Adapter to connect data to the ViewHolders. 5. Setting a LayoutManager (Linear/Grid) and attaching the Adapter to the RecyclerView instance in the Activity or Fragment.

Android Widgets (specifically RemoteViews) are miniature application views that can be embedded in other applications, such as the Home screen. They allow users to see live app data (weather, clock, task lists) and perform quick actions without opening the full app, functioning as small extensions of the main application's UI.

Jetpack & Architecture Components6

Jetpack is a suite of libraries and tools provided by Google to help developers build high-quality apps more easily. It simplifies complex tasks like background work (WorkManager), lifecycle management (Lifecycle), UI building (Compose), and data persistence (Room). It reduces boilerplate code and ensures apps behave consistently across different Android versions.

A ViewModel is a class designed to store and manage UI-related data in a lifecycle-conscious way. Its primary role is to ensure data survives configuration changes like screen rotations. By separating data management from Activities/Fragments, it prevents memory leaks and ensures that the UI always displays up-to-date state from a stable source.

LiveData is an observable data holder that is lifecycle-aware. In MVVM, the ViewModel exposes data as LiveData. The View (Activity/Fragment) observes this data. LiveData automatically handles lifecycle events, stopping notifications when the View is inactive and resuming when active, which prevents crashes and redundant UI updates during background states.

Advantages include: 1. Ensuring the UI matches the data state. 2. Preventing memory leaks as observers are automatically cleaned up. 3. No crashes due to stopped activities. 4. Handling configuration changes automatically. 5. Resources are shared between components efficiently through the Observer pattern, reducing redundant network or DB calls.

Jetpack Compose is a modern, declarative UI toolkit. Unlike XML, which is imperative (requiring manual view updates), Compose uses Kotlin functions to describe the UI based on state. It simplifies UI development by using 'Composables', reducing code by up to 50%, and eliminating the need for separate layout XMLs and 'findViewById' boilerplate.

Data Binding is a library that allows developers to bind UI components in layouts directly to data sources in the app using a declarative format rather than programmatically in code. This reduces boilerplate in Activities, makes UI logic more readable, and can improve app performance by minimizing the calls required to update visual elements.

Data Storage & Persistence10

SQLite is a lightweight, serverless, relational database engine built directly into the Android OS. It stores data locally in a text file on the device. It is used for persistent data storage that requires structured querying (SQL). Developers typically access it through the high-level Room library to simplify implementation and ensure thread safety.

Room is a persistence library that provides an abstraction layer over SQLite. It consists of three major components: Entity (table definitions), DAO (Data Access Objects for SQL queries), and Database (main access point). Room provides compile-time verification of SQL queries and reduces boilerplate code, making local storage management much safer and easier.

SQLite requires manual boilerplate code to handle database creation and cursors. Room is an abstraction that generates this code via annotations. Critically, Room provides compile-time verification of SQL queries (catching errors before run), handles database migrations more gracefully, and integrates directly with reactive streams like LiveData and Flow for automatic UI updates.

Room is used to manage local persistent data. It allows developers to define a database schema using simple POJO classes (Entities) and write SQL queries inside Interfaces (DAOs). It handles all the heavy lifting of opening, closing, and updating the database, while ensuring queries are executed off the main thread for optimal performance.

Data in Room can be secured using SQLCipher, which provides transparent 256-bit AES encryption of the database file. This ensures that even if a device is compromised or storage is accessed directly, the database remains unreadable without the specific key provided by the Android Keystore system at runtime.

Pros: Very easy to implement for simple key-value pairs like settings or flags. Cons: Not designed for large or complex data, operations are synchronous which can block the Main thread (causing ANR), it lacks type-safety, and does not provide built-in mechanisms for data migration or secure encryption without extra libraries.

EncryptedSharedPreferences (part of Jetpack Security) automatically encrypts keys and values using the Android Keystore system. It works just like standard SharedPreferences but uses hardware-backed keys to perform AES encryption on the data before it is written to disk, ensuring sensitive strings like Auth tokens are never stored in plain text.

Jetpack DataStore is a modern replacement for SharedPreferences. Its advantages include: 1. Asynchronous data updates via Coroutines and Flow (no main-thread blocking). 2. Strong type-safety (using Protocol Buffers). 3. Safe error handling and data migration. 4. Consistent data state across the app, solving the thread-safety issues found in the older SharedPreferences implementation.

Offline caching is typically implemented using the 'Single Source of Truth' pattern. The app fetches data from a remote API (Retrofit), saves it into the local database (Room), and the UI then observes only the database. This ensures that the user always sees the most recent locally-stored data even when no internet connection is available.

Secure storage involves a multi-layered approach: 1. Use Android Keystore for managing cryptographic keys. 2. Use EncryptedSharedPreferences for small tokens. 3. Use Room with SQLCipher for large databases. 4. Store extremely sensitive binary data in internal storage with 'MODE_PRIVATE' to prevent other applications from accessing the app's directory on disk.

Networking & APIs6

Volley is an HTTP library developed by Google that makes networking for Android apps easier and, most importantly, faster. It is particularly effective for small, frequent requests like image loading and JSON fetching. It handles request queuing, automatic scheduling, and transparent disk/memory caching of responses to reduce network traffic.

Retrofit is a type-safe HTTP client for Android and Java developed by Square. It works by turning a REST API into a Java/Kotlin interface using annotations (like @GET, @POST). It handles JSON parsing automatically (via GSON/Moshi) and integrates seamlessly with Coroutines, making it the industry standard for making network requests in modern apps.

Experience summary: 'I use Retrofit to define API endpoints and Moshi for data serialization. I handle background threading with Coroutines (Dispatchers.IO) and manage different response states (Success/Error/Loading) using sealed classes. Integration usually involves a Repository that fetches remote data and caches it locally in Room for offline access.'

To secure communication: 1. Enforce HTTPS only (TLS 1.2+). 2. Use SSL Pinning to prevent MITM attacks. 3. Use Network Security Configuration (XML) to restrict domains. 4. Implement proper Auth headers (OAuth2/Bearer tokens). 5. Sanitize and validate all incoming server data to prevent injection attacks or improper state changes in the client.

Pagination is the process of loading large data sets in small 'pages' or chunks. In Android, it is implemented using the Paging 3 library. It uses a PagingSource to fetch data, a Pager to manage the stream, and a PagingDataAdapter to bind the data to a RecyclerView, automatically handling 'infinite scroll' and loading states.

The most popular libraries are: 1. Glide (standard for efficient scrolling and memory management). 2. Coil (modern, Kotlin-first, and built on Coroutines). 3. Picasso (simple and reliable). These libraries handle complex tasks like image resizing, memory/disk caching, and placeholder display automatically to ensure smooth UI performance.

Kotlin Programming Language9

Kotlin features include: 1. Null-safety (builtin ?. operator). 2. Conciseness (no boilerplate getters/setters). 3. Interoperability with Java. 4. Coroutines for async work. 5. Extension functions. Unlike Java, Kotlin is more expressive, less prone to NullPointerExceptions, and treats functions as first-class citizens, making it the preferred language for Android.

Behavioral/Technical: 'I find Kotlin superior due to its safe handling of nulls and reduced boilerplate. Features like Data Classes and Sealed Classes make model management much cleaner. Compared to Java, Kotlin's Coroutines significantly simplify complex asynchronous tasks that previously required difficult-to-manage AsyncTask or manual thread handling.'

Kotlin handles null safety through its type system by distinguishing between nullable types (String?) and non-nullable types (String). It forces developers to handle nulls using the 'Safe Call' (?.), 'Elvis operator' (??), or 'Bang-Bang' (!!). This design virtually eliminates the 'billion-dollar mistake' (NullPointerException) common in Java development.

'lateinit' is used for mutable 'var' properties that will be initialized later (must be non-null). 'lazy' is used for read-only 'val' properties; the initialization happens only when the property is first accessed, and the result is cached. Lazy is thread-safe by default, whereas lateinit is a promise to the compiler for future initialization.

Extension functions allow you to add new functionality to existing classes without inheriting from them or modifying their source code. For example, you can add a 'validateEmail()' function to the standard String class. They provide a clean way to organize utility code and make it available as if it were a native member of the class.

A higher-order function is a function that takes another function as a parameter or returns a function. Common examples in Kotlin are 'map', 'filter', and 'forEach'. They enable functional programming patterns, allowing developers to write more expressive and reusable code by passing logic blocks as arguments to other methods.

Sealed classes represent restricted class hierarchies. Unlike standard classes, all subclasses of a sealed class must be defined in the same file. They are primarily used to represent restricted states in a 'when' expression, ensuring that all possible cases (like Success, Error, Loading) are handled by the developer at compile-time.

Inline functions tell the compiler to copy the function's bytecode directly into the call site rather than creating a new function object and stack frame. This improves performance for higher-order functions that take lambdas, as it removes the memory overhead and execution penalty of creating anonymous classes for every lambda call.

These annotations assist in Java-Kotlin interoperability: @JVMStatic generates a real static method in Java for a Kotlin companion object method. @JVMOverloads generates multiple Java constructors/methods for Kotlin functions with default parameters. @JVMField exposes a Kotlin property as a public field in Java rather than a getter/setter, simplifying access from legacy code.

Coroutines & Multithreading7

Coroutines are 'lightweight threads' managed by the Kotlin runtime rather than the OS. They work through 'suspension' points; when a coroutine hits a suspend function, it pauses without blocking its underlying thread, allowing the thread to do other work. They use 'Scopes' and 'Dispatchers' to manage concurrency in a structured, readable way.

Modern tasks are handled with Coroutines. Example: 'lifecycleScope.launch { val data = withContext(Dispatchers.IO) { api.fetch() }; ui.update(data) }'. This ensures heavy network work happens on the IO thread while UI updates return to the Main thread. Older methods like RxJava or AsyncTasks are now largely superseded by this cleaner, structured concurrency model.

1. Main Thread (UI Thread): Handles all UI updates and user input. 2. Background Threads: Used for all non-UI tasks. In Coroutines, these are managed via Dispatchers: Dispatchers.Main (UI), Dispatchers.IO (Network/Disk), and Dispatchers.Default (CPU-intensive calculations like sorting or image processing).

Multi-threading is handled via Structured Concurrency. Using 'CoroutineScopes' (like viewModelScope), developers launch tasks that are automatically cancelled when the component dies. This ensures that background threads don't leak memory or attempt to update UI components that no longer exist, providing a safe and efficient way to manage multiple concurrent tasks.

Dispatchers.Main runs on the UI thread for visual updates. Dispatchers.IO is optimized for disk and network operations with a large pool of threads. Dispatchers.Default is optimized for CPU-heavy tasks and uses a smaller pool of threads typically matching the number of CPU cores to avoid context-switching overhead during intense calculations.

'launch' is 'fire and forget' and returns a 'Job'; use it when you don't need a result back from the task. 'async' returns a 'Deferred' object; you must call '.await()' to get the result. Async is ideal when you need to run multiple tasks in parallel and wait for their combined results before proceeding.

Suspend functions are functions that can be paused and resumed later without blocking the current thread. They can only be called from other suspend functions or inside a coroutine scope. They represent an asynchronous operation (like fetching data) that 'waits' for completion while leaving the system thread free to perform other UI tasks.

Memory Management & Performance4

Optimization includes: 1. Using LeakCanary to detect leaks. 2. Avoiding static references to Context/Activities. 3. Using specialized collections (SparseArray). 4. Optimizing bitmaps (using Glide/downsampling). 5. Clearing references in onDestroy. 6. Using 'WeakReference' for listeners that might outlive their host. 7. Reducing the number of active background services and broadcast receivers.

I focus on: 1. Reducing View hierarchy depth (using ConstraintLayout). 2. Offloading work to background threads immediately. 3. Implementing efficient caching (Single Source of Truth). 4. Monitoring the 'Memory Profiler' in Android Studio. 5. Enabling R8/ProGuard to shrink the APK and remove unused code paths during the release build process.

The GC manages memory by identifying objects that are no longer reachable from any 'GC Root' (like the main thread or static variables). It uses a 'Mark and Sweep' algorithm. In modern ART, the GC is highly optimized to run concurrently with the app, minimizing 'Stop-the-world' pauses that previously caused visible UI jank and stuttering.

ANR (Application Not Responding) occurs when the Main thread is blocked for more than 5 seconds, usually by a heavy calculation or network call. It is prevented by ensuring the Main thread only handles UI and user input, while all data processing, disk I/O, and networking are moved to background threads using Coroutines or WorkManager.

Security7

Security is enforced by: 1. Using HTTPS/SSL Pinning. 2. Encrypting local data (SQLCipher/EncryptedSharedPreferences). 3. Using the Android Keystore for keys. 4. Obfuscating code with ProGuard/R8. 5. Verifying App Signatures. 6. Implementing Biometrics for sensitive screens. 7. Restricting component access in the Manifest via 'android:exported=false' where interaction with other apps is not required.

Common threats include: 1. Man-in-the-Middle (MITM) attacks on network data. 2. Data leakage from logs or world-readable files. 3. Reverse engineering of the APK. 4. Code injection through unprotected Broadcast Receivers/Intents. 5. Insecure storage of authentication tokens in plain text. 6. Screen overlay attacks where malicious apps draw over your UI to steal credentials.

ProGuard (now mostly superseded by R8) is a tool that shrinks, optimizes, and obfuscates your code. It removes unused code and resources, renames classes/methods into cryptic names, and reduces the final APK size. It is used in the 'release' build type by setting 'minifyEnabled true' in the build.gradle configuration file.

The purpose is two-fold: security and performance. It protects intellectual property by making the app harder to reverse-engineer and reduces the app's footprint on the device. By eliminating 'dead code', it ensures the application binary is as lean as possible, which improves download rates and installation speed for end users.

The Android Keystore system lets you store cryptographic keys in a container to make it more difficult to extract from the device. Once keys are in the keystore, they can be used for cryptographic operations without being exposed to the application process, providing hardware-backed security (on supported devices) for sensitive data encryption and digital signatures.

Biometrics are implemented using the BiometricPrompt API. It provides a standard system dialog for Fingerprint, Face, and Iris authentication. You create a BiometricPrompt instance, define an AuthenticationCallback, and call 'authenticate()'. This ensures the app doesn't handle actual biometric data, relying instead on a 'success' signal from the secure OS layer.

I follow the 'Principle of Least Privilege': 1. Only request necessary permissions. 2. Use the 'Photo Picker' instead of full storage access. 3. Encrypt all PII (Personally Identifiable Information). 4. Clear data on logout. 5. Provide clear privacy disclosures. 6. Use 'Data Deletion' APIs if required by stores, ensuring users have full control over their personal information.

Build Process & Gradle5

Gradle is an advanced build automation system used by Android Studio. Its usage includes managing dependencies (libraries), defining build variants (Debug/Release), automating the compilation/packaging process, and running tests. It uses a Domain Specific Language (Groovy or Kotlin DSL) to configure complex build logic for multi-module projects and diverse device configurations.

Gradle orchestrates the entire lifecycle of building an APK/AAB. It is configured through 'build.gradle' files (Project level and App level). You configure the 'android' block for SDK versions, 'dependencies' for external libraries, and 'buildTypes' for release optimizations like minification. Its role is to turn code and assets into a signed, deployable package efficiently.

Reduction techniques: 1. Use Android App Bundles (.AAB). 2. Enable R8/ProGuard minification. 3. Use WebP for images. 4. Enable 'shrinkResources' in Gradle. 5. Use VectorDrawables instead of PNGs. 6. Remove unused localized resources. 7. Use 'Dynamic Delivery' to load features on-demand rather than bundling everything into the initial download.

Multidex is a solution for the 65,536 method limit in a single DEX file (the '64k limit'). When an app (including libraries) exceeds this limit, Multidex allows the build system to generate multiple DEX files. In modern Android (API 21+), this is enabled by default as the ART runtime natively supports loading multiple DEX files.

I use Git for version control, typically with platforms like GitHub, GitLab, or Bitbucket. I follow the 'GitFlow' branching strategy (Feature, Develop, Master branches) and use tools like GitKraken or the built-in Android Studio Git client. I prioritize small, atomic commits and thorough Pull Request reviews to maintain code quality and history.

Testing & Debugging6

Debugging: Android Studio Debugger, Profiler, Logcat, and Layout Inspector. Testing: JUnit and Mockito for Unit tests, Espresso and Barista for UI tests, and LeakCanary for memory leaks. I also use Flipper or Charles Proxy to inspect network traffic and verify API responses during the development process.

I set breakpoints in the code and run the app in 'Debug mode'. I use the 'Variables' and 'Watches' panes to inspect state, 'Evaluate Expression' to test logic at runtime, and 'Step Over/Into' to trace execution. I also use 'Logcat' for real-time output and the 'Network Inspector' to monitor API calls.

ADB is a versatile command-line tool that lets you communicate with a device. Its purpose includes installing/debugging apps, providing access to a Unix shell for running commands, pulling/pushing files, and capturing screen logs or screenshots. It acts as the bridge between your development workstation and the connected Android device or emulator.

I follow the 'Testing Pyramid': 1. Write extensive JUnit/Mockito tests for business logic (ViewModel/Repository). 2. Use Espresso for critical UI paths. I ensure my code is 'testable' by using Dependency Injection. For unit tests, I mock all external dependencies to ensure they are fast and deterministic, while UI tests verify the actual user flow.

JUnit is the standard testing framework for Java and Kotlin. In Android, it is used for local unit tests that run on the JVM. You annotate methods with @Test and use assertions like 'assertEquals()' to verify results. It is the foundation for ensuring that individual methods and classes perform their logic correctly without needing a device.

A Toast is a simple feedback message that appears as a small popup at the bottom of the screen. It automatically fades in and out after a short duration and does not accept user interaction. It is used for brief notifications (e.g., 'File saved' or 'Network error') that don't require the user to take an action.

Advanced Topics5

UX is critical because it determines user retention. Importance includes: 1. Ensuring the app is intuitive and follows Material Design. 2. Maintaining high performance (no jank). 3. Handling offline states gracefully. 4. Ensuring accessibility for all users. A good UX reduces cognitive load and makes the app feel responsive and reliable, directly impacting the app's success in the store.

I use: 1. Android Jetpack (AndroidX) libraries for backward compatibility. 2. Layout qualifiers (sw600dp, land) for responsive UI. 3. 'if (Build.VERSION.SDK_INT >= ...)' checks for version-specific features. 4. Firebase Test Lab to run tests on diverse physical devices. 5. VectorDrawables to handle all screen densities (LDPI to XXXHDPI) with a single asset.

I use the 'strings.xml' resource system. I create separate 'values' folders with language qualifiers (e.g., values-es for Spanish). I avoid hardcoding strings and use 'String.format()' for dynamic content. I also test layouts for Right-to-Left (RTL) languages like Arabic to ensure the UI mirrors correctly and handles different text lengths without breaking the design.

Dark mode is implemented by defining a 'Night' theme in 'themes.xml (night)'. I use theme attributes (e.g., ?attr/colorSurface) instead of hardcoded hex colors in layouts. The system automatically switches themes based on the user's OS settings. For manual control, I use 'AppCompatDelegate.setDefaultNightMode()' to allow users to toggle themes within the app's settings.

I choose libraries based on: 1. Community support (GitHub stars/issues). 2. Maintenance frequency. 3. License compatibility. 4. Bundle size impact. Common choices include Retrofit (Network), Glide/Coil (Image), Hilt (DI), and Timber (Logging). I always evaluate if a library is necessary or if a native Jetpack solution exists to minimize external dependencies and potential build issues.

Related