Progress Β· 0/10 sections
π‘ π‘ Android Core Concepts
5 min read Β· Notion
π± Activity & Fragment Lifecycle
Activity Lifecycle callbacks (in order)
onCreate() β onStart() β onResume() β [RUNNING]
β onPause() β onStop() β onDestroy()onCreate()β Initialize UI, bindings, ViewModel. Called once.onStart()β Activity visible but not interactive.onResume()β Activity in foreground and interactive.onPause()β Another activity comes to foreground (save lightweight state here).onStop()β Activity no longer visible (save heavier state here).onDestroy()β Activity is finishing or being destroyed by system.
Fragment Lifecycle vs Activity Lifecycle
Fragment has additional callbacks:
onAttach()βonCreate()βonCreateView()βonViewCreated()βonStart()βonResume()onPause()βonStop()βonDestroyView()βonDestroy()βonDetach()
Key difference: onDestroyView() destroys the view hierarchy but the Fragment instance survives (e.g., in backstack). Always clear view references in onDestroyView() to avoid memory leaks.
What are Launch Modes in Android?
Defined in AndroidManifest.xml via android:launchMode:
| Mode | Behavior |
|---|---|
standard | New instance always created (default) |
singleTop | Reuses top-of-stack instance if same; calls onNewIntent() |
singleTask | Single instance per task; clears tasks above it; calls onNewIntent() |
singleInstance | Single instance in its own task, no other activities in same task |
What is the AndroidManifest.xml?
The manifest declares:
- App components (Activities, Services, Receivers, Providers)
- Required permissions
- Hardware/software features required
- App metadata (app ID, version, min/target SDK)
- Entry point Activity (intent-filter with MAIN + LAUNCHER)
π Intents
Explicit vs Implicit Intents
- Explicit: Directly specifies the target component (class name).
startActivity(Intent(this, DetailActivity::class.java))- Implicit: Declares an action; the system finds the matching component.
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com"))
startActivity(intent)How to pass data between Activities?
// Sending
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("USER_ID", 42)
startActivity(intent)
// Receiving
val userId = intent.getIntExtra("USER_ID", -1)For complex objects, use Parcelable (faster on Android) or Serializable. Prefer @Parcelize annotation with the Kotlin plugin.
πΌ RecyclerView
How does RecyclerView work?
RecyclerView recycles View objects using the ViewHolder pattern:
RecyclerView.AdaptercreatesViewHolderobjects (inflating views).onBindViewHolder()binds data to the ViewHolder.- When an item scrolls off screen, its ViewHolder is put in a RecycledViewPool.
- New items are bound by reusing pooled ViewHolders β avoiding costly
inflate()calls.
Always use DiffUtil for efficient list updates instead of notifyDataSetChanged().
What is DiffUtil?
DiffUtil computes the difference between two lists and dispatches minimal update operations. It runs on a background thread with AsyncListDiffer or ListAdapter:
class MyAdapter : ListAdapter<Item, MyViewHolder>(ItemDiffCallback()) {
class ItemDiffCallback : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(a: Item, b: Item) = a.id == b.id
override fun areContentsTheSame(a: Item, b: Item) = a == b
}
}πΎ Storage
Room Database architecture
Room is a Jetpack abstraction over SQLite with three main components:
- Entity (
@Entity): Data class mapped to a DB table. - DAO (
@Dao): Interface with@Query,@Insert,@Update,@Deletemethods. - Database (
@Database): Abstract class extendingRoomDatabase, holds DAOs.
@Entity
data class User(@PrimaryKey val id: Int, val name: String)
@Dao
interface UserDao {
@Query("SELECT * FROM user") fun getAll(): Flow<List<User>>
@Insert suspend fun insert(user: User)
}DAO methods can be suspend functions or return Flow for reactive updates.
Serializable vs Parcelable
- Serializable: Java standard, uses reflection, slower, more GC pressure.
- Parcelable: Android-specific, manual marshaling, ~10x faster for IPC/Intent passing.
Use @Parcelize from Kotlin Android Extensions to auto-generate Parcelable:
@Parcelize
data class User(val id: Int, val name: String) : Parcelableπ‘ Services & Background Work
Types of Services
| Type | Description |
|---|---|
| Foreground Service | Visible to user via notification; survives app backgrounding |
| Background Service | Limited by OS on API 26+ |
| Bound Service | Client-server model; stops when all clients unbind |
WorkManager vs Service vs JobScheduler
- WorkManager: Recommended for guaranteed deferred background work (even after reboot). Supports chaining, constraints (network, battery), and backoff.
- Service: Real-time work while app is running.
- JobScheduler: System-level API for scheduled jobs (WorkManager uses it internally on Android 6+).
For most use cases, prefer WorkManager.
Broadcast Receivers
Components that respond to system-wide broadcast announcements:
- Registered in manifest (static) or
registerReceiver()(dynamic). - Examples:
ACTION_BOOT_COMPLETED,CONNECTIVITY_CHANGE,BATTERY_LOW. - Static receivers are restricted on API 26+ for implicit broadcasts.
Always unregister dynamic receivers in onStop() or onDestroy() to prevent leaks.
π Permissions
Runtime Permissions flow
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.CAMERA), REQUEST_CODE)
}
override fun onRequestPermissionsResult(code: Int, permissions: Array<String>, results: IntArray) {
if (results[0] == PackageManager.PERMISSION_GRANTED) { /* proceed */ }
}On API 30+, use ActivityResultContracts.RequestPermission() with the Activity Result API.