404 Not Found

404 Not Found


nginx

Kotlin Android Basics Explained

Android development has fully embraced Kotlin — Jetpack Compose builds declarative UI with Kotlin DSL, ViewModel + StateFlow manages reactive state, and Room + coroutines simplify the data layer. Charlie builds the Android order dashboard.

1. What You'll Learn


2. A Developer's Real Story

(1) Pain Point: The State-Sync Hell of Imperative UI

Alice used XML + RecyclerView to build the order list. Every state change required manual UI updates: when data changed, call notifyDataSetChanged(); when loading, show/hide a progress bar — 10 states needed 20+ lines of UI sync code.

(2) Compose Declarative UI Solution

KOTLIN
// XML + RecyclerView: imperative, error-prone
// recyclerView.adapter.notifyDataSetChanged()

// Compose: declarative, automatic
@Composable
fun OrderList(orders: List<Order>) {
    LazyColumn {
        items(orders) { order ->
            OrderCard(order)  // UI automatically reflects data changes
        }
    }
}

Compose is declarative — UI is a function of state. When data changes, UI updates automatically with no manual sync.


3. Activity Lifecycle

(1) Lifecycle Callbacks

KOTLIN
class OrderActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            OrderScreen()
        }
    }

    override fun onResume() {
        super.onResume()
        // Refresh data when activity comes to foreground
    }

    override fun onPause() {
        super.onPause()
        // Save draft data
    }
}

(2) Lifecycle State Diagram

100%
stateDiagram-v2
    [*] --> Created: onCreate()
    Created --> Started: onStart()
    Started --> Resumed: onResume()
    Resumed --> Started: onPause()
    Started --> Created: onStop()
    Created --> [*]: onDestroy()
    Started --> Resumed: onResume()

(3) Lifecycle Callback Comparison

Callback Trigger Typical Operation
onCreate(bundle) Activity created Initialize UI, ViewModel
onStart() Visible but not interactive Register listeners
onResume() Gains focus Start animations, refresh data
onPause() Loses focus Pause animations, save draft
onStop() No longer visible Release resources
onDestroy() Being destroyed Clean up resources

4. Jetpack Compose

(1) Composable Functions

KOTLIN
@Composable
fun OrderCard(order: Order) {
    Card(
        modifier = Modifier.fillMaxWidth().padding(8.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
    ) {
        Row(
            modifier = Modifier.padding(16.dp),
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Column {
                Text(text = order.id, style = MaterialTheme.typography.titleMedium)
                Text(text = order.customer, style = MaterialTheme.typography.bodyMedium)
            }
            Text(
                text = "\$${order.total} USD",
                style = MaterialTheme.typography.titleLarge
            )
        }
    }
}

(2) Lists and State

KOTLIN
@Composable
fun OrderListScreen(viewModel: OrderViewModel = viewModel()) {
    val orders by viewModel.orders.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()

    if (isLoading) {
        CircularProgressIndicator()
    } else {
        LazyColumn {
            items(orders) { order ->
                OrderCard(order)
            }
        }
    }
}

(3) Compose Rendering Pipeline

100%
flowchart TD
    A[State Change] --> B[Composition<br/>Run @Composable functions]
    B --> C[Layout<br/>Measure and position]
    C --> D[Drawing<br/>Render to canvas]
    D --> E[UI Updated]

(4) XML vs Compose Comparison

Dimension XML + View Jetpack Compose
Paradigm Imperative Declarative
Language XML + Kotlin Pure Kotlin
State sync Manual Automatic
Code volume High (XML + Adapter + ViewHolder) Low (@Composable)
Preview Requires run IDE real-time preview
Learning curve Moderate Moderate (new paradigm)

5. ViewModel + StateFlow

(1) ViewModel

KOTLIN
class OrderViewModel : ViewModel() {
    private val _orders = MutableStateFlow<List<Order>>(emptyList())
    val orders: StateFlow<List<Order>> = _orders.asStateFlow()

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    fun loadOrders() {
        viewModelScope.launch {
            _isLoading.value = true
            try {
                _orders.value = orderRepository.findAll()
            } finally {
                _isLoading.value = false
            }
        }
    }
}

(2) StateFlow vs LiveData

Dimension LiveData StateFlow
Initial value Not required Required
Coroutine support Needs adapters Native
Thread Main thread Any thread
Lifecycle Aware Needs repeatOnLifecycle
Recommendation Legacy code New projects

6. Room Database

(1) Entity + Dao

KOTLIN
@Entity(tableName = "orders")
data class OrderEntity(
    @PrimaryKey val id: String,
    val total: Double,
    val status: String,
    val customer: String,
    val createdAt: Long = System.currentTimeMillis()
)

@Dao
interface OrderDao {
    @Query("SELECT * FROM orders ORDER BY createdAt DESC")
    fun getAllOrders(): Flow<List<OrderEntity>>

    @Query("SELECT * FROM orders WHERE id = :id")
    suspend fun getById(id: String): OrderEntity?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(order: OrderEntity)

    @Delete
    suspend fun delete(order: OrderEntity)
}

(2) Room Components

Component Annotation Responsibility
Entity @Entity Database table mapping
Dao @Dao Data access operations
Database @Database Database entry point

7. Complete Example: Android Order Dashboard

KOTLIN
// ============================================
// OrderProcessor - Android Order Dashboard
// Feature: Compose UI + ViewModel + StateFlow
// ============================================

// --- Domain ---
data class Order(val id: String, val total: Double, val status: String, val customer: String)

// --- ViewModel ---
class OrderViewModel : ViewModel() {
    private val _orders = MutableStateFlow<List<Order>>(emptyList())
    val orders: StateFlow<List<Order>> = _orders.asStateFlow()

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    init {
        loadOrders()
    }

    fun loadOrders() {
        viewModelScope.launch {
            _isLoading.value = true
            delay(500)  // Simulate network
            _orders.value = listOf(
                Order("ORD-001", 299.99, "CONFIRMED", "Alice"),
                Order("ORD-002", 15_000.00, "PENDING", "Bob"),
                Order("ORD-003", 2_500.00, "SHIPPED", "Charlie"),
                Order("ORD-004", 8_900.00, "CONFIRMED", "Bob"),
                Order("ORD-005", 45.50, "CANCELLED", "Alice")
            )
            _isLoading.value = false
        }
    }

    fun totalRevenue(): Double = _orders.value
        .filter { it.status != "CANCELLED" }
        .sumOf { it.total }
}

// --- Compose UI (Conceptual - requires Android runtime) ---
// @Composable
// fun OrderScreen(viewModel: OrderViewModel = viewModel()) {
//     val orders by viewModel.orders.collectAsState()
//     val isLoading by viewModel.isLoading.collectAsState()
//
//     Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
//         Text("Order Dashboard", style = MaterialTheme.typography.headlineMedium)
//         Text("Revenue: \$${viewModel.totalRevenue()} USD", style = MaterialTheme.typography.titleLarge)
//
//         if (isLoading) {
//             CircularProgressIndicator()
//         } else {
//             LazyColumn {
//                 items(orders) { order -> OrderCard(order) }
//             }
//         }
//     }
// }

// --- JVM Demo (simulating the ViewModel logic) ---
fun main() {
    val viewModel = OrderViewModel()

    // Simulate observing state
    Thread.sleep(1000)  // Wait for loading

    println("=== Android Order Dashboard ===")
    println("Revenue: \$${viewModel.totalRevenue()} USD")
    println("Loading: ${viewModel.isLoading.value}")

    viewModel.orders.value.forEach { order ->
        val icon = when (order.status) {
            "CONFIRMED" -> "✅"
            "PENDING" -> "⏳"
            "SHIPPED" -> "📦"
            "CANCELLED" -> "❌"
            else -> "?"
        }
        println("  $icon ${order.id}: \$${order.total} USD (${order.customer}) [${order.status}]")
    }
}

Output:

TEXT
=== Android Order Dashboard ===
Revenue: $26699.99 USD
Loading: false
  ✅ ORD-001: $299.99 USD (Alice) [CONFIRMED]
  ⏳ ORD-002: $15000.0 USD (Bob) [PENDING]
  📦 ORD-003: $2500.0 USD (Charlie) [SHIPPED]
  ✅ ORD-004: $8900.0 USD (Bob) [CONFIRMED]
  ❌ ORD-005: $45.5 USD (Alice) [CANCELLED]

❓ FAQ

Q Do I have to use Compose? Can I still use XML?
A You can continue using XML, but Google recommends Compose as the default for new projects. Compose and XML can coexist (use ComposeView to embed Compose in XML).
Q What's the difference between ViewModel and Presenter?
A ViewModel is not destroyed on configuration changes (screen rotation) — data is automatically retained. A Presenter requires manual save/restore on config changes.
Q How do I choose between StateFlow and SharedFlow?
A StateFlow is for holding state (must have an initial value, deduplicates). SharedFlow is for event streams (multicast, no initial value). Use StateFlow for UI state, SharedFlow for one-shot events.
Q Does Room support coroutines?
A Yes. Dao suspend functions automatically execute on Room's thread pool. Queries returning Flow automatically re-emit when data changes.
Q How is Compose performance?
A Compose uses smart recomposition (only redraws changed Composables), with performance close to hand-written Views. Avoid computations inside Composable — use remember and derivedStateOf for caching.
Q How do I handle navigation in Compose?
A Use NavHost + composable() to define a navigation graph, navController.navigate() to navigate. Type-safe navigation is available in Compose 1.0+.

📖 Summary


📝 Exercises

  1. Beginner (⭐): Create a Compose Greeting function that displays "Hello, $name!". Hint: @Composable fun Greeting(name: String) { Text("Hello, $name!") }
  2. Intermediate (⭐⭐): Use ViewModel + StateFlow to implement a counter with Compose UI showing the count in real time. Hint: MutableStateFlow(0) + collectAsState()
  3. Advanced (⭐⭐⭐): Implement a complete order list Compose UI: LazyColumn displaying orders, pull-to-refresh, click to navigate to detail. Hint: LazyColumn + pullRefresh + NavController

← Previous | Next →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏