DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Android App Lifecycle Explained: Why Your AI-Generated App Survives Rotation

Android App Lifecycle Explained: Why Your AI-Generated App Survives Rotation

When you generate an Android app with AI (or write one manually), one of the most critical concepts to understand is the Activity lifecycle. Why? Because your app will crash, rotate, and go to sleep—and if you don't handle it correctly, users will lose their data or see a blank screen. Let's break it down.

The Activity Lifecycle: The Five States

An Android Activity goes through five main states:

onCreate() → onStart() → onResume() → [Activity visible and interactive]
                                              ↓
                                    onPause() → onStop() → onDestroy()
Enter fullscreen mode Exit fullscreen mode

Here's what each one does:

1. onCreate()

  • Called when the Activity is first created
  • Initialize UI, set up data bindings
  • This is where you restore saved state (more on that later)

2. onStart()

  • Activity is becoming visible to the user
  • Resources like cameras or location can be started
  • Not interactive yet

3. onResume()

  • Activity is now fully interactive
  • The user can tap buttons, input text, etc.
  • This is where you start animations or resume video playback

4. onPause()

  • User is leaving the Activity (another app came to foreground)
  • Save critical data here
  • Stop animations, pause video
  • Note: You have ~500ms before the system kills your app

5. onStop()

  • Activity is no longer visible
  • Clean up heavy resources like database connections
  • The system might kill your app here to free memory

6. onDestroy()

  • Activity is being destroyed
  • Either the user pressed Back, or the system is killing it
  • Final cleanup

The Problem: Configuration Changes (Rotation)

When the user rotates their phone, Android does something brutal: it destroys the current Activity and recreates it from scratch.

Here's the sequence:

User rotates phone
         ↓
onPause() → onStop() → onDestroy() [old Activity destroyed]
         ↓
onCreate() → onStart() → onResume() [new Activity created]
Enter fullscreen mode Exit fullscreen mode

This means:

  • All local variables are lost
  • Any data you were holding in memory is gone
  • UI state is reset
  • Users see a flash/flicker as the UI rebuilds

This is why many AI-generated apps crash on rotation. The developer didn't save state properly.

Solution 1: Using ViewModel to Survive Configuration Changes

ViewModel is a special Jetpack component that survives configuration changes. Data stored in ViewModel is NOT destroyed when the Activity rotates.

Kotlin Code Example:

// 1. Define your ViewModel
class UserViewModel : ViewModel() {
    private val _userName = MutableLiveData<String>("")
    val userName: LiveData<String> = _userName

    fun setUserName(name: String) {
        _userName.value = name
    }
}

// 2. In your Activity, use the ViewModel
class MainActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val nameInput = findViewById<EditText>(R.id.nameInput)

        // When user types, save to ViewModel
        nameInput.addTextChangedListener { text ->
            viewModel.setUserName(text.toString())
        }

        // Observe ViewModel data
        viewModel.userName.observe(this) { name ->
            nameInput.setText(name)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Why this works:

  • ViewModel is scoped to the Activity
  • Android keeps it alive during configuration changes
  • Your data persists through rotation
  • UI automatically updates via LiveData

Solution 2: Using Jetpack Compose with rememberSaveable

If you're using Compose (modern Android UI framework), the pattern is even simpler:

@Composable
fun UserScreen() {
    // This state survives rotation because of rememberSaveable
    var userName by rememberSaveable { mutableStateOf("") }

    Column {
        TextField(
            value = userName,
            onValueChange = { userName = it },
            label = { Text("Enter your name") }
        )
        Text("Hello, $userName!")
    }
}
Enter fullscreen mode Exit fullscreen mode

Key differences:

  • remember { } does NOT survive rotation (lost on destroy)
  • rememberSaveable { } DOES survive rotation (saved to Bundle)
  • Compose automatically restores state when the recomposition happens

How Compose Recomposition Works:

When rotation happens:

  1. Activity is destroyed → rememberSaveable state is saved to a Bundle
  2. Activity is recreated → rememberSaveable restores the Bundle
  3. Compose recomposes the UI tree
  4. TextField is rendered with the restored value
User rotates phone
         ↓
rememberSaveable saves state → Activity destroyed
         ↓
Activity recreated → rememberSaveable restores state
         ↓
Compose recomposes → TextField shows saved text
Enter fullscreen mode Exit fullscreen mode

Solution 3: Saving to savedInstanceState (onSaveInstanceState)

For more complex data, use the onSaveInstanceState() callback:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val nameInput = findViewById<EditText>(R.id.nameInput)

        // Restore from saved state
        if (savedInstanceState != null) {
            val savedName = savedInstanceState.getString("userName", "")
            nameInput.setText(savedName)
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        val nameInput = findViewById<EditText>(R.id.nameInput)
        outState.putString("userName", nameInput.text.toString())
    }
}
Enter fullscreen mode Exit fullscreen mode

Limitations:

  • Only works for Parcelable/Serializable objects
  • Bundle has a ~1MB size limit
  • Not ideal for large datasets

Preventing Rotation: Disabling Configuration Changes (Not Recommended)

You can force your Activity to NOT rotate:

<!-- AndroidManifest.xml -->
<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize"
    android:screenOrientation="portrait" />
Enter fullscreen mode Exit fullscreen mode

Why NOT to do this:

  • Users hate apps that don't rotate
  • You break accessibility for people who rely on rotation
  • AI-generated apps that ignore rotation get bad reviews

When Your App Dies: Process Death

Android can kill your entire process in the background to free memory. This is different from configuration changes.

Sequence:

User backgrounded your app
         ↓
System needs memory
         ↓
System kills your process (calls onDestroy())
         ↓
User returns to your app
         ↓
System recreates the entire app stack
Enter fullscreen mode Exit fullscreen mode

onSaveInstanceState() still works here, but only for UI state. For important data:

  • Use a local database (Room)
  • Use SharedPreferences
  • Use DataStore (Jetpack)
// Room example
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

@Dao
interface UserDao {
    @Insert
    suspend fun insertUser(user: User)

    @Query("SELECT * FROM users LIMIT 1")
    fun getUser(): Flow<User>
}
Enter fullscreen mode Exit fullscreen mode

When your app restarts, query the database:

class MainActivity : AppCompatActivity() {
    private val db = AppDatabase.getInstance(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleScope.launch {
            val user = db.userDao().getUser().collect { user ->
                // Restore UI with user data
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Summary Table

Scenario Solution Survives Data Limit
Configuration change (rotation) ViewModel + LiveData Yes No limit
Configuration change (Compose) rememberSaveable Yes ~1MB (Bundle)
Brief pause (onPause) ViewModel Yes No limit
Process death Room/SharedPreferences Yes Device storage
User presses Back Not saved No N/A

AI-Generated Apps and Lifecycle

When you use Claude Code or other AI to generate an Android app, always verify:

  • ✅ Does it handle rotation without crashing?
  • ✅ Does it use ViewModel or rememberSaveable?
  • ✅ Does it persist important data to a database?
  • ✅ Does it properly implement onPause() for cleanup?

Bad AI-generated apps:

  • Ignore the lifecycle entirely
  • Lose data on rotation
  • Crash on process death
  • Don't save user input

Our templates handle all of this correctly.

The Diagram (For Reference)

┌────────────────────────────────────────────────┐
│           Activity Lifecycle Flow              │
└────────────────────────────────────────────────┘

                  onCreate()
                      ↓
                  onStart()
                      ↓
                  onResume()  ← [User interacting]
                  ↙       ↖
          onPause()      [Rotation: destroys & recreates]
                  ↓
              onStop()
                  ↓
            onDestroy()

ViewModel survives rotation ✓
rememberSaveable survives rotation ✓
Local variables do NOT survive rotation ✗
Enter fullscreen mode Exit fullscreen mode

Conclusion

Android's lifecycle is complex, but it's not magic. The key is:

  1. Use ViewModel or rememberSaveable for UI state
  2. Use Room/SharedPreferences for persistent data
  3. Save state in onSaveInstanceState() for edge cases
  4. Test rotation before shipping

All 8 templates handle lifecycle correctly. https://myougatheax.gumroad.com

Top comments (0)