Android Fundamentals with Java

Intents & Starting Activities

18 min Lesson 9 of 12

Intents & Starting Activities

Android applications rarely consist of a single screen. The mechanism that moves a user from one screen to another — and that lets the Android OS route requests between components — is the Intent. Understanding how to construct and fire an Intent is the most fundamental navigation skill in Android development.

What Is an Intent?

An Intent is a messaging object. It describes an action the application wants to perform. When you want to open a second Activity, you create an Intent that names that Activity as the target, optionally attach data to pass along, and hand the Intent to the OS. The OS looks up the target, verifies it is declared in the manifest, launches or resumes it, and delivers the Intent.

Two flavours of Intent: An explicit Intent names a specific component by class — always use this for navigation within your own app. An implicit Intent declares an action (e.g. ACTION_VIEW with a URL) and lets the OS find any app that can handle it. This lesson focuses exclusively on explicit Intents.

Creating an Explicit Intent

The canonical constructor takes a Context and the .class of the destination Activity:

Intent intent = new Intent(this, DetailActivity.class); startActivity(intent);

this refers to the current Activity, which implements Context. DetailActivity.class is the compile-time reference to the target. The OS will start DetailActivity, calling its onCreate() (or onRestart() / onResume() if an existing instance is reused, depending on the launch mode).

Passing Data with Extras

Intents carry a key-value store called the extras bundle. Use putExtra(key, value) to attach data before firing the Intent, and the matching getXxxExtra(key, defaultValue) in the destination to retrieve it.

// In the launching Activity (e.g. MainActivity.java) Intent intent = new Intent(this, DetailActivity.class); intent.putExtra("product_id", 42); intent.putExtra("product_name", "Wireless Keyboard"); startActivity(intent);
// In DetailActivity.java — inside onCreate() int productId = getIntent().getIntExtra("product_id", -1); String name = getIntent().getStringExtra("product_name"); if (productId == -1) { // guard: caller forgot to pass the ID finish(); return; } titleTextView.setText(name);
Define extra keys as constants. Declare them as public static final String fields on the receiving Activity. That way the caller imports the constant and there is no chance of a typo mismatch at runtime.
// DetailActivity.java public class DetailActivity extends AppCompatActivity { public static final String EXTRA_PRODUCT_ID = "product_id"; public static final String EXTRA_PRODUCT_NAME = "product_name"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_detail); int id = getIntent().getIntExtra(EXTRA_PRODUCT_ID, -1); String n = getIntent().getStringExtra(EXTRA_PRODUCT_NAME); // ... } } // MainActivity.java — launch site Intent intent = new Intent(this, DetailActivity.class); intent.putExtra(DetailActivity.EXTRA_PRODUCT_ID, selectedItem.getId()); intent.putExtra(DetailActivity.EXTRA_PRODUCT_NAME, selectedItem.getName()); startActivity(intent);

The Back Stack

Every time you call startActivity(), Android pushes the new Activity onto the back stack of the current task. Pressing the system Back button or calling finish() pops the top Activity and returns to the one underneath. This gives the user a coherent navigation history without any extra code on your part.

// To return to the calling screen programmatically: finish(); // pops this Activity off the stack

Getting a Result Back — ActivityResultLauncher

Sometimes the second screen exists to collect information that the first screen needs — for example a filter dialog, an image picker, or a login screen. The modern API for this is ActivityResultLauncher, introduced as a type-safe replacement for the deprecated startActivityForResult().

// In the launching Activity — declare at class level private ActivityResultLauncher<Intent> pickColorLauncher; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Register BEFORE the Activity is started (must be in onCreate) pickColorLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == RESULT_OK && result.getData() != null) { String color = result.getData().getStringExtra("chosen_color"); applyColor(color); } } ); findViewById(R.id.btnPickColor).setOnClickListener(v -> { Intent intent = new Intent(this, ColorPickerActivity.class); pickColorLauncher.launch(intent); }); }
// In ColorPickerActivity.java — return data to the caller Intent result = new Intent(); result.putExtra("chosen_color", "#FF6200EE"); setResult(RESULT_OK, result); finish();
Always register the launcher in onCreate(), never inside a click listener. Registration must happen before the Activity enters the STARTED state. If you call registerForActivityResult() inside setOnClickListener() you will get an IllegalStateException at runtime when the button is tapped after the Activity is already started.

Putting It All Together — A Two-Screen Flow

Here is a minimal but complete example of MainActivity launching ProfileActivity and passing a username string:

// MainActivity.java public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnOpen = findViewById(R.id.btnOpenProfile); btnOpen.setOnClickListener(v -> { Intent intent = new Intent(MainActivity.this, ProfileActivity.class); intent.putExtra(ProfileActivity.EXTRA_USERNAME, "alice"); startActivity(intent); }); } }
// ProfileActivity.java public class ProfileActivity extends AppCompatActivity { public static final String EXTRA_USERNAME = "username"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_profile); String username = getIntent().getStringExtra(EXTRA_USERNAME); TextView tvName = findViewById(R.id.tvUsername); tvName.setText(username != null ? username : "Unknown"); } }

Both Activities must be declared in AndroidManifest.xml (covered in Lesson 6). If ProfileActivity is missing from the manifest the OS will throw ActivityNotFoundException at runtime.

Summary

An explicit Intent is the standard way to navigate between Activities in Android. Create one with new Intent(context, Target.class), attach any data you need via putExtra(), and call startActivity(). When the second screen needs to return data to the first, use ActivityResultLauncher registered in onCreate(). The back stack handles history automatically. In the final lesson of this tutorial you will bring all these pieces together to build a complete two-screen application.