Android Fundamentals
Activity, Fragment, lifecycle, Intent, Manifest, configuration changes
- 01
In what order do the Activity lifecycle callbacks fire, and which one runs only once per instance?
EasyAn 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 … - 02
What is a Fragment? When do you use Fragments vs Activities?
EasyA 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) … - 03
What is an Intent? Explicit vs implicit, and how do you get a result back?
MediumAn 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) … - 04
How do you handle configuration changes (rotation) on Android?
MediumBy 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 } … - 05
Which parts of an app have to be declared in AndroidManifest.xml rather than in code?
EasyThe 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" /> … - 06
How do runtime permissions work on Android?
MediumAndroid 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() } … - 07
What kinds of
Contextdoes Android hand you, and which one must never be held by a long-lived object?EasyA 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 … - 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?
MediumWork that must outlive the screen belongs to WorkManager or a foreground service, because
viewModelScopeis 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) { … - 09
What has to be true for
https://example.com/orders/42to open your app directly instead of showing a chooser?MediumAn 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
autoVerifyand 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
You add
res/values-ru/strings.xmlandres/drawable-xxhdpi/. How does Android decide which one a given device gets?EasyAndroid 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
What do
singleTopandsingleTaskactually change about the back stack, and how should Back be handled in a current app?MediumA 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
What do
minSdk,targetSdkandcompileSdkeach control, and what actually changes when you raisetargetSdk?EasyThe 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
You pass
userIdinto a Fragment through its constructor and after rotation it is gone — why does Fragment insist onarguments?EasyThe system recreates your Fragment itself and can only call the no-argument constructor, so anything that is not in the
argumentsBundle 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
You need to send an
Orderobject to the next Activity — what does that extra have to be, and why isParcelablethe Android answer?EasyExtras are written into a
Parceland carried across a Binder transaction, so every object you put in must know how to flatten itself —Parcelableis the platform's own format andSerializableis 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
Where do you put code that must run before any Activity exists, and what should never be kept there?
EasyIn
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
Crash reports show
IllegalStateException: Can not perform this action after onSaveInstanceStateon a fragmentcommit()— what did the app do?MediumIt committed a fragment transaction after the Activity had already saved its state, so the
FragmentManagercannot 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
Building a notification throws an
IllegalArgumentExceptionabout mutability, and once that is fixed every notification opens the same order — what isPendingIntentdoing?MediumA
PendingIntentis 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
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?
MediumTwo 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
registerReceiverwhether 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
Sharing a PDF your app just wrote throws
FileUriExposedException, and switching to a content URI gets aSecurityExceptionin the other app — what is missing?MediumA
file://URI points at a path the receiving app has no rights to, so since Android 7 you publish the file through aFileProviderand 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
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?
MediumSince Android 10 every visible activity in multi-window can be RESUMED at the same time, so
onResumeno longer means "the user is interacting with me" — exclusive resources belong inonTopResumedActivityChanged(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() } …