Skip to content
IT-606 · Android Programming/Quick Revision Short Notes

Android Programming (IT-606) - Unit 5 Short Notes

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 (or moshi), and logging-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: HttpLoggingInterceptor for 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.IO for network calls, Dispatchers.Main for 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 via Interceptor.

  • OAuth 2.0 / JWT: Use Interceptor to dynamically add Authorization: Bearer <token> header to requests.

  • HTTPS: Mandatory for production. Configure networkSecurityConfig if 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, @Delete methods. | | Database | @Database | Abstract class extending RoomDatabase, lists entities & version. |

  • Instance Creation: Room.databaseBuilder(context, AppDatabase::class.java, "db-name").build()

  • Migrations: Handle schema changes by implementing Migration class and passing to builder.

  • Type Converters: Use @TypeConverter to 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 supports Flow<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) or by activityViewModels() (shared).

  • Data Exposure: Expose LiveData or StateFlow for 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):

    1. Check local DB for data.

    2. If stale/missing, fetch from network.

    3. Save network response to local DB.

    4. 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. Use binding.textView.

  • Data Binding: Binds data directly in XML layout.

    • Expression: android:text="@{viewModel.userName}"

    • Two-way binding: android:text="@={viewModel.userName}"

    • Requires ObservableField or LiveData in ViewModel.


5.4 Advanced Asynchronous Programming & Coroutines

5.4.1 Coroutines Fundamentals

  • launch: Fire-and-forget, returns Job. Doesn't return result.

  • async: Returns Deferred<T> (like a future). Use await() to get result.

  • Dispatchers:

    • Main: UI thread.

    • IO: Disk/Network I/O.

    • Default: CPU-intensive work.

  • Structured Concurrency: Coroutines launched in a CoroutineScope are canceled when the scope is canceled (e.g., viewModelScope cancels 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: Use try/catch.

  • Use .catch { exception -> emit(Result.failure(exception)) } operator.

  • CoroutineExceptionHandler: For uncaught exceptions in launch coroutines.


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-kapt plugin, add hilt-android and hilt-android-compiler dependencies.

  • 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 @Provides or @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.

  • @Singleton with SingletonComponent → lives as long as app.

  • @ActivityScoped with ActivityComponent → 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 runTest test dispatcher. TestDispatcher to 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: IdlingResource to wait for async operations (like network calls).

  • Testing Architecture Components:

    • ViewModel: Use InstantTaskExecutorRule to sync LiveData.

    • Room: Use @RunWith(AndroidJUnit4::class), create in-memory DB for tests.

5.6.3 Testing with Hilt

  • Use @HiltAndroidTest on test class.

  • Launch activity with launchFragmentInContainer from fragment-testing library.

  • 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/View after Activity is destroyed.

  • Tool: LeakCanary automatically detects memory leaks.

5.7.3 Efficient List Rendering

  • RecyclerView: Always use ViewHolder pattern.

  • DiffUtil: Calculate list differences efficiently for updates.

  • ListAdapter: Built-in RecyclerView.Adapter that uses AsyncListDiffer and DiffUtil.

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: resConfigs in build.gradle to 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

  1. Create Keystore: .jks file with alias, password, validity.

  2. Configure signingConfigs in build.gradle (storeFile, storePassword, keyAlias, keyPassword).

  3. Assign signingConfig to release build type.

  4. 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 AndroidView to embed traditional Views, or ComposeView in XML.

5.9.2 WorkManager

  • Purpose: Deferrable, guaranteed background work (even after app exit/reboot).

  • Define Worker: Override doWork().

  • WorkRequest: OneTimeWorkRequest or PeriodicWorkRequest.

  • Constraints: Set with .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())

5.9.3 Navigation Component

  • Setup: nav_graph.xml defines 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/DrawerLayout with NavController.

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.Adapter that handles paging.

  • Flow Integration: Pager returns Flow<PagingData<T>> which can be collected in ViewModel and submitted to adapter with adapter.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 (SingletonComponent vs ActivityComponent).

Go to where you left off?

Quick Add to Notes

Save questions, your own notes and screenshots into notes filed by unit. It takes a free account.

Create free account

Have an account? Log in