Topic 3: Android Application Components required to make an Application
📖 13 min read · 🎯 beginner · 🧭 Prerequisites: android-environment-set-up-hello-world, introduction-to-android-os-java-programming-language
Why this matters
Here's the thing — every Android app you've ever tapped on your phone, whether it's WhatsApp, YouTube, or a simple calculator, is built from the same five building blocks: Activities, Services, Broadcast Receivers, Content Providers, and Intents. That's it. Before you write a single line of code, you need to know what these components are and what role each one plays. Skip this, and you'll keep getting lost wondering why your app isn't working. Understand it, and suddenly Android development has a clear structure — and you'll know exactly where to look when something breaks.
What You'll Learn
- What each of the five core Android application components does and when to use it
- How the Activity lifecycle controls what users see and when
- How Services run background work without a UI, and how Broadcast Receivers react to system-wide events
- How Content Providers share structured data between apps via the ContentResolver
- How Intents — both explicit and implicit — wire all the components together
The Analogy
Think of an Android app as a busy city department. The Activity is the front desk — it's the face citizens interact with, open for business during the day. The Service is the back-office staff running overnight batch processes that citizens never see. Broadcast Receivers are the city's emergency alert system — when the siren sounds (say, "boot completed"), every registered listener wakes up and responds. The Content Provider is the central records office: other departments may request access to files, but only through the official window (the ContentResolver), with proper permissions. And Intents are the memos that travel between departments, either addressed to a specific office (explicit) or posted on the general board for whoever can handle it (implicit).
Chapter 1: Activities
Overview
An Activity represents a single screen with a user interface. Activities are the entry points for interacting with the user and are essential for any Android application. Each activity can contain multiple fragments and handle user interactions.
Key Points
- Activities have a lifecycle with methods:
onCreate(),onStart(),onResume(),onPause(),onStop(), andonDestroy(). - Activities can start other activities using Intents.
- The main activity is specified in the
AndroidManifest.xmlfile.
Lifecycle Diagram
stateDiagram-v2
[*] --> onCreate
onCreate --> onStart
onStart --> onResume
onResume --> onPause : user navigates away
onPause --> onResume : user returns
onPause --> onStop
onStop --> onRestart : user returns
onRestart --> onStart
onStop --> onDestroy
onDestroy --> [*]
Example
MainActivity.java
package com.example.myapp;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
button.setOnClickListener(v -> {
// Handle button click
});
}
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me" />
</LinearLayout>
Chapter 2: Services
Overview
A Service is a component that performs long-running operations in the background. Services do not provide a user interface — they can be started and stopped by activities or other components, running silently behind the scenes.
Key Points
- Services can run in the background even if the user switches to another application.
- Started services perform a single operation (e.g., downloading a file) then stop themselves.
- Bound services provide a client-server interface, allowing components to interact with them while they run.
Example
MyService.java
package com.example.myapp;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Service Created");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service Started");
new Thread(() -> {
for (int i = 0; i < 5; i++) {
Log.d(TAG, "Running task " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
stopSelf();
}).start();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service Destroyed");
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Register the service in AndroidManifest.xml:
<service android:name=".MyService" />
START_STICKY tells the OS to restart the service automatically if it is killed while running — important for long-lived background work.
Chapter 3: Broadcast Receivers
Overview
A Broadcast Receiver is a component that responds to system-wide broadcast announcements. It can listen for specific system events (like device boot or network changes) or custom broadcasts sent by your own app or third-party apps.
Key Points
- Broadcast Receivers do not display a user interface.
- They can be registered statically in
AndroidManifest.xml(active even when the app is not running) or dynamically at runtime viaregisterReceiver(). - They handle broadcasts from the system or other applications.
Example
MyBroadcastReceiver.java
package com.example.myapp;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Broadcast Received!", Toast.LENGTH_LONG).show();
}
}
Register it statically in AndroidManifest.xml to fire on device boot:
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Chapter 4: Content Providers
Overview
A Content Provider manages access to a structured set of data. It encapsulates the data and provides mechanisms for defining data security. Content Providers are used to share data between applications — for example, the Android Contacts app exposes its data through a Content Provider that any app with permission can query.
Key Points
- Content Providers provide a standard interface to access data via URIs.
- They can read and write data stored in databases, files, or other storage mechanisms.
- Data access and modification happens through the ContentResolver.
- URI matching (
UriMatcher) maps incoming URI patterns to the correct query logic.
Example
MyContentProvider.java — a full SQLite-backed provider:
package com.example.myapp;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class MyContentProvider extends ContentProvider {
public static final String AUTHORITY = "com.example.myapp.provider";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/items");
private static final int ITEMS = 100;
private static final int ITEM_ID = 101;
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI(AUTHORITY, "items", ITEMS);
uriMatcher.addURI(AUTHORITY, "items/#", ITEM_ID);
}
private MyDatabaseHelper dbHelper;
@Override
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext());
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
SQLiteDatabase database = dbHelper.getReadableDatabase();
Cursor cursor;
switch (uriMatcher.match(uri)) {
case ITEMS:
cursor = database.query("items", projection, selection, selectionArgs,
null, null, sortOrder);
break;
case ITEM_ID:
selection = "_id=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
cursor = database.query("items", projection, selection, selectionArgs,
null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri)) {
case ITEMS:
return "vnd.android.cursor.dir/" + AUTHORITY + ".items";
case ITEM_ID:
return "vnd.android.cursor.item/" + AUTHORITY + ".items";
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
long id = database.insert("items", null, values);
if (id > 0) {
Uri returnUri = ContentUris.withAppendedId(CONTENT_URI, id);
getContext().getContentResolver().notifyChange(uri, null);
return returnUri;
} else {
throw new SQLException("Failed to insert row into " + uri);
}
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection,
@Nullable String[] selectionArgs) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
int rowsDeleted;
switch (uriMatcher.match(uri)) {
case ITEMS:
rowsDeleted = database.delete("items", selection, selectionArgs);
break;
case ITEM_ID:
selection = "_id=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
rowsDeleted = database.delete("items", selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
if (rowsDeleted != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsDeleted;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values,
@Nullable String selection, @Nullable String[] selectionArgs) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
int rowsUpdated;
switch (uriMatcher.match(uri)) {
case ITEMS:
rowsUpdated = database.update("items", values, selection, selectionArgs);
break;
case ITEM_ID:
selection = "_id=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
rowsUpdated = database.update("items", values, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
if (rowsUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsUpdated;
}
private static class MyDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "mydatabase.db";
private static final int DATABASE_VERSION = 1;
MyDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
final String CREATE_TABLE = "CREATE TABLE items (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT NOT NULL);";
db.execSQL(CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS items");
onCreate(db);
}
}
}
Register the provider in AndroidManifest.xml:
<provider
android:name=".MyContentProvider"
android:authorities="com.example.myapp.provider"
android:exported="true"
android:grantUriPermissions="true" />
android:exported="true" is required to allow other apps to access your provider. android:grantUriPermissions="true" lets your app grant temporary access to specific URIs to other apps on a per-request basis.
Chapter 5: Intents — the Messenger Between Components
Overview
Intents are messaging objects used to request actions from other app components. An Intent can start an Activity, start a Service, or deliver a broadcast. They are the glue that holds all the other four components together.
Key Points
- Explicit Intents: Directly specify the component to start by class name. Used when you know exactly which component should handle the request (within your own app).
- Implicit Intents: Do not specify a component. Instead, they declare a general action to perform, and the Android system finds the right component (in your app or another) that can handle it.
Example: Starting an Activity with an Explicit Intent
MainActivity.java — sends the user to SecondActivity on button press:
package com.example.myapp;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
button.setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
});
}
}
SecondActivity.java — the destination screen:
package com.example.myapp;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
}
}
Component Communication Map
flowchart LR
A[Activity] -- Explicit Intent --> B[Activity]
A -- startService Intent --> C[Service]
A -- sendBroadcast Intent --> D[BroadcastReceiver]
A -- ContentResolver --> E[ContentProvider]
C -- sendBroadcast --> D
D -- startActivity --> A
🧪 Try It Yourself
Task: Add a second screen to a basic Android project using an explicit Intent.
- In Android Studio, open (or create) a project with a
MainActivity. - Create a new empty Activity called
SecondActivity(File → New → Activity → Empty Activity). - In
activity_main.xml, add a Button withandroid:id="@+id/btnGoToSecond"and text"Go to Second Screen". - In
MainActivity.java, wire the button to launchSecondActivity:
Button btnGoToSecond = findViewById(R.id.btnGoToSecond);
btnGoToSecond.setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
});
- In
activity_second.xml, add aTextViewwith the text"Welcome to the Second Screen!".
Success criterion: Run the app. Tap the button — you should navigate to the second screen showing your welcome message. Press the back button to return to MainActivity.
🔍 Checkpoint Quiz
Q1. Which Activity lifecycle method is called first when an activity becomes visible to the user after being created?
A) onResume()
B) onStart()
C) onCreate()
D) onRestart()
Q2. Given this service definition, what value does onStartCommand return, and what does it mean?
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// ... background work ...
return START_STICKY;
}
A) START_STICKY means the service will never restart after being killed.
B) START_STICKY means the OS will restart the service after it is killed, but will not re-deliver the original intent.
C) START_STICKY means the service stops itself after completing the task.
D) START_STICKY means the service runs only while an activity is bound to it.
Q3. A developer registers a BroadcastReceiver in AndroidManifest.xml with the action android.intent.action.BOOT_COMPLETED. When exactly does the onReceive() method get called?
A) Every time the user opens the app B) When the device finishes booting C) When the app's main activity is destroyed D) When the user connects to Wi-Fi
Q4. You are building a notes app and want to expose your notes database so that a widget on the home screen (a separate process) can read the latest note. Which Android component is the correct tool, and what class does the widget use to talk to it?
Open-ended — write your answer before checking.
A1. B) onStart() — onCreate() runs first internally, but onStart() is the call that marks the activity as visible. onResume() comes after, indicating the activity is in the foreground and interactive.
A2. B) START_STICKY tells the Android OS to restart the service after it is killed due to memory pressure. The OS will call onStartCommand() again, but the Intent parameter will be null because the original intent is not re-delivered. Use START_REDELIVER_INTENT if you need the original intent back.
A3. B) onReceive() is triggered when the device finishes booting and the system sends the BOOT_COMPLETED broadcast. This is commonly used to restart background services or schedule alarms after a reboot.
A4. The correct component is a Content Provider (MyContentProvider extends ContentProvider). The widget (running in a separate process) uses the ContentResolver — retrieved via getContext().getContentResolver() — to query the provider's URI (e.g., content://com.example.notesapp.provider/notes). The provider handles all SQLite access and returns a Cursor with the data.
🪞 Recap
- An Activity is a single UI screen; its lifecycle (
onCreate→onDestroy) controls what is shown and when. - A Service runs background tasks with no UI; returning
START_STICKYfromonStartCommandkeeps it alive through system restarts. - A Broadcast Receiver listens for system-wide or custom events, registered either statically in the manifest or dynamically at runtime.
- A Content Provider exposes structured data (typically SQLite) to other apps or processes via a standardized URI interface and the
ContentResolver. - Intents are the messaging glue of Android — explicit intents target a specific component by class, implicit intents describe an action and let the system route them.
📚 Further Reading
- Android Application Fundamentals — official docs — the source of truth on all four component types
- Activity Lifecycle — official docs — detailed diagrams and edge cases for every lifecycle state
- Services Overview — official docs — started vs. bound services, foreground services, WorkManager alternatives
- Content Provider Basics — official docs — URI design, MIME types, and permissions in depth
- Intents and Intent Filters — official docs — implicit intent resolution, intent flags, and data URIs
- ⬅️ Previous: Introduction to Android OS & Java Programming Language
- ➡️ Next: Java Day I: Introduction to OOPs