EAS-202310715274-DimasHendr.../HISTORY_CRASH_ROOT_CAUSE.md
2026-01-14 21:13:18 +07:00

3.8 KiB

FIX: History Screen Close App - Root Cause & Solutions

🔍 Root Causes Identified

Issue 1: Race Condition dalam LaunchedEffect

Problem:

LaunchedEffect(Unit) {
    userPreferences.npmFlow.collect { npm ->
        currentNpm = npm
    }
}

LaunchedEffect(Unit) {
    userPreferences.namaFlow.collect { nama ->
        currentNama = nama
    }
}
  • Dua LaunchedEffect dengan key yang sama (Unit) bersaing satu sama lain
  • Bisa menyebabkan currentNpm tidak ter-load dengan benar saat membuka history
  • Akibat: currentNpm masih null saat navigasi ke history → crash

Issue 2: No Error Handling di Composable History

Problem:

composable("history") {
    val attendanceList by repository.getAttendanceByNpm(
        currentNpm ?: ""  // Bisa null jika loading belum selesai
    ).collectAsState(initial = emptyList())
    
    HistoryScreen(attendanceList = attendanceList, ...)
}
  • currentNpm bisa null jika Flow belum selesai di-load
  • collectAsState bisa throw exception
  • Tidak ada error handling → langsung crash

Solutions Applied

Solution 1: Added Error Handling di LaunchedEffect

LaunchedEffect(Unit) {
    try {
        userPreferences.npmFlow.collect { npm ->
            currentNpm = npm
            isUserLoaded = npm != null  // Track loading state
        }
    } catch (e: Exception) {
        currentNpm = null
        isUserLoaded = false
    }
}

Solution 2: Improved History Composable dengan Try-Catch & Null Safety

composable("history") {
    try {
        val npmForHistory = currentNpm ?: ""
        
        if (npmForHistory.isEmpty()) {
            // Show user-friendly warning
            Box(modifier = Modifier.fillMaxSize(), 
                contentAlignment = Alignment.Center) {
                Text("NPM tidak ditemukan. Silakan login ulang.")
            }
        } else {
            val attendanceList by repository.getAttendanceByNpm(npmForHistory)
                .collectAsState(initial = emptyList())
            
            HistoryScreen(
                attendanceList = attendanceList,
                onBackClick = { navController.navigateUp() }
            )
        }
    } catch (e: Exception) {
        // Fallback error UI
        Box(modifier = Modifier.fillMaxSize(), 
            contentAlignment = Alignment.Center) {
            Text("Error: ${e.message ?: "Tidak dapat membuka riwayat"}")
        }
    }
}

Solution 3: Added Required Imports

import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.Alignment

📊 Changes Summary

Komponen Sebelum Sesudah
LaunchedEffect No error handling Try-catch + state tracking
History Composable No null check Defensive null check
Error Handling None Try-catch with fallback UI
Loading State None isUserLoaded flag
User Feedback Crash Clear error messages

🧪 Test Scenarios

Test 1: Normal Flow

1. Login dengan npm valid
2. Tunggu loading selesai
3. Buka History
✅ Expected: Riwayat ditampilkan dengan aman

Test 2: Rapid Navigation

1. Login
2. Langsung ke history (sebelum loading selesai)
✅ Expected: Tampil "NPM tidak ditemukan..." atau loading

Test 3: Data Error

1. Network issue saat load preferences
2. Buka History
✅ Expected: Tampil user-friendly error message

📝 Files Modified

  • MainActivity.kt - Added error handling & improved history composable

🚀 Expected Results

App tidak lagi crash saat membuka History User mendapat informasi jelas jika ada error Graceful handling untuk berbagai edge cases Proper state management untuk user preferences