Activity Lifecycle (Java)
Activities move through well-defined lifecycle callbacks as they appear, gain focus, lose focus, become hidden, and are destroyed. Understanding these transitions helps you place code in the right callback, persist UI state, and avoid memory leaks or crashes during configuration changes and process death.
High-Level Flow
Created
onCreate()
Visible
onStart()
Interactive
onResume()
Paused
onPause()
Stopped
onStop()
Destroyed
onDestroy()
Callback Responsibilities
| Callback | When | Do | Avoid |
|---|---|---|---|
| onCreate(Bundle) | First time created | setContentView, DI, inflate/bind views, restore savedInstanceState | Long-running tasks (move to background thread) |
| onStart() | Becoming visible | Register UI-related receivers, start lightweight UI updates | Heavy work, starting sensors if not needed |
| onResume() | Gains focus (interactive) | Start camera, location updates, animations, listeners | Blocking I/O, disk DB operations on main thread |
| onPause() | Partially obscured | Commit small UI state, pause animations/sensors | Lengthy saves; it should be fast |
| onStop() | No longer visible | Release resources, unregister receivers, stop expensive work | Holding onto context-bound references |
| onDestroy() | Finishing / system destroying | Final cleanup; beware it may not always be called | Relying on it for critical persistence |
// Skeleton with logs (Java)
public class LifecycleActivity extends AppCompatActivity {
private static final String TAG = "LifecycleActivity";
private static final String KEY_COUNT = "count";
private int clickCount = 0;
@Override protected void onCreate(Bundle s) {
super.onCreate(s);
setContentView(R.layout.activity_lifecycle);
Log.d(TAG, "onCreate");
if (s != null) {
clickCount = s.getInt(KEY_COUNT, 0); // restore small UI state
}
}
@Override protected void onStart(){super.onStart(); Log.d(TAG,"onStart");}
@Override protected void onResume(){super.onResume(); Log.d(TAG,"onResume");}
@Override protected void onPause(){ Log.d(TAG,"onPause"); super.onPause(); }
@Override protected void onStop(){ Log.d(TAG,"onStop"); super.onStop(); }
@Override protected void onDestroy(){ Log.d(TAG,"onDestroy"); super.onDestroy(); }
@Override protected void onSaveInstanceState(Bundle out) {
super.onSaveInstanceState(out);
out.putInt(KEY_COUNT, clickCount); // small, serializable state
}
}
State, Configuration Changes & Process Death
Android can destroy and recreate your Activity during configuration changes (rotation, locale, night mode…) or when the process is killed in the background. Use multiple layers of state protection:
- UI instant state → onSaveInstanceState / savedInstanceState
- Screen data → ViewModel (survives config changes; not process death)
- Long-term → DB / SharedPreferences / file
// ViewModel for screen-level state
public class CounterViewModel extends ViewModel {
public MutableLiveData<Integer> counter = new MutableLiveData<>(0);
}
// In Activity
CounterViewModel vm;
@Override protected void onCreate(Bundle s) {
super.onCreate(s);
setContentView(R.layout.activity_main);
vm = new ViewModelProvider(this).get(CounterViewModel.class);
vm.counter.observe(this, value -> ((TextView)findViewById(R.id.tv)).setText(String.valueOf(value)));
}
Lifecycle-Aware Components
Classes that implement DefaultLifecycleObserver or use LifecycleOwner can start/stop work when the Activity goes to the foreground/background automatically.
// Lifecycle-aware helper
public class Tracker implements DefaultLifecycleObserver {
@Override public void onResume(@NonNull LifecycleOwner owner){ start(); }
@Override public void onPause(@NonNull LifecycleOwner owner){ stop(); }
private void start(){ // start sensors/network polling }
private void stop(){ // stop sensors/network polling }
}
// In Activity
Tracker tracker = new Tracker();
getLifecycle().addObserver(tracker);
Foreground vs Background Work
Configuration Changes (Options)
- Default (recommended): let system recreate; rely on ViewModel + savedInstanceState + persistent store.
- Handle yourself: declare configChanges (advanced, use sparingly).
<!-- AndroidManifest.xml (advanced) -->
<activity android:name=".CameraActivity"
android:configChanges="orientation|keyboardHidden|screenSize"/>
Long-Running Work
Use background threads, coroutines (Kotlin) or ExecutorService/HandlerThread in Java. Tie start/stop to lifecycle.
private ExecutorService io = Executors.newSingleThreadExecutor();
@Override protected void onResume(){
super.onResume();
io.submit(() -> {
// network/db work
runOnUiThread(() -> { // update UI safely });
});
}
@Override protected void onStop(){
super.onStop();
// optionally cancel/interrupt ongoing tasks
}
Common Pitfalls
- Leaking Context: Passing Activity to static singletons or long-lived objects.
- Heavy work on main thread: Causes ANRs; move to background threads.
- Forgetting to unregister: Receivers/sensors kept after onStop().
- Relying on onDestroy: Not guaranteed; persist important state earlier.
- Not handling process death: Use savedInstanceState + persistent storage.
Hands-On Checklist
- Add logs to each lifecycle method and rotate the device—observe order.
- Save a counter in onSaveInstanceState; restore it on recreate.
- Start camera preview in onResume, stop it in onPause.
- Move list data to a ViewModel and verify it survives rotation.