UNIT 5: ADVANCED ANDROID DEVELOPMENT & MODERN ARCHITECTURE
5.0 Unit Overview & Learning Objectives
-
Goal: Build scalable, maintainable, and robust Android applications using modern Android Jetpack components.
-
Core Themes:
-
Separation of Concerns: Implement MVVM architecture.
-
Asynchronous Operations: Master Kotlin Coroutines and Flows.
-
Data Management: Handle networking (Retrofit) and local persistence (Room).
-
Dependency Management: Use Hilt for DI.
-
Quality & Release: Write tests, profile performance, and publish to Play Store.
-
5.1 Networking & Web APIs
5.1.1 HTTP Fundamentals & REST Principles
-
HTTP Methods:
-
GET: Retrieve data. -
POST: Create new resource. -
PUT/PATCH: Update existing resource. -
DELETE: Remove resource.
-
-
REST Principles: Stateless, uniform interface, resource-based URLs (e.g.,
/users/1). -
Status Codes:
| Code Range | Meaning | Common Examples | |------------|------------------|-------------------------------------| | 1xx | Informational | 100 Continue | | 2xx | Success | 200 OK, 201 Created | | 4xx | Client Error | 400 Bad Request, 404 Not Found | | 5xx | Server Error | 500 Internal Server Error |
5.1.2 Retrofit Library
-
Setup: Add dependencies for
retrofit,converter-gson(ormoshi), andlogging-interceptor. -
API Interface: Define with annotations.
interface ApiService { @GET("users/{id}") suspend fun getUser(@Path("id") userId: String): Response<User> @POST("users") suspend fun createUser(@Body user: User): Response<User> } -
Data Models: Create Kotlin data classes matching JSON structure.
-
Response Handling:
-
Call<T>(asynchronous with callbacks) vs.Response<T>(direct response object). -
enqueue(): Async, runs on background thread, callbacks on main. -
execute(): Sync, must be called from background thread.
-
-
Logging:
HttpLoggingInterceptorfor debugging requests/responses.
5.1.3 Asynchronous Operations with Coroutines & Retrofit
-
Make Retrofit service functions
suspend. -
Call from a
CoroutineScope(e.g.,viewModelScope). -
Use
Dispatchers.IOfor network calls,Dispatchers.Mainfor UI updates.viewModelScope.launch { try { val user = apiService.getUser("123") _uiState.value = Result.success(user) } catch (e: Exception) { _uiState.value = Result.failure(e) } }
5.1.4 Authentication & Secure Communication
-
Basic Auth: Add
Authorization: Basic <credentials>header viaInterceptor. -
OAuth 2.0 / JWT: Use
Interceptorto dynamically addAuthorization: Bearer <token>header to requests. -
HTTPS: Mandatory for production. Configure
networkSecurityConfigif needed for debug.
5.2 Advanced Local Data Persistence
5.2.1 SQLite & Room Persistence Library
-
Room Components:
| Component | Annotation | Purpose | |-----------|------------|---------| | Entity |
@Entity| Defines DB table structure.@PrimaryKey,@ForeignKey,@Relation. | | DAO |@Dao| Interface with@Query,@Insert,@Update,@Deletemethods. | | Database |@Database| Abstract class extendingRoomDatabase, lists entities & version. | -
Instance Creation:
Room.databaseBuilder(context, AppDatabase::class.java, "db-name").build() -
Migrations: Handle schema changes by implementing
Migrationclass and passing to builder. -
Type Converters: Use
@TypeConverterto store custom objects (e.g.,List<String>as JSON string).
5.2.2 Data Flow with LiveData & Flow
-
LiveData: Lifecycle-aware, holds data, only updates active observers. Ideal for UI.
@Query("SELECT * FROM users") fun getAll(): LiveData<List<User>> -
Kotlin Flow: Cold stream, supports complex operators (
map,filter,combine). Room supportsFlow<List<T>>in@Query. -
Transformation: Use
.map { ... }on Flow/LiveData to transform data before UI consumption.
5.3 Android Architecture Components & MVVM
5.3.1 ViewModel
-
Purpose: Survive configuration changes (e.g., screen rotation). Holds UI-related data.
-
Creation:
val viewModel: MyViewModel by viewModels()(in Fragment) orby activityViewModels()(shared). -
Data Exposure: Expose
LiveDataorStateFlowfor UI observation.
5.3.2 Repository Pattern
-
Role: Single source of truth. Mediates between Network and Local Database.
-
Logic Example (Network as source of truth, local as cache):
-
Check local DB for data.
-
If stale/missing, fetch from network.
-
Save network response to local DB.
-
Emit data from local DB (which is now updated).
-
5.3.3 The MVVM Architecture
| Layer | Components | Responsibility |
|---|---|---|
| Model | Entities, DAOs, Repository, API models | Data & business logic. |
| View | Activity/Fragment | Observe ViewModel, handle UI events, render state. |
| ViewModel | Holds UI state, exposes LiveData/Flow, calls Repository. |
Bridge between View and Model. |
- Data Flow:
View→ViewModel→Repository→(Network/Local)→Repository→ViewModel→View.
5.3.4 Data Binding & View Binding
-
View Binding: Type-safe access to views in layout. Enabled in
build.gradle. Usebinding.textView. -
Data Binding: Binds data directly in XML layout.
-
Expression:
android:text="@{viewModel.userName}" -
Two-way binding:
android:text="@={viewModel.userName}" -
Requires
ObservableFieldorLiveDatain ViewModel.
-
5.4 Advanced Asynchronous Programming & Coroutines
5.4.1 Coroutines Fundamentals
-
launch: Fire-and-forget, returnsJob. Doesn't return result. -
async: ReturnsDeferred<T>(like a future). Useawait()to get result. -
Dispatchers:
-
Main: UI thread. -
IO: Disk/Network I/O. -
Default: CPU-intensive work.
-
-
Structured Concurrency: Coroutines launched in a
CoroutineScopeare canceled when the scope is canceled (e.g.,viewModelScopecancels on ViewModel clear).
5.4.2 Flows
-
Cold Stream: Starts emitting only when collected.
-
Creating:
flow { emit(1); emit(2) },flowOf(1,2,3),list.asFlow(). -
Operators:
-
Intermediate:
map,filter,transform,debounce(for search),distinctUntilChanged. -
Terminal:
collect,toList,first.
-
-
StateFlow & SharedFlow (Hot Streams):
-
StateFlow: Holds a single current value, always has a value. Best for state. -
SharedFlow: No initial value, configurable replay. Best for events.
-
5.4.3 Error Handling in Coroutines
-
Inside
flow { }builder: Usetry/catch. -
Use
.catch { exception -> emit(Result.failure(exception)) }operator. -
CoroutineExceptionHandler: For uncaught exceptions inlaunchcoroutines.
5.5 Dependency Injection (DI)
5.5.1 The Need for DI
-
Decoupling: Classes don't create their own dependencies.
-
Testability: Easy to swap real dependencies with mocks.
-
Single Responsibility: Class focuses on logic, not object creation.
5.5.2 Dagger & Hilt (Official Android DI)
-
Hilt Setup: Apply
kotlin-kaptplugin, addhilt-androidandhilt-android-compilerdependencies. -
Key Annotations:
| Annotation | Used On | Purpose | |------------|---------|---------| |
@HiltAndroidApp| Application class | Generates Dagger components, entry point. | |@AndroidEntryPoint| Android classes (Activity, Fragment, Service, ViewModel) | Enables injection in Android classes. | |@Inject| Constructor, field, method | Requests a dependency. | |@Module| Class/object | Provides dependencies via@Providesor@Binds. | |@InstallIn| Module | Specifies which component (e.g.,SingletonComponent,ActivityComponent). | |@HiltViewModel| ViewModel class | Enables injection in ViewModel. | -
Qualifiers: Use
@Named("qualifier")or custom annotation to disambiguate bindings of same type. -
ViewModel Injection:
@HiltViewModel class MyVM @Inject constructor(val repo: Repo) : ViewModel()
5.5.3 Scoping
-
Annotate provided dependency with
@Singleton,@ActivityScoped, etc., to match component lifetime. -
@SingletonwithSingletonComponent→ lives as long as app. -
@ActivityScopedwithActivityComponent→ lives as long as Activity.
5.6 Testing in Android
5.6.1 Unit Testing (JVM)
-
Scope: Test pure Kotlin/Java logic (ViewModel, Repository, Utils) on JVM (fast).
-
Tools: JUnit4/5, MockK (Kotlin) or Mockito (Java).
-
Testing Coroutines: Use
runTesttest dispatcher.TestDispatcherto control execution.
5.6.2 Instrumented Testing (On Device/Emulator)
-
UI Testing with Espresso:
-
Find Views:
onView(withId(R.id.text)) -
Perform Action:
.perform(click()),.perform(typeText("Hello")) -
Assert:
.check(matches(isDisplayed())),.check(matches(withText("Hello"))) -
Synchronization:
IdlingResourceto wait for async operations (like network calls).
-
-
Testing Architecture Components:
-
ViewModel: Use
InstantTaskExecutorRuleto sync LiveData. -
Room: Use
@RunWith(AndroidJUnit4::class), create in-memory DB for tests.
-
5.6.3 Testing with Hilt
-
Use
@HiltAndroidTeston test class. -
Launch activity with
launchFragmentInContainerfromfragment-testinglibrary. -
Uninstall components after test with
@UninstallModules.
5.7 App Performance, Optimization & Best Practices
5.7.1 Profiling Tools
- Android Studio Profiler: Monitor CPU, Memory, Network, Energy usage in real-time.
5.7.2 Memory Leaks & Detection
-
Common Cause: Holding reference to
Context/ViewafterActivityis destroyed. -
Tool: LeakCanary automatically detects memory leaks.
5.7.3 Efficient List Rendering
-
RecyclerView: Always useViewHolderpattern. -
DiffUtil: Calculate list differences efficiently for updates. -
ListAdapter: Built-inRecyclerView.Adapterthat usesAsyncListDifferandDiffUtil.
5.7.4 Image Loading
-
Libraries: Glide (most popular), Coil (Kotlin-first), Picasso.
-
Features: Caching (memory & disk), resizing, placeholder/error images, GIF support.
5.7.5 App Size Reduction
-
R8/ProGuard: Code shrinking, obfuscation, optimization (enabled by default in release).
-
Remove Unused Resources:
resConfigsinbuild.gradleto keep only needed language/resolution. -
Analyze APK: Use APK Analyzer in Android Studio to inspect contents.
5.8 App Deployment & Publishing
5.8.1 Build Variants & Product Flavors
-
buildTypes:debug(debuggable, debuggable),release(minified, signed). -
productFlavors: Create app variants (e.g.,free,paid,demo). -
Combination:
freeDebug,paidRelease, etc. -
Merging: Manifest and resources from flavors/build types are merged with main source set.
5.8.2 Generating Signed APK / AAB
-
Create Keystore:
.jksfile with alias, password, validity. -
Configure
signingConfigsinbuild.gradle(storeFile, storePassword, keyAlias, keyPassword). -
Assign signingConfig to
releasebuild type. -
Generate: Build → Generate Signed Bundle/APK → Select module & variant.
5.8.3 Google Play Console
-
Store Listing: Title, description, screenshots, feature graphic.
-
Content Rating: Complete questionnaire.
-
Pricing & Distribution: Free/Paid, countries.
-
App Signing by Google Play: Recommended. Google manages app signing key; you upload an upload key.
-
Releases: Internal test → Closed test → Open test → Production.
5.9 Modern & Emerging Topics (Contextual)
5.9.1 Jetpack Compose (Declarative UI) - Overview
-
Core: UI is defined in Composable functions (
@Composable fun MyScreen()). -
State:
mutableStateOf<T>()triggers recomposition when state changes. -
Layouts:
Column,Row,Box,ConstraintLayout. -
Material:
MaterialTheme, components (Button,TextField). -
Interoperability: Can use
AndroidViewto embed traditional Views, orComposeViewin XML.
5.9.2 WorkManager
-
Purpose: Deferrable, guaranteed background work (even after app exit/reboot).
-
Define
Worker: OverridedoWork(). -
WorkRequest:OneTimeWorkRequestorPeriodicWorkRequest. -
Constraints: Set with
.setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
5.9.3 Navigation Component
-
Setup:
nav_graph.xmldefines destinations (fragments) and actions. -
NavHostFragment: Container that swaps fragments based on navigation graph. -
NavController: Object to navigate (navigate(R.id.action)), handle back stack. -
Safe Args: Gradle plugin to generate classes for type-safe argument passing.
-
UI Integration: Connect
BottomNavigationView/DrawerLayoutwithNavController.
5.9.4 Paging 3 Library
-
Purpose: Load large datasets from network/database in pages.
-
Key Classes:
-
PagingSource: Defines how to load data (from network/DB). -
PagingData: Container for paginated data. -
PagingDataAdapter:RecyclerView.Adapterthat handles paging.
-
-
Flow Integration:
PagerreturnsFlow<PagingData<T>>which can be collected in ViewModel and submitted to adapter withadapter.submitData(lifecycle, pagingData).
[!TIP] Exam Focus: Be prepared to draw the MVVM architecture diagram and explain the Repository pattern's role. Know Retrofit annotations and Room annotations by heart. Understand Coroutine context (Dispatchers) and Flow operators. Know the difference between StateFlow and SharedFlow. Be clear on Hilt's component hierarchy (
SingletonComponentvsActivityComponent).