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.