Overview
The Activity lifecycle is a fundamental concept in Android development that defines how activities are created, started, resumed, paused, stopped, and destroyed. Understanding the lifecycle is crucial for managing resources, saving state, and providing a smooth user experience.
Activity Lifecycle States
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LifecycleDemo";
private TextView statusTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
statusTextView = findViewById(R.id.statusTextView);
updateStatus("onCreate");
Log.d(TAG, "onCreate called");
// Initialize UI components
initializeUI();
// Restore saved instance state
if (savedInstanceState != null) {
restoreState(savedInstanceState);
}
}
private void initializeUI() {
Button button = findViewById(R.id.myButton);
button.setOnClickListener(v -> {
// Handle button click
startActivity(new Intent(this, SecondActivity.class));
});
}
private void restoreState(Bundle savedInstanceState) {
String savedText = savedInstanceState.getString("SAVED_TEXT");
if (savedText != null) {
statusTextView.setText(savedText);
}
}
@Override
protected void onStart() {
super.onStart();
updateStatus("onStart");
Log.d(TAG, "onStart called");
// Start services, register broadcast receivers
registerReceivers();
}
@Override
protected void onResume() {
super.onResume();
updateStatus("onResume");
Log.d(TAG, "onResume called");
// Resume animations, start camera preview, acquire resources
startAnimations();
resumeAudioVideo();
}
@Override
protected void onPause() {
super.onPause();
updateStatus("onPause");
Log.d(TAG, "onPause called");
// Pause animations, release camera, save temporary data
pauseAnimations();
releaseCamera();
}
@Override
protected void onStop() {
super.onStop();
updateStatus("onStop");
Log.d(TAG, "onStop called");
// Unregister receivers, stop location updates
unregisterReceivers();
stopBackgroundTasks();
}
@Override
protected void onDestroy() {
super.onDestroy();
updateStatus("onDestroy");
Log.d(TAG, "onDestroy called");
// Clean up resources, close database connections
cleanupResources();
}
@Override
protected void onRestart() {
super.onRestart();
updateStatus("onRestart");
Log.d(TAG, "onRestart called");
// Refresh data that might have changed while activity was stopped
refreshData();
}
private void updateStatus(String methodName) {
String currentText = statusTextView.getText().toString();
statusTextView.setText(currentText + "\n" + methodName);
}
private void registerReceivers() {
// Register broadcast receivers
}
private void unregisterReceivers() {
// Unregister broadcast receivers
}
private void startAnimations() {
// Start or resume animations
}
private void pauseAnimations() {
// Pause animations
}
private void resumeAudioVideo() {
// Resume audio/video playback
}
private void releaseCamera() {
// Release camera resources
}
private void stopBackgroundTasks() {
// Stop background tasks
}
private void cleanupResources() {
// Clean up resources
}
private void refreshData() {
// Refresh data from database or network
}
}
Saving and Restoring State
1. Instance State Saving
public class StatefulActivity extends AppCompatActivity {
private static final String KEY_COUNTER = "counter";
private static final String KEY_USER_TEXT = "user_text";
private int counter = 0;
private EditText userInput;
private TextView counterText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stateful);
userInput = findViewById(R.id.userInput);
counterText = findViewById(R.id.counterText);
Button incrementButton = findViewById(R.id.incrementButton);
incrementButton.setOnClickListener(v -> {
counter++;
updateCounterDisplay();
});
// Restore state after configuration changes
if (savedInstanceState != null) {
restoreInstanceState(savedInstanceState);
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.d("StatefulActivity", "onSaveInstanceState called");
// Save important data
outState.putInt(KEY_COUNTER, counter);
outState.putString(KEY_USER_TEXT, userInput.getText().toString());
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d("StatefulActivity", "onRestoreInstanceState called");
restoreInstanceState(savedInstanceState);
}
private void restoreInstanceState(Bundle savedInstanceState) {
counter = savedInstanceState.getInt(KEY_COUNTER, 0);
String savedText = savedInstanceState.getString(KEY_USER_TEXT, "");
userInput.setText(savedText);
updateCounterDisplay();
}
private void updateCounterDisplay() {
counterText.setText("Counter: " + counter);
}
}
2. Persistent Data Storage
public class DataPersistenceActivity extends AppCompatActivity {
private static final String PREF_NAME = "MyAppPrefs";
private static final String KEY_USER_NAME = "user_name";
private static final String KEY_LAST_ACTIVITY = "last_activity";
private SharedPreferences sharedPreferences;
private EditText nameEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_persistence);
sharedPreferences = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
nameEditText = findViewById(R.id.nameEditText);
loadSavedData();
Button saveButton = findViewById(R.id.saveButton);
saveButton.setOnClickListener(v -> saveData());
}
@Override
protected void onPause() {
super.onPause();
// Save data when activity goes to background
saveData();
}
private void loadSavedData() {
String savedName = sharedPreferences.getString(KEY_USER_NAME, "");
String lastActivity = sharedPreferences.getString(KEY_LAST_ACTIVITY, "");
nameEditText.setText(savedName);
if (!lastActivity.isEmpty()) {
Toast.makeText(this, "Last activity: " + lastActivity, Toast.LENGTH_SHORT).show();
}
}
private void saveData() {
String userName = nameEditText.getText().toString();
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(KEY_USER_NAME, userName);
editor.putString(KEY_LAST_ACTIVITY, "DataPersistenceActivity at " +
System.currentTimeMillis());
editor.apply();
Toast.makeText(this, "Data saved!", Toast.LENGTH_SHORT).show();
}
}
Practical Examples
Example 1: Camera Activity with Lifecycle Management
public class CameraActivity extends AppCompatActivity {
private Camera camera;
private SurfaceView cameraPreview;
private boolean isCameraAvailable = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
cameraPreview = findViewById(R.id.cameraPreview);
Button captureButton = findViewById(R.id.captureButton);
captureButton.setOnClickListener(v -> capturePhoto());
setupCameraPreview();
}
@Override
protected void onResume() {
super.onResume();
openCamera();
}
@Override
protected void onPause() {
super.onPause();
releaseCamera();
}
@Override
protected void onDestroy() {
super.onDestroy();
// Ensure camera is released
if (camera != null) {
camera.release();
camera = null;
}
}
private void setupCameraPreview() {
// Setup surface view for camera preview
SurfaceHolder holder = cameraPreview.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
// Surface is ready, but we'll open camera in onResume
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (isCameraAvailable) {
startPreview();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stopPreview();
}
});
}
private void openCamera() {
try {
camera = Camera.open(); // Deprecated in API 21, but for demonstration
isCameraAvailable = true;
startPreview();
} catch (Exception e) {
Log.e("CameraActivity", "Failed to open camera: " + e.getMessage());
isCameraAvailable = false;
}
}
private void releaseCamera() {
if (camera != null) {
stopPreview();
camera.release();
camera = null;
isCameraAvailable = false;
}
}
private void startPreview() {
if (camera == null || !cameraPreview.getHolder().getSurface().isValid()) {
return;
}
try {
camera.setPreviewDisplay(cameraPreview.getHolder());
camera.startPreview();
} catch (IOException e) {
Log.e("CameraActivity", "Error starting camera preview: " + e.getMessage());
}
}
private void stopPreview() {
if (camera != null) {
camera.stopPreview();
}
}
private void capturePhoto() {
if (!isCameraAvailable) {
Toast.makeText(this, "Camera not available", Toast.LENGTH_SHORT).show();
return;
}
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// Save picture or process data
savePicture(data);
camera.startPreview(); // Restart preview after capture
}
});
}
private void savePicture(byte[] data) {
// Implementation to save picture to storage
Toast.makeText(this, "Picture captured!", Toast.LENGTH_SHORT).show();
}
}
Example 2: Music Player Activity
public class MusicPlayerActivity extends AppCompatActivity {
private MediaPlayer mediaPlayer;
private boolean isPrepared = false;
private int currentPosition = 0;
private static final String KEY_POSITION = "playback_position";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music_player);
initializePlayer();
setupControls();
if (savedInstanceState != null) {
currentPosition = savedInstanceState.getInt(KEY_POSITION, 0);
}
}
@Override
protected void onStart() {
super.onStart();
if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(currentPosition);
}
}
@Override
protected void onResume() {
super.onResume();
// Audio focus management would go here
}
@Override
protected void onPause() {
super.onPause();
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
currentPosition = mediaPlayer.getCurrentPosition();
}
}
@Override
protected void onStop() {
super.onStop();
// Save playback position to preferences for next session
savePlaybackPosition();
}
@Override
protected void onDestroy() {
super.onDestroy();
releasePlayer();
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (mediaPlayer != null) {
outState.putInt(KEY_POSITION, mediaPlayer.getCurrentPosition());
}
}
private void initializePlayer() {
mediaPlayer = MediaPlayer.create(this, R.raw.sample_song);
mediaPlayer.setOnPreparedListener(mp -> {
isPrepared = true;
mp.seekTo(currentPosition);
});
mediaPlayer.setOnCompletionListener(mp -> {
// Song completed, play next or reset
resetPlayer();
});
}
private void setupControls() {
Button playButton = findViewById(R.id.playButton);
Button pauseButton = findViewById(R.id.pauseButton);
Button stopButton = findViewById(R.id.stopButton);
playButton.setOnClickListener(v -> playMusic());
pauseButton.setOnClickListener(v -> pauseMusic());
stopButton.setOnClickListener(v -> stopMusic());
}
private void playMusic() {
if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
if (isPrepared) {
mediaPlayer.start();
} else {
mediaPlayer.prepareAsync();
}
}
}
private void pauseMusic() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
currentPosition = mediaPlayer.getCurrentPosition();
}
}
private void stopMusic() {
if (mediaPlayer != null) {
mediaPlayer.stop();
isPrepared = false;
currentPosition = 0;
}
}
private void resetPlayer() {
if (mediaPlayer != null) {
mediaPlayer.seekTo(0);
currentPosition = 0;
}
}
private void releasePlayer() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
private void savePlaybackPosition() {
SharedPreferences prefs = getSharedPreferences("MusicPlayer", MODE_PRIVATE);
prefs.edit().putInt("last_position", currentPosition).apply();
}
}
Example 3: Fragment-Activity Lifecycle Coordination
public class FragmentContainerActivity extends AppCompatActivity
implements MyFragment.FragmentInteractionListener {
private static final String TAG_FRAGMENT = "my_fragment";
private boolean isFragmentActive = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_container);
// Add fragment if first creation
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new MyFragment(), TAG_FRAGMENT)
.commit();
}
}
@Override
protected void onResume() {
super.onResume();
isFragmentActive = true;
// Notify fragment that activity is resumed
MyFragment fragment = (MyFragment) getSupportFragmentManager()
.findFragmentByTag(TAG_FRAGMENT);
if (fragment != null) {
fragment.onActivityResumed();
}
}
@Override
protected void onPause() {
super.onPause();
isFragmentActive = false;
// Notify fragment that activity is paused
MyFragment fragment = (MyFragment) getSupportFragmentManager()
.findFragmentByTag(TAG_FRAGMENT);
if (fragment != null) {
fragment.onActivityPaused();
}
}
// Fragment interaction callbacks
@Override
public void onFragmentButtonClicked(String message) {
Toast.makeText(this, "Fragment says: " + message, Toast.LENGTH_SHORT).show();
}
@Override
public boolean isActivityActive() {
return isFragmentActive;
}
}
// Corresponding Fragment
public class MyFragment extends Fragment {
public interface FragmentInteractionListener {
void onFragmentButtonClicked(String message);
boolean isActivityActive();
}
private FragmentInteractionListener listener;
private TextView statusText;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof FragmentInteractionListener) {
listener = (FragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString() +
" must implement FragmentInteractionListener");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_my, container, false);
statusText = view.findViewById(R.id.statusText);
Button actionButton = view.findViewById(R.id.actionButton);
actionButton.setOnClickListener(v -> {
if (listener != null) {
listener.onFragmentButtonClicked("Button clicked from fragment!");
}
});
return view;
}
@Override
public void onResume() {
super.onResume();
updateStatus("Fragment resumed");
}
@Override
public void onPause() {
super.onPause();
updateStatus("Fragment paused");
}
public void onActivityResumed() {
updateStatus("Activity resumed - Fragment notified");
}
public void onActivityPaused() {
updateStatus("Activity paused - Fragment notified");
}
private void updateStatus(String message) {
if (statusText != null) {
statusText.setText(message);
}
}
@Override
public void onDetach() {
super.onDetach();
listener = null;
}
}
Advanced Lifecycle Scenarios
1. Handling Configuration Changes
public class ConfigChangeActivity extends AppCompatActivity {
private static final String KEY_DATA = "important_data";
private static final String KEY_TIMESTAMP = "timestamp";
private String importantData;
private long timestamp;
private boolean isConfigChange = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_config_change);
// Check if this is a configuration change
if (savedInstanceState != null) {
isConfigChange = true;
restoreFromSavedState(savedInstanceState);
} else {
initializeNewData();
}
setupUI();
logLifecycle("onCreate - Config change: " + isConfigChange);
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
logLifecycle("onSaveInstanceState");
// Save data that should survive configuration changes
outState.putString(KEY_DATA, importantData);
outState.putLong(KEY_TIMESTAMP, timestamp);
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
logLifecycle("onRestoreInstanceState");
restoreFromSavedState(savedInstanceState);
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
logLifecycle("onConfigurationChanged");
// Handle configuration changes manually
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "Switched to Landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
Toast.makeText(this, "Switched to Portrait", Toast.LENGTH_SHORT).show();
}
}
private void initializeNewData() {
importantData = "Initial data created at " + System.currentTimeMillis();
timestamp = System.currentTimeMillis();
}
private void restoreFromSavedState(Bundle savedInstanceState) {
importantData = savedInstanceState.getString(KEY_DATA, "Default data");
timestamp = savedInstanceState.getLong(KEY_TIMESTAMP, System.currentTimeMillis());
}
private void setupUI() {
TextView dataView = findViewById(R.id.dataTextView);
dataView.setText(importantData + "\nTimestamp: " + timestamp);
Button refreshButton = findViewById(R.id.refreshButton);
refreshButton.setOnClickListener(v -> refreshData());
}
private void refreshData() {
importantData = "Refreshed data at " + System.currentTimeMillis();
timestamp = System.currentTimeMillis();
setupUI(); // Refresh UI
}
private void logLifecycle(String method) {
Log.d("ConfigChangeActivity", method);
}
}
2. Activity Result Handling
public class ResultHandlingActivity extends AppCompatActivity {
private static final int REQUEST_SELECT_CONTACT = 1;
private static final int REQUEST_TAKE_PHOTO = 2;
private static final int REQUEST_PICK_DOCUMENT = 3;
private TextView resultTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_result_handling);
resultTextView = findViewById(R.id.resultTextView);
setupButtons();
}
private void setupButtons() {
Button selectContactBtn = findViewById(R.id.selectContactButton);
Button takePhotoBtn = findViewById(R.id.takePhotoButton);
Button pickDocumentBtn = findViewById(R.id.pickDocumentButton);
selectContactBtn.setOnClickListener(v -> selectContact());
takePhotoBtn.setOnClickListener(v -> takePhoto());
pickDocumentBtn.setOnClickListener(v -> pickDocument());
}
private void selectContact() {
Intent intent = new Intent(Intent.ACTION_PICK,
ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(intent, REQUEST_SELECT_CONTACT);
}
private void takePhoto() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_TAKE_PHOTO);
} else {
Toast.makeText(this, "No camera app available", Toast.LENGTH_SHORT).show();
}
}
private void pickDocument() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(intent, REQUEST_PICK_DOCUMENT);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "No file manager available", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_SELECT_CONTACT:
handleContactSelection(data);
break;
case REQUEST_TAKE_PHOTO:
handlePhotoCapture(data);
break;
case REQUEST_PICK_DOCUMENT:
handleDocumentPick(data);
break;
}
} else if (resultCode == RESULT_CANCELED) {
updateResult("Operation cancelled by user");
}
}
private void handleContactSelection(Intent data) {
if (data != null && data.getData() != null) {
// Process contact URI
updateResult("Contact selected: " + data.getData().toString());
}
}
private void handlePhotoCapture(Intent data) {
if (data != null && data.getExtras() != null) {
Bitmap imageBitmap = (Bitmap) data.getExtras().get("data");
if (imageBitmap != null) {
updateResult("Photo captured: " + imageBitmap.getWidth() + "x" +
imageBitmap.getHeight());
}
}
}
private void handleDocumentPick(Intent data) {
if (data != null && data.getData() != null) {
updateResult("Document selected: " + data.getData().toString());
}
}
private void updateResult(String message) {
resultTextView.setText("Last Result: " + message);
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
Best Practices
1. Lifecycle-Aware Components
public class LifecycleAwareActivity extends AppCompatActivity {
private LocationManager locationManager;
private NetworkMonitor networkMonitor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lifecycle_aware);
// Initialize lifecycle-aware components
locationManager = new LocationManager(this, getLifecycle());
networkMonitor = new NetworkMonitor(this, getLifecycle());
}
}
// Lifecycle-aware Location Manager
public class LocationManager implements LifecycleObserver {
private Context context;
private LocationListener locationListener;
public LocationManager(Context context, Lifecycle lifecycle) {
this.context = context;
lifecycle.addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void startLocationUpdates() {
Log.d("LocationManager", "Starting location updates");
// Start location updates
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void stopLocationUpdates() {
Log.d("LocationManager", "Stopping location updates");
// Stop location updates
}
}
// Lifecycle-aware Network Monitor
public class NetworkMonitor implements LifecycleObserver {
private Context context;
private BroadcastReceiver networkReceiver;
public NetworkMonitor(Context context, Lifecycle lifecycle) {
this.context = context;
lifecycle.addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void registerNetworkReceiver() {
Log.d("NetworkMonitor", "Registering network receiver");
// Register network broadcast receiver
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void unregisterNetworkReceiver() {
Log.d("NetworkMonitor", "Unregistering network receiver");
// Unregister network broadcast receiver
}
}
2. Memory Management
public class MemoryAwareActivity extends AppCompatActivity {
private Bitmap largeBitmap;
private ArrayList<String> largeDataList;
private static WeakReference<Context> contextWeakRef;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memory_aware);
contextWeakRef = new WeakReference<>(this);
loadDataSafely();
}
@Override
protected void onDestroy() {
super.onDestroy();
// Clean up resources to prevent memory leaks
if (largeBitmap != null) {
largeBitmap.recycle();
largeBitmap = null;
}
if (largeDataList != null) {
largeDataList.clear();
largeDataList = null;
}
}
@Override
public void onLowMemory() {
super.onLowMemory();
Log.w("MemoryAwareActivity", "Low memory warning received");
// Release non-critical resources
releaseNonCriticalResources();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
switch (level) {
case TRIM_MEMORY_RUNNING_MODERATE:
case TRIM_MEMORY_RUNNING_LOW:
case TRIM_MEMORY_RUNNING_CRITICAL:
// Release resources that can be quickly recreated
releaseRecreatableResources();
break;
case TRIM_MEMORY_UI_HIDDEN:
// UI is no longer visible, release UI resources
releaseUIResources();
break;
}
}
private void loadDataSafely() {
// Load data in background to avoid ANR
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
// Load data in background
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
// Update UI on main thread
if (!isFinishing() && !isDestroyed()) {
updateUIWithData();
}
}
}.execute();
}
private void releaseNonCriticalResources() {
// Release cache, temporary files, etc.
}
private void releaseRecreatableResources() {
// Release resources that can be recreated
}
private void releaseUIResources() {
// Release bitmaps, animations, etc.
}
private void updateUIWithData() {
// Update UI with loaded data
}
}
Common Lifecycle Pitfalls and Solutions
public class LifecyclePitfallsActivity extends AppCompatActivity {
// Pitfall 1: Starting async tasks without lifecycle consideration
private void safeAsyncOperation() {
if (isFinishing() || isDestroyed()) {
return; // Don't start if activity is dying
}
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... voids) {
return performLongOperation();
}
@Override
protected void onPostExecute(String result) {
// Check if activity is still alive
if (!isFinishing() && !isDestroyed()) {
updateUI(result);
}
}
}.execute();
}
// Pitfall 2: Holding context references
private static class SafeHandler {
private final WeakReference<Activity> activityRef;
public SafeHandler(Activity activity) {
this.activityRef = new WeakReference<>(activity);
}
public void doSomething() {
Activity activity = activityRef.get();
if (activity != null && !activity.isFinishing()) {
// Safe to use activity
}
}
}
// Pitfall 3: Not handling configuration changes properly
@Override
public Object onRetainCustomNonConfigurationInstance() {
// Retain objects across configuration changes
return new RetainedData("Important data");
}
private void useRetainedData() {
RetainedData data = (RetainedData) getLastCustomNonConfigurationInstance();
if (data != null) {
// Use retained data
}
}
private static class RetainedData {
private String importantData;
public RetainedData(String data) {
this.importantData = data;
}
}
private String performLongOperation() {
return "Result";
}
private void updateUI(String result) {
// Update UI
}
}
Understanding and properly managing the Activity lifecycle is crucial for creating robust, efficient Android applications that provide a good user experience and properly manage system resources.