Android Fundamentals
Irbisa · cheatsheetSeptember 13, 2026

Android Fundamentals

Activity, Fragment, lifecycle, Intent, Manifest, configuration changes

Junior Developer20 itemscompressed for a skim
  1. 01

    In what order do the Activity lifecycle callbacks fire, and which one runs only once per instance?

    Easy

    An Activity is the entry point for one screen, and the system drives it through a fixed sequence of callbacks.

    class HomeActivity : ComponentActivity() {
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)
        if (savedInstanceState == null) {
          // first launch only
    …
  2. 02

    What is a Fragment? When do you use Fragments vs Activities?

    Easy

    A Fragment is a reusable, self-contained piece of UI that lives inside an Activity (or another Fragment).

    class ProfileFragment : Fragment(R.layout.fragment_profile) {
      private val vm: ProfileViewModel by viewModels()
    
      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val nameView = view.findViewById<TextView>(R.id.name)
    …
  3. 03

    What is an Intent? Explicit vs implicit, and how do you get a result back?

    Medium

    An Intent is a message describing an operation to perform.

    // Explicit intent — start your own Activity
    val intent = Intent(this, DetailActivity::class.java).apply {
      putExtra("id", 42L)
      flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
    }
    startActivity(intent)
    …
  4. 04

    How do you handle configuration changes (rotation) on Android?

    Medium

    By default, configuration changes (rotation, locale, dark-mode flip, multi-window resize) trigger Activity destruction + recreation.

    // ViewModel — most state goes here
    class HomeViewModel(private val state: SavedStateHandle) : ViewModel() {
      private val _query = state.getStateFlow("query", "")
      val query: StateFlow<String> = _query
    
      fun update(q: String) { state["query"] = q }
    …
  5. 05

    Which parts of an app have to be declared in AndroidManifest.xml rather than in code?

    Easy

    The manifest is how an app tells the system what it contains, before any of its code runs.

    <!-- AndroidManifest.xml — main pieces -->
    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
    
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.CAMERA" />
      <uses-feature android:name="android.hardware.camera" android:required="false" />
    …
  6. 06

    How do runtime permissions work on Android?

    Medium

    Android grades permissions by risk, and only the dangerous ones need a runtime request.

    class CameraScreen : ComponentActivity() {
      private val cameraPermission = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
      ) { granted ->
        if (granted) openCamera() else showWhyWeNeedIt()
      }
    …
  7. 07

    What kinds of Context does Android hand you, and which one must never be held by a long-lived object?

    Easy

    A Context is your handle on the system — resources, assets, preferences, starting components — and the only thing that really separates the kinds is how long each one lives.

    // ❌ a singleton initialised from an Activity — leaks the Activity and its view tree
    object Prefs {
      lateinit var context: Context
      fun init(c: Context) { context = c }
    }
    Prefs.init(this)                                   // called from an Activity
    …
  8. 08

    The user taps Export and the job takes two minutes. Where does that work run so it still finishes after they leave the app?

    Medium

    Work that must outlive the screen belongs to WorkManager or a foreground service, because viewModelScope is cancelled the moment the ViewModel is cleared.

    @HiltWorker
    class ExportWorker @AssistedInject constructor(
      @Assisted appContext: Context,
      @Assisted params: WorkerParameters,
      private val repo: ExportRepo
    ) : CoroutineWorker(appContext, params) {
    …
  9. 09

    What has to be true for https://example.com/orders/42 to open your app directly instead of showing a chooser?

    Medium

    An https link opens straight into the app only when the system has verified that you own the domain, and that takes a matching pair: an intent filter marked autoVerify and a Digital Asset Links file served from the domain.

    class MainActivity : ComponentActivity() {
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handle(intent)                                  // cold start
        setContent { AppRoot() }
      }
    …
  10. 10

    You add res/values-ru/strings.xml and res/drawable-xxhdpi/. How does Android decide which one a given device gets?

    Easy

    Android matches directory qualifiers against the device configuration, discards every directory that contradicts it, and among the survivors takes the one highest in a fixed precedence order.

    // You reference an id; the lookup happens for you
    val title = stringResource(R.string.order_title)      // values/ or values-ru/
    val icon = painterResource(R.drawable.ic_receipt)     // drawable/ or drawable-xxhdpi/
    
    // Plurals and formatting belong in resources, not in string concatenation
    val label = pluralStringResource(R.plurals.item_count, count, count)
    …
  11. 11

    What do singleTop and singleTask actually change about the back stack, and how should Back be handled in a current app?

    Medium

    A launch mode decides whether a new Activity instance is created or an existing one is reused, and choosing wrong shows up as duplicated screens or a Back button that drops the user out of the app.

    // Compose: intercept Back only while you genuinely want it
    @Composable
    fun EditorScreen(hasUnsavedChanges: Boolean, onDiscard: () -> Unit) {
      var confirming by remember { mutableStateOf(false) }
      BackHandler(enabled = hasUnsavedChanges) { confirming = true }
      if (confirming) DiscardDialog(onConfirm = onDiscard, onDismiss = { confirming = false })
    …
  12. 12

    What do minSdk, targetSdk and compileSdk each control, and what actually changes when you raise targetSdk?

    Easy

    The three SDK numbers answer three different questions: which APIs you can call, which devices can install the app, and which behaviour changes the system applies to you.

    // app/build.gradle.kts
    android {
      compileSdk = 36                       // what you can compile against
      defaultConfig {
        minSdk = 26                         // oldest device that can install
        targetSdk = 36                      // behaviour changes you have tested for
    …
  13. 13

    You pass userId into a Fragment through its constructor and after rotation it is gone — why does Fragment insist on arguments?

    Easy

    The system recreates your Fragment itself and can only call the no-argument constructor, so anything that is not in the arguments Bundle does not come back.

    // Wrong: a constructor argument does not survive recreation
    class ProfileFragment(private val userId: String) : Fragment()
    // after rotation: "could not find Fragment constructor" — the FragmentManager
    // can only call the no-arg one
    
    // Right: identity travels in the arguments Bundle
    …
  14. 14

    You need to send an Order object to the next Activity — what does that extra have to be, and why is Parcelable the Android answer?

    Easy

    Extras are written into a Parcel and carried across a Binder transaction, so every object you put in must know how to flatten itself — Parcelable is the platform's own format and Serializable is a slow reflection-based fallback.

    // build.gradle.kts: plugins { id("kotlin-parcelize") }
    
    @Parcelize
    data class Order(val id: String, val total: Int, val items: List<String>) : Parcelable
    
    // Sending
    …
  15. 15

    Where do you put code that must run before any Activity exists, and what should never be kept there?

    Easy

    In Application.onCreate() — it runs once per process, on the main thread, before any component of yours — which is also why it has to stay nearly empty: every millisecond there sits on the critical path of every cold start.

    @HiltAndroidApp
    class ShopApp : Application() {
    
      override fun onCreate() {
        super.onCreate()
    …
  16. 16

    Crash reports show IllegalStateException: Can not perform this action after onSaveInstanceState on a fragment commit() — what did the app do?

    Medium

    It committed a fragment transaction after the Activity had already saved its state, so the FragmentManager cannot record it and refuses rather than losing the screen silently.

    // Wrong: the response can land while the app is stopped
    viewModel.result.observe(this) { result ->
      parentFragmentManager.commit {
        replace(R.id.container, ResultFragment.newInstance(result))
      }
      // IllegalStateException after onSaveInstanceState — or, once someone
    …
  17. 17

    Building a notification throws an IllegalArgumentException about mutability, and once that is fixed every notification opens the same order — what is PendingIntent doing?

    Medium

    A PendingIntent is a token you hand to another process so it can later start your component with your identity and permissions, so the system makes you declare whether that process may fill fields in, and it returns an existing token instead of a second one whenever the intents look equal.

    // Wrong: same request code for every item, no update flag
    val stale = PendingIntent.getActivity(
      context,
      0,                                          // every order shares one token
      Intent(context, OrderActivity::class.java).putExtra(EXTRA_ID, order.id),
      PendingIntent.FLAG_IMMUTABLE
    …
  18. 18

    A receiver declared in the manifest never fires on real devices, and registering one in code now crashes on Android 14 — what changed under you?

    Medium

    Two separate restrictions: since Android 8 a manifest-declared receiver is no longer started by most implicit broadcasts, and since Android 14 an app targeting API 34 must tell registerReceiver whether the receiver is exported.

    // AndroidManifest.xml — only an exempt implicit broadcast still wakes a dead process
    // <receiver android:name=".BootReceiver" android:exported="true">
    //   <intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter>
    // </receiver>
    
    class BootReceiver : BroadcastReceiver() {
    …
  19. 19

    Sharing a PDF your app just wrote throws FileUriExposedException, and switching to a content URI gets a SecurityException in the other app — what is missing?

    Medium

    A file:// URI points at a path the receiving app has no rights to, so since Android 7 you publish the file through a FileProvider and grant temporary read access on the very intent that carries it.

    // AndroidManifest.xml
    // <provider
    //   android:name="androidx.core.content.FileProvider"
    //   android:authorities="${applicationId}.fileprovider"
    //   android:exported="false"
    //   android:grantUriPermissions="true">
    …
  20. 20

    In split screen your app keeps the camera while the user works in the other app, which then cannot open it — which lifecycle callback are you missing?

    Medium

    Since Android 10 every visible activity in multi-window can be RESUMED at the same time, so onResume no longer means "the user is interacting with me" — exclusive resources belong in onTopResumedActivityChanged(isTopResumedActivity).

    class ScannerActivity : ComponentActivity() {
    
      // Wrong: in split screen both apps are RESUMED, so both grab the camera
      // override fun onResume() { super.onResume(); openCamera() }
      // override fun onPause() { releaseCamera(); super.onPause() }
    …