UNIT 2: Core Android Components & UI Fundamentals
2.1 Activities & The Activity Lifecycle
An Activity is a single, focused task with a user interface. It is the entry point for interacting with your app.
The 7 Lifecycle Callbacks
The system calls these methods as an activity transitions through its states.
| Callback | When Called | Primary Purpose |
|---|---|---|
onCreate() |
Activity first created. | Initialize essential components (set content view, instantiate ViewModel). |
onStart() |
Activity becomes visible. | Prepare UI to become interactive (less common heavy work). |
onResume() |
Activity moves to foreground (user can interact). | Start animations, acquire exclusive resources (camera, sensors). |
onPause() |
Another activity comes in front (partially obscuring). | Pause/commit unsaved changes, release resources that don't need to run in background. Must be quick. |
onStop() |
Activity is no longer visible. | Release all resources not needed while not visible. |
onRestart() |
Activity is stopped, then becomes visible again. | Called before onStart() after a stop. |
onDestroy() |
Activity is finishing or system is destroying it. | Final cleanup (release all resources). |
Activity States & Process Death
-
Foreground (Resumed): Activity is at the top of the stack, user interacting.
-
Visible (Started but not Resumed): Activity is visible but not in front (e.g., dialog on top).
-
Background (Stopped): Activity is completely obscured. System may kill its process to reclaim memory.
-
State Restoration: If killed in background, system saves
onSaveInstanceState()Bundle(view hierarchy state, your custom data). When user returns,onCreate(Bundle savedInstanceState)andonRestoreInstanceState()are called to restore.
Implementing Lifecycle-Aware Code
-
Initialize in
onCreate():setContentView(),ViewModelsetup,RecyclerViewadapter. -
Start/Resume in
onResume(): Camera preview, sensor listeners, animations. -
Pause/Stop in
onPause()/onStop(): Stop animations, release camera/sensors, pause network calls. -
onSaveInstanceState(): Save UI state (scroll position, form text) into theoutStateBundle. Called beforeonStop()if activity may be killed. -
onRestoreInstanceState(): Restore state fromsavedInstanceStateBundle(afteronStart()). Alternatively, checksavedInstanceStateinonCreate().
Activity Launch Modes (launchMode)
Defined in AndroidManifest.xml for an <activity>. Controls how an activity is instantiated and placed in the back stack.
| Mode | Behavior | Back Stack Effect |
|---|---|---|
standard (default) |
New instance always created. | New instance pushed on top. |
singleTop |
If an instance of the activity is already at the top of the stack, reuse it (calls onNewIntent()). Else, new instance. |
Reuses top instance; no new entry. |
singleTask |
System searches for an existing instance in any task. If found, brings it to front (clears above it) and calls onNewIntent(). Else, new task created. |
Can be root of a task. Clears activities above it. |
singleInstance |
Like singleTask, but the activity is always the only one in its task. Any other activity launched from it opens in a new task. |
Always sole member of its task. |
[!TIP] Exam Focus: Know the lifecycle state diagram and which callback is best for specific resource management. Understand how
singleTaskvssingleInstanceaffect task/back stack behavior.
2.2 Intents & Intent Filters
Intents are messaging objects that request an action from another app component (Activity, Service, BroadcastReceiver).
Explicit vs. Implicit Intents
| Feature | Explicit Intent | Implicit Intent |
|---|---|---|
| Target | Specific component (class name). | General action for any capable app to handle. |
| Use Case | Navigating within your own app (e.g., MainActivity -> DetailActivity). |
Sharing content (ACTION_SEND), viewing a URL (ACTION_VIEW). |
| Creation | Intent(this, TargetActivity::class.java) |
Intent(Intent.ACTION_VIEW, uri) |
Intent Components
-
Action: What to do (
ACTION_VIEW,ACTION_SEND). -
Data/URI: The data to act on (
Uri.parse("https://...")). -
Category: Additional info about the component that should handle the intent (
CATEGORY_BROWSABLE). -
Extras: Key-value
Bundleof additional data (putExtra("key", value)). -
Flags: Control how the activity is launched (
FLAG_ACTIVITY_NEW_TASK).
Intent Filters (in AndroidManifest.xml)
Components (especially activities) advertise their capabilities via <intent-filter>.
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="www.example.com" />
</intent-filter>
-
<action>: Primary action the component performs. -
<data>: Data type (MIME type) and URI scheme/host/path. -
<category>: Additional classification.DEFAULTcategory is required forstartActivity(). -
Resolution: System matches Intent's Action, Data (URI & type), Category against all filters. If multiple matches, user chooses.
Passing Data & Getting Results
-
Passing Data:
intent.putExtra("name", value). Retrieve withintent.getStringExtra("name"). -
Returning Results (Legacy):
startActivityForResult(intent, requestCode). OverrideonActivityResult(requestCode, resultCode, data). -
Modern Approach (API 30+): Use
ActivityResultContracts.val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> // Handle returned Uri } button.setOnClickListener { getContent.launch("image/*") }
[!TIP] Common Pitfall: Forgetting
CATEGORY_DEFAULTin an implicit intent filter makes the component invisible tostartActivity(). Always handleActivityNotFoundExceptionfor implicit intents.
2.3 Fragments
Fragments modularize an activity's UI, enabling flexible layouts (e.g., single-pane phones, multi-pane tablets).
Fragment Lifecycle (vs. Activity)
Key difference: Fragment's view hierarchy has its own creation/destruction callbacks.
| Activity Callback | Fragment Callback | Note |
|---|---|---|
onCreate() |
onAttach() -> onCreate() |
onAttach(): Fragment attached to Activity. |
onCreate() |
onCreateView() |
Inflate fragment's layout. |
onStart() |
onViewCreated() |
View hierarchy created. Access views here. |
onResume() |
onStart() -> onResume() |
|
onPause() |
onPause() |
|
onStop() |
onStop() -> onDestroyView() |
onDestroyView(): View hierarchy destroyed. Clean up view references. |
onDestroy() |
onDestroy() -> onDetach() |
onDetach(): Fragment detached from Activity. |
Adding Fragments
-
Static (XML):
<fragment android:name="com.example.MyFragment" ... />in layout. Fixed at compile time. -
Dynamic (Recommended):
val fragment = MyFragment() supportFragmentManager.beginTransaction() .add(R.id.container, fragment) // or .replace() .addToBackStack(null) // Optional: add to back stack .commit()-
add(): Places fragment on top. -
replace(): Removes any existing fragments in container and adds new one. -
addToBackStack(): Transaction is saved; pressing BACK pops it (reverses replace/remove), not finishing activity.
-
Fragment Communication
-
Fragment → Activity: Define an interface in fragment, implemented by hosting activity. Callback in
onAttach(). -
Activity → Fragment:
findFragmentById(R.id.fragment)or useViewModel(preferred). -
Fragment ↔ Fragment: Communicate via shared
ViewModel(activity scope) or through the hosting activity.
[!TIP] Critical: Access views in
onViewCreated(), notonCreateView(). Clean up view references inonDestroyView()to prevent memory leaks. UseViewModelfor data sharing instead of direct fragment references.
2.4 UI Design: Layouts & Views
View: Basic UI widget (button, text field). ViewGroup: Container that holds Views and other ViewGroups.
Common Layout ViewGroups
| Layout | Key Features | Use Case |
|---|---|---|
LinearLayout |
Stacks children vertically or horizontally. layout_weight for proportional sizing. |
Simple lists, forms. Can be inefficient for deep nesting. |
RelativeLayout |
Positions children relative to siblings or parent (layout_below, layout_toRightOf). |
Complex relative positioning. Can be hard to maintain. |
ConstraintLayout (Modern Standard) |
Flat hierarchy. Position relative to any other view via constraints. Supports chains, barriers, bias, groups. | Replaces nested LinearLayout/RelativeLayout. Most flexible, performant. |
FrameLayout |
Simple stacking container. Children drawn in order (last on top). | Fragments container, overlays (e.g., badge on image). |
TableLayout |
Arranges children in rows and columns (TableRow). |
Tabular data. |
GridLayout |
Places children in a rectangular grid. | Simple grids. |
RecyclerView |
Advanced, efficient list/grid with view recycling. | Large, dynamic collections. (See 2.5) |
Common Widget Views
-
TextView,EditText,Button,ImageView. -
CheckBox,RadioButton(grouped inRadioGroup). -
Spinner(dropdown),ProgressBar(indeterminate/determinate). -
SeekBar,Switch,ToggleButton.
Units of Measurement
-
px(pixels): Actual screen pixels. Avoid—varies by screen density. -
dp(density-independent pixel): Standard for layout dimensions. Scales with screen density. -
sp(scale-independent pixel): Standard for font sizes. Scales with both density and user's font size preference.
[!TIP] Always use
dpfor layout sizes andspfor text sizes. UseConstraintLayoutto minimize nesting and improve performance.
2.5 UI Design: Adapters & RecyclerView
Adapters bridge a data source (e.g., List<T>) to a UI component that displays a collection.
RecyclerView vs. ListView
RecyclerView is the modern, more flexible replacement.
-
ViewHolder Pattern Mandatory: Forces view recycling, improving performance.
-
LayoutManager: Pluggable (
Linear,Grid,Staggered).ListViewonly vertical list. -
ItemAnimator: Built-in support for add/remove/change animations.
-
ItemDecoration: Easy way to add dividers/offsets.
RecyclerView Core Components
-
RecyclerView.Adapter: CreatesViewHolders and binds data to them. -
RecyclerView.ViewHolder: Holds references to item layout's views (nofindViewByIdinonBind). -
LayoutManager: Positions items (LinearLayoutManager,GridLayoutManager). -
ItemDecoration: Draws dividers, offsets around items.
Implementing a RecyclerView.Adapter
class MyAdapter(private val items: List<MyData>) :
RecyclerView.Adapter<MyAdapter.ViewHolder>() {
// 1. Inflate item layout & create ViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_layout, parent, false)
return ViewHolder(view)
}
// 2. Bind data to ViewHolder's views
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
holder.textView.text = item.name
// Set click listener on holder.itemView if needed
}
override fun getItemCount() = items.size
// 3. ViewHolder class
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.text_view)
}
}
Handling Item Clicks
Set listener in ViewHolder constructor or in onBindViewHolder() on holder.itemView.
[!TIP]
RecyclerViewhas noonItemClickListenerby design. You must implement it in theViewHolderor adapter. Always use theViewHolderpattern—never callfindViewByIdrepeatedly inonBindViewHolder.
2.6 Resources & Assets
Resources are external files (XML, images, etc.) that add to your app without code.
Resource Types & Directories (res/)
| Directory | Purpose | Access Example |
|---|---|---|
values/ |
XML files for strings, colors, dimensions, styles. | R.string.app_name, R.color.primary |
drawable/ |
Bitmap files (.png, .jpg), XML shapes (gradient, solid), selectors (state lists). |
R.drawable.ic_logo |
layout/ |
XML layout files. | R.layout.activity_main |
menu/ |
XML menu definitions. | R.menu.main_menu |
raw/ |
Arbitrary files (audio, video). Accessed as R.raw.filename. |
R.raw.sound |
xml/ |
Arbitrary XML config files (e.g., preferences). | R.xml.preferences |
Accessing Resources
-
In Code:
R.<type>.<name>(e.g.,getString(R.string.hello)). -
In XML:
@<type>/<name>(e.g.,android:text="@string/hello").
Alternative Resources & Configuration Qualifiers
Provide specialized resources for different device configurations.
-
Directory naming:
res/<resource_type>-<qualifier>/(e.g.,layout-land/for landscape,values-fr/for French,drawable-hdpi/for high-density screens). -
Selection: System picks the best-matching resource based on current device configuration.
Assets (assets/ folder)
-
Unlike
res/raw, files inassets/keep their original filenames and hierarchy. -
Accessed via
AssetManager:assets.open("filename.txt"). -
Use for non-resource files (e.g., a pre-populated database, HTML files).
[!TIP] Rule: Use
res/for resources the Android framework should know about (for automatic configuration matching). Useassets/for raw file bundles you'll read manually.
2.7 Themes & Styles
Theme: A collection of default attribute values applied application-wide or per-activity (in manifest). Defines overall look (colors, window background).
Style: A collection of attributes applied to a single View (via style="@style/MyStyle").
Inheritance
-
Style can inherit from another style:
parent="@style/Widget.AppCompat.Button". -
Theme can inherit from another theme:
parent="Theme.AppCompat.Light.NoActionBar".
Defining & Applying
<!-- res/values/styles.xml -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
</style>
<style name="RedTextStyle">
<item name="android:textColor">@android:color/holo_red_dark</item>
<item name="android:textSize">18sp</item>
</style>
<!-- AndroidManifest.xml -->
<application android:theme="@style/AppTheme" ... >
<activity android:theme="@style/AppTheme.NoActionBar" ... />
</application>
<!-- layout.xml -->
<TextView
style="@style/RedTextStyle"
... />
Common Theme Attributes
-
colorPrimary: Primary branding color (toolbar). -
colorPrimaryDark: Status bar color. -
colorAccent: Accent color for widgets (EditText cursor, Button text). -
android:windowBackground: Default window background. -
android:textColorPrimary: Primary text color.
[!TIP] Theme vs. Style: Theme = app/activity-level defaults. Style = view-level attribute bundle. A theme is essentially a style applied at the top level.
2.8 View Binding & Data Binding (Modern UI Patterns)
The Problem with findViewById()
-
Boilerplate:
TextView tv = findViewById(R.id.text); -
Type Safety: Returns
View, requires cast. Risk ofClassCastException. -
Null Risk:
findViewByIdreturnsnullif ID not in layout, causing NPE at runtime.
View Binding
-
What: Generates a type-safe binding class for each XML layout file.
-
Enable:
build.gradle→buildFeatures { viewBinding true }. -
Usage:
// For activity_main.xml -> ActivityMainBinding private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // Access views directly: binding.textViewHello.text = "Hi" } -
Advantages: Null-safe (binding class only contains views present in layout), no
findViewById, works withincludeand<merge>.
Data Binding (Advanced)
-
What: Binds data directly to layout XML via expressions. Enables MVVM pattern.
-
Enable:
buildFeatures { dataBinding true }. -
Layout Root: Must be
<layout>tag.<layout xmlns:...> <data> <variable name="user" type="com.example.User" /> </data> <LinearLayout ...> <TextView android:text="@{user.name}" /> </LinearLayout> </layout> -
Key Features:
-
Binding Expressions:
@{}for one-way binding. -
Two-way Binding:
@={}(e.g.,android:text="@={viewModel.input}"updates ViewModel on text change). -
Binding Adapters: Custom methods for complex logic (e.g.,
@BindingAdapter("imageUrl")to load image with Glide).
-
-
Comparison:
-
View Binding: Use for simple, direct view access. Minimal overhead.
-
Data Binding: Use for declarative UI with observable data (LiveData/Flow). More powerful, slight compile-time cost.
-
[!TIP] For most apps, start with View Binding. Migrate to Data Binding only when you need automatic UI updates from observable data (LiveData) or complex binding logic.
2.9 Handling Configuration Changes & Advanced Lifecycle
Configuration Changes
Events that change screen size, orientation, locale, etc. Default: Activity destroyed & recreated.
Handling Manually (Not Recommended for Most Cases)
In AndroidManifest.xml for the activity:
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
-
Effect: System does NOT destroy activity on these changes. Instead,
onConfigurationChanged()is called. -
Cons: You must manually handle all resource reloading (layouts, drawables). Easy to miss edge cases. Not future-proof.
-
Use Only For: Simple apps where recreation is truly disruptive (e.g., camera preview), or for specific performance-critical scenarios.
Preferred Approach: ViewModel
-
ViewModel(Android Architecture Component) is designed to retain UI-related data across configuration changes. -
It is lifecycle-aware and survives
onDestroy()/onCreate()of the activity/fragment. -
Usage:
class MyViewModel : ViewModel() { val data: LiveData<String> = ... // LiveData holds UI data } // In Activity/Fragment: val viewModel: MyViewModel by viewModels() viewModel.data.observe(this) { newData -> ... }
LiveData
-
Observable data holder. Lifecycle-aware: only notifies active observers (STARTED/RESUMED).
-
Prevents memory leaks and crashes from updating UI when not active.
-
Works seamlessly with
ViewModeland Data Binding.
[!TIP] Golden Rule: Never use
android:configChangesto avoid rotation recreation as a first resort. Always useViewModel+LiveData(or StateFlow) to survive configuration changes. This is the modern, robust pattern.
2.10 Debugging & Testing UI
Logcat
-
Android's logging system. Filter by tag, PID, or log level (
V,D,I,W,E). -
Use
Log.d("MyTag", "Debug message")in code. -
Tip: Create a custom
TAGconstant per class:private const val TAG = "MainActivity".
Android Studio Layout Inspector
-
Tool:
View > Tool Windows > Layout Inspector. -
Use: Inspect the live view hierarchy of a running app. See all views, their properties, constraints (for
ConstraintLayout), and layout bounds.
Layout Validation Tool
-
Tool:
Tools > Layout Validation. -
Use: Preview a single layout on multiple device configurations (screen sizes, orientations, API levels) simultaneously. Catches layout issues early.
Espresso (UI Testing)
Automated UI testing framework.
-
Core Concepts:
-
onView(): Find aViewin hierarchy. -
withId(R.id.button): Matcher to locate view. -
perform(): PerformViewAction(click, typeText). -
check(): PerformViewAssertion(matches, doesNotExist).
-
-
Example:
onView(withId(R.id.edit_text)).perform(typeText("Hello")); onView(withId(R.id.button)).perform(click()); onView(withText("Hello")).check(matches(isDisplayed())); -
IdlingResource: Tells Espresso when the app is idle (e.g., during async network calls). Prevents flaky tests.
[!TIP] Espresso Best Practice: Use
@RunWith(AndroidJUnit4::class)andActivityScenarioRuleto launch activity. Always useIdlingResourceorCountingIdlingResourcefor background operations.