Skip to main content

Handling Application Lifecycle

1. Understanding Process Lifecycle

State Description Key Methods
Created Activity is created but not visible onCreate()
Started Activity is visible but not in focus onStart()
Resumed Activity is in focus and interactive onResume()
Paused Activity is partially obscured or in background onPause()
Stopped Activity is no longer visible onStop()
Destroyed Activity is being destroyed onDestroy()

Example: Lifecycle Callbacks in Kotlin

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

    override fun onStart() {
        super.onStart()
    }

    override fun onResume() {
        super.onResume()
    }

    override fun onPause() {
        super.onPause()
    }

    override fun onStop() {
        super.onStop()
    }

    override fun onDestroy() {
        super.onDestroy()
    }
}
Note: The lifecycle methods are called in a specific order, and each state transition can trigger multiple callbacks.

2. Handling App Startup

Component Description Usage
Application Base class for maintaining global application state Extend to manage app-level resources and initialization logic
onCreate() Called when the application is starting Initialize global variables, set up logging, or load configuration

Example: Custom Application Class

import android.app.Application
import android.util.Log

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Log.d("MyApp", "Application started")
        // Initialize global state here
    }
}
Note: Register the custom Application class in AndroidManifest.xml using the android:name attribute in the <application> tag.

Key Points

  • Application is the base class for maintaining global application state.
  • onCreate() is called once during app startup, before any activities or services are created.
  • Use Application to initialize global resources or logging systems.

3. Managing Low Memory

Property Description Notes
onLowMemory() Called when the system detects that the application is running low on memory. Invoked by the Android system; not guaranteed to be called.
onTrimMemory() Called when the system determines that the application should trim its memory usage. Provides a hint level for how much to trim.

Example: Implementing onTrimMemory()

override fun onTrimMemory(level: Int) {
    super.onTrimMemory(level)
    when (level) {
        TRIM_MEMORY_RUNNING_MODERATE -> {
            // Clean up moderate memory usage
            cleanupCache()
        }
        TRIM_MEMORY_RUNNING_LOW -> {
            // Clean up more aggressively
            cleanupCache()
            releaseUnnecessaryResources()
        }
        TRIM_MEMORY_RUNNING_CRITICAL -> {
            // Clean up all non-essential resources
            releaseAllResources()
        }
    }
}
Note: onTrimMemory() is a more granular way to handle memory pressure compared to onLowMemory().

Memory Trim Levels

  • TRIM_MEMORY_RUNNING_MODERATE: Application is running moderately low on memory.
  • TRIM_MEMORY_RUNNING_LOW: Application is running low on memory.
  • TRIM_MEMORY_RUNNING_CRITICAL: Application is running critically low on memory.

4. Handling App Visibility

Property Description Usage
onStart() Called when the app becomes visible to the user Use to resume UI updates or start animations
onStop() Called when the app is no longer visible to the user Use to pause UI updates or stop animations
onResume() Called when the app is in the foreground and interactive Use to restore state or start foreground tasks
onPause() Called when the app is partially obscured or going to background Use to save state or pause foreground tasks

Example: Managing Visibility in Activity

class MainActivity : AppCompatActivity() {
    override fun onStart() {
        super.onStart()
        // App is now visible
        Log.d("Lifecycle", "App started")
    }

    override fun onStop() {
        super.onStop()
        // App is no longer visible
        Log.d("Lifecycle", "App stopped")
    }

    override fun onResume() {
        super.onResume()
        // App is in foreground
        Log.d("Lifecycle", "App resumed")
    }

    override fun onPause() {
        super.onPause()
        // App is partially obscured
        Log.d("Lifecycle", "App paused")
    }
}
Note: These lifecycle methods are crucial for managing resources efficiently and ensuring a smooth user experience.

5. Implementing Lifecycle-Aware Components

Android's Lifecycle API allows components to observe the lifecycle state of an Android component and respond to changes without hardcoding lifecycle handling logic into the component itself.

Component Description Key Methods
LifecycleOwner Interface for components that own a lifecycle, such as Activities and Fragments. getLifecycle()
LifecycleObserver Interface for components that observe lifecycle changes. onLifecycleEvent()
Lifecycle Represents the lifecycle state of a component and provides methods to observe it. addObserver(), removeObserver()

Example: Creating a LifecycleObserver

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent

class MyLifecycleObserver : LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onStart() {
        // Handle start event
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onStop() {
        // Handle stop event
    }
}

Example: Registering a LifecycleObserver

class MainActivity : AppCompatActivity(), LifecycleOwner {
    private val lifecycleRegistry = LifecycleRegistry(this)

    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }

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

        val observer = MyLifecycleObserver()
        lifecycleRegistry.addObserver(observer)
    }
}
Note: Lifecycle-aware components are essential for managing resources efficiently and avoiding memory leaks in Android apps.

Key Concepts

  • LifecycleOwner: Any object that can own a Lifecycle, such as an Activity or Fragment.
  • LifecycleObserver: An object that observes lifecycle changes.
  • Lifecycle.Event: Events like ON_START, ON_STOP, etc., that can be observed.

6. Managing Configuration Changes

Property Description Notes
onConfigurationChanged() Callback method invoked when configuration changes occur Must be declared in manifest with configChanges attribute to receive callback
configChanges Manifest attribute to specify which configurations to handle in code Values: screenSize, orientation, keyboardHidden, locale, etc.

Example: Handling Configuration Changes

class MainActivity : AppCompatActivity() {
    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        when (newConfig.orientation) {
            Configuration.ORIENTATION_LANDSCAPE -> {
                // Handle landscape mode
            }
            Configuration.ORIENTATION_PORTRAIT -> {
                // Handle portrait mode
            }
        }
    }
}

Example: Manifest Declaration

<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:exported="true">
</activity>
Note: When configChanges is specified, the activity will not be restarted on configuration changes, and onConfigurationChanged is called instead.
Warning: Not declaring configChanges in the manifest can lead to unnecessary activity recreation and loss of state.

7. Handling App Updates

Update Type Description Implementation Notes
In-App Updates Allows users to update the app directly from within the app
val updateManager = UpdateManager(this)
updateManager.startUpdateFlow(
    updateInfo,
    UpdateType.IMMEDIATE
)
Requires com.google.android.play:app-update dependency
Flexible Updates Allows users to continue using the app while update downloads
val updateManager = UpdateManager(this)
updateManager.startUpdateFlow(
    updateInfo,
    UpdateType.FLEXIBLE
)
Requires com.google.android.play:app-update dependency
Immediate Updates Forces the app to update immediately, blocking user interaction
val updateManager = UpdateManager(this)
updateManager.startUpdateFlow(
    updateInfo,
    UpdateType.IMMEDIATE
)
May interrupt user flow

Example: Check for App Updates

val updateManager = UpdateManager(this)
updateManager.appUpdateInfo
    .addOnSuccessListener { updateInfo ->
        if (updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
            // Show update dialog or start update
        }
    }
Note: Ensure AppUpdateManager is initialized in onCreate() of your main activity.
Warning: Immediate updates can cause poor user experience if not handled carefully.

Update Handling Best Practices

  • Use flexible updates for better UX
  • Implement update progress tracking
  • Handle update errors gracefully

8. Managing Multi-Window Mode

Property Description Values
windowMode Defines the current window mode of the activity WINDOW_MODE_UNDEFINED, WINDOW_MODE_FULLSCREEN, WINDOW_MODE_SPLIT_SCREEN_PRIMARY, WINDOW_MODE_SPLIT_SCREEN_SECONDARY
isInMultiWindowMode Checks if the activity is running in multi-window mode true or false

Example: Check Multi-Window Mode

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

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val isInMultiWindow = isInMultiWindowMode
            if (isInMultiWindow) {
                // Handle UI adjustments for multi-window mode
            }
        }
    }
}

Example: Handle Window Mode Changes

class MainActivity : AppCompatActivity() {
    override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean) {
        super.onMultiWindowModeChanged(isInMultiWindowMode)
        if (isInMultiWindowMode) {
            // Adjust layout for multi-window mode
        } else {
            // Restore layout for full-screen mode
        }
    }
}
Note: Multi-window mode support requires Android N (API level 24) or higher.

Key Points

  • Use isInMultiWindowMode to detect if the app is in multi-window mode
  • Implement onMultiWindowModeChanged() to respond to mode changes
  • Adjust UI layout and behavior for split-screen or floating window modes

9. Handling Picture-in-Picture

Property Description Values
isPictureInPictureSupported Checks if PiP is supported on the device true / false
enterPictureInPictureMode Enters Picture-in-Picture mode PictureInPictureParams
pictureInPictureParams Parameters for PiP mode configuration PictureInPictureParams.Builder

Example: Enter Picture-in-Picture Mode

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

        val pipParams = PictureInPictureParams.Builder()
            .setAspectRatio(Rational(16, 9))
            .build()

        if (isPictureInPictureSupported) {
            enterPictureInPictureMode(pipParams)
        }
    }
}
Note: Requires Android 8.0 (API level 26) or higher.
Warning: PiP mode is not available in all devices or configurations.
Property Description Notes
PictureInPictureParams.Builder Builder for PiP parameters Used to configure aspect ratio and other settings
Rational Represents a fraction (numerator/denominator) Used for aspect ratio configuration

10. Implementing App Shortcuts

App shortcuts allow users to quickly access specific features or content within your app directly from the home screen. These are supported on Android 7.1 (API level 25) and higher.

Property Description Values
id Unique identifier for the shortcut string
shortLabel Short label shown on the shortcut string
longLabel Longer label for accessibility string
icon Resource ID of the icon int
intent Intent to launch when shortcut is tapped Intent

Example: Defining App Shortcuts

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

        val shortcutManager = getSystemService(ShortcutManager::class.java)
        val shortcuts = mutableListOf<ShortcutInfo>()

        val shortcut = ShortcutInfo.Builder(this, "shortcut_id")
            .setShortLabel("New Note")
            .setLongLabel("Create a new note")
            .setIcon(Icon.createWithResource(this, R.drawable.ic_note))
            .setIntent(Intent(this, NoteActivity::class.java).apply {
                action = Intent.ACTION_VIEW
                putExtra("note_id", "new")
            })
            .build()

        shortcuts.add(shortcut)
        shortcutManager.setShortcuts(shortcuts)
    }
}
Note: App shortcuts must be registered at runtime, typically in onCreate or after the app is launched for the first time.
Warning: On Android 12 (API level 31) and above, you must declare the android.permission.POST_NOTIFICATIONS permission to show app shortcuts.

Key Points

  • App shortcuts provide quick access to features.
  • They require ShortcutManager for registration.
  • Icons must be defined as resource IDs.
  • Shortcuts are only available on Android 7.1+.

Was this article helpful?