Android App Lifecycle Explained: Why Your AI-Generated App Survives Rotation
Source: Dev.to
The Activity Lifecycle: The Five States
onCreate() → onStart() → onResume() → [Activity visible and interactive]
↓
onPause() → onStop() → onDestroy()
1. onCreate()
- Called when the Activity is first created.
- Initialize UI, set up data bindings.
- Restore saved state (more on that later).
2. onStart()
- Activity becomes visible to the user.
- Start resources like cameras or location services.
- Not interactive yet.
3. onResume()
- Activity is now fully interactive.
- Users can tap buttons, input text, etc.
- Start animations or resume video playback.
4. onPause()
- User is leaving the Activity (another app came to the foreground).
- Save critical data here.
- Stop animations, pause video.
- Note: You have ~500 ms before the system may kill your app.
5. onStop()
- Activity is no longer visible.
- Clean up heavy resources (e.g., database connections).
- The system might kill your app here to free memory.
6. onDestroy()
- Activity is being destroyed (user pressed Back or the system is killing it).
- Final cleanup.
The Problem: Configuration Changes (Rotation)
When the user rotates the phone, Android destroys the current Activity and recreates it from scratch.
User rotates phone
↓
onPause() → onStop() → onDestroy() [old Activity destroyed]
↓
onCreate() → onStart() → onResume() [new Activity created]
Consequences
- All local variables are lost.
- Any in‑memory data disappears.
- 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 Jetpack component that survives configuration changes. Data stored in a ViewModel is not destroyed when the Activity rotates.
Kotlin Code Example
// 1. Define your ViewModel
class UserViewModel : ViewModel() {
private val _userName = MutableLiveData("")
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)
}
}
}
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 (the 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!")
}
}
Key differences
| Compose function | Rotation survival |
|---|---|
remember { } | No – state is lost on destroy |
rememberSaveable { } | Yes – saved to a Bundle and restored |
Compose automatically restores the saved state when the Activity is recreated.
How Compose Recomposition Works
When rotation happens:
- Activity is destroyed →
rememberSaveablesaves its state to aBundle. - Activity is recreated →
rememberSaveablerestores the state from theBundle. - Compose recomposes the UI tree, rendering the
TextFieldwith the restored value.
User rotates phone
↓
rememberSaveable saves state → Activity destroyed
↓
Activity recreated → rememberSaveable restores state
↓
Compose recomposes → TextField shows saved text
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())
}
}
onSaveInstanceState is called before the Activity is destroyed, allowing you to place primitive types, Parcelables, or Serializables into the Bundle. When the Activity is recreated, the saved values are available in onCreate (or onRestoreInstanceState).
TL;DR
- Activity lifecycle determines when you can safely allocate and release resources.
- Rotation triggers a full destroy/recreate cycle, wiping out in‑memory state.
- ViewModel,
rememberSaveable, andonSaveInstanceStateare the three primary ways to preserve data across configuration changes.
Implement one (or a combination) of these strategies, and your AI‑generated Android app will survive rotation gracefully.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("userName", nameInput.text.toString())
}
Limitations
- Only works for
Parcelable/Serializableobjects. Bundlehas a ~1 MB size limit.- Not ideal for large data sets.
Preventing Rotation: Disabling Configuration Changes (Not Recommended)
<!-- Add to your Activity declaration in AndroidManifest.xml -->
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="portrait" />
Why NOT to do this
- Users dislike apps that don’t rotate.
- You break accessibility for people who rely on rotation.
- AI‑generated apps that ignore rotation receive bad reviews.
When Your App Dies: Process Death
Android can kill your entire process in the background to free memory. This is different from a configuration change.
Sequence
User backgrounds your app
↓
System needs memory
↓
System kills your process (calls onDestroy())
↓
User returns to your app
↓
System recreates the entire app stack
onSaveInstanceState() still works here, but only for UI state.
For important data, use persistent storage:
- Room (local database)
- SharedPreferences
- 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>
}
Restoring Data After a Restart
class MainActivity : AppCompatActivity() {
private val db = AppDatabase.getInstance(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
db.userDao().getUser().collect { user ->
// Restore UI with user data
}
}
}
}
Summary Table
| Scenario | Solution | Survives Rotation? | Data Limit |
|---|---|---|---|
| Configuration change (rotation) | ViewModel + LiveData | Yes | No limit |
| Configuration change (Compose) | rememberSaveable | Yes | ~1 MB (Bundle) |
Brief pause (onPause) | ViewModel | Yes | No limit |
| Process death | Room / SharedPreferences / DataStore | Yes | Device storage |
| User presses Back | Not saved | No | N/A |
AI‑Generated Apps and Lifecycle
When you use Claude Code or any other AI to generate an Android app, always verify:
- ✅ Does it handle rotation without crashing?
- ✅ Does it use
ViewModelorrememberSaveable? - ✅ Does it persist important data to a database?
- ✅ Does it properly implement
onPause()for cleanup?
Common issues in poorly generated apps
- Ignoring the lifecycle entirely.
- Losing data on rotation.
- Crashing on process death.
- Not saving 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 ✗
Conclusion
Android’s lifecycle is complex, but it isn’t magic. The key take‑aways:
- Use ViewModel or
rememberSaveablefor UI state. - Use Room, SharedPreferences, or DataStore for persistent data.
- Save state in
onSaveInstanceState()for edge cases. - Test rotation before shipping.
All 8 templates handle the lifecycle correctly.