Activities & Intents (Java)

An Activity is a single, focused screen in your app. An Intent is a message object you use to start activities, services, or deliver broadcasts. Together, they drive navigation, data handoff, and interactions with other apps.

Big Picture

Activity

UI screen with lifecycle & state

Intent

“What to do” + optional data

Resolver

Finds matching component

Target

New screen / external app

onCreateonStartonResume(running)onPauseonStoponDestroy
Lifecycle intuition: UI becomes Visible at onStart(), becomes Interactive at onResume(). When a new screen partially covers it, you’ll get onPause(); if fully hidden, onStop().

Explicit vs Implicit Intents

TypeUse CaseHow it ResolvesExample
Explicit Navigate inside your app You specify the exact class new Intent(this, DetailActivity.class)
Implicit Ask system/other apps to handle an action Intent resolver matches action/data to registered intent-filters ACTION_SEND with text/plain to share text
// Explicit: open DetailActivity
Intent intent = new Intent(CurrentActivity.this, DetailActivity.class);
intent.putExtra("ITEM_ID", 42);
startActivity(intent);

// Implicit: share text
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("text/plain");
share.putExtra(Intent.EXTRA_TEXT, "Hello from CodingwithSonu!");
startActivity(Intent.createChooser(share, "Share via"));

Passing Data (Extras & Bundles)

Attach key-value pairs with putExtra (or a Bundle) and read them in the target activity.

// Sender
Intent i = new Intent(this, ProfileActivity.class);
i.putExtra("name", "Sonu");
i.putExtra("age", 28);
ArrayList<String> hobbies = new ArrayList<>();
hobbies.add("Android"); hobbies.add("Biking");
i.putStringArrayListExtra("hobbies", hobbies);
startActivity(i);

// Receiver (ProfileActivity)
String name = getIntent().getStringExtra("name");
int age = getIntent().getIntExtra("age", 0);
ArrayList<String> h = getIntent().getStringArrayListExtra("hobbies");
Tip: Prefer Parcelable models (over Serializable) for performance when sending complex objects.

Activity Result API (Modern way)

Instead of the old startActivityForResult(), use the Activity Result API to get results safely across configuration changes.

// In Activity (Java): register a launcher at field scope
private ActivityResultLauncher<Intent> pickContactLauncher =
    registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            Intent data = result.getData();
            // TODO: read selected contact from data
        }
    });

// Launch somewhere
Intent pick = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
pickContactLauncher.launch(pick);

Intent-Filters (Manifest)

Components advertise what they can handle. The resolver compares an implicit intent’s action/data/category to these.

<!-- In AndroidManifest.xml -->
<activity android:name=".ShareActivity"
          android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="image/*" />
  </intent-filter>
</activity>

Tasks & Back Stack (Essentials)

Each Activity belongs to a task (a stack). Pressing back pops the top Activity. Launch modes and flags control stack behavior.

Flag / ModeEffectCommon Use
FLAG_ACTIVITY_CLEAR_TOPClear above target if it existsReturn to existing instance
FLAG_ACTIVITY_NEW_TASKStart in a new taskLaunch from notification/service
singleTop (manifest)Reuse if already at topHome screens, detail screens
singleTask (manifest)One instance per taskMain entry points

Navigation Frequency (example)

Explicit intents (in-app)
Implicit intents (share/open)
Result APIs

* Illustrative only

Back Stack Complexity

Standard
singleTop
singleTask / flags

PendingIntent (for Notifications/Alarms)

A PendingIntent wraps an intent so other processes (e.g., NotificationManager, AlarmManager) can execute it on your behalf with your app’s identity.

// Create an explicit Intent for an Activity
Intent open = new Intent(this, DetailActivity.class);
open.putExtra("from", "notification");

PendingIntent contentPI = PendingIntent.getActivity(
    this, 1001, open,
    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);

Common Patterns

1) Open details with an ID

Intent i = new Intent(this, ProductDetailActivity.class);
i.putExtra("PRODUCT_ID", productId);
startActivity(i);

2) Share an image (file provider)

Uri uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", imageFile);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(share, "Share image"));

3) Open map direction (implicit VIEW)

Uri gmmIntentUri = Uri.parse("geo:0,0?q=India Gate, New Delhi");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
mapIntent.setPackage("com.google.android.apps.maps");
startActivity(mapIntent);

Error Handling & Safety

  • Intent may not resolve: Check with resolveActivity(getPackageManager()) before startActivity().
  • Large data in extras: Avoid huge bitmaps/arrays; use file URIs or a shared repository (DB).
  • Security: Be careful with exported=true. Only expose what’s necessary.
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("https://codingwithsonu.com"));
if (i.resolveActivity(getPackageManager()) != null) {
  startActivity(i);
} else {
  Toast.makeText(this, "No app can handle this action", Toast.LENGTH_SHORT).show();
}

Hands-On Exercise

  1. Create SecondActivity that displays a greeting from MainActivity.
  2. Add a button in MainActivity to open SecondActivity with name as extra.
  3. Use the Activity Result API to let SecondActivity return a resultMessage back.
// In MainActivity
private ActivityResultLauncher<Intent> launcher =
  registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), res -> {
    if (res.getResultCode() == RESULT_OK && res.getData() != null) {
      String msg = res.getData().getStringExtra("resultMessage");
      Toast.makeText(this, "From SecondActivity: " + msg, Toast.LENGTH_SHORT).show();
    }
  });

public void openSecond(View v) {
  Intent i = new Intent(this, SecondActivity.class);
  i.putExtra("name", "Sonu");
  launcher.launch(i);
}

// In SecondActivity
String name = getIntent().getStringExtra("name");
TextView tv = findViewById(R.id.tvHello);
tv.setText("Hello, " + name + "!");

public void finishWithResult(View v) {
  Intent data = new Intent();
  data.putExtra("resultMessage", "Nice to meet you.");
  setResult(RESULT_OK, data);
  finish();
}

Troubleshooting

“Activity class does not exist” ➜ Check package name & manifest registration.

No app handles implicit intent ➜ Guard with resolveActivity() and/or use createChooser().

Data lost on rotation ➜ Use onSaveInstanceState, ViewModel, or persistent storage.

App crashes on File URI ➜ Use FileProvider and grant URI permission.

Best Practices

  • Keep Activities thin; move logic to ViewModels/helpers.
  • Prefer explicit intents for in-app navigation.
  • Validate external intents; never trust incoming data blindly.
  • Use Activity Result API (avoid legacy request codes).
  • Minimize exported components; least-privilege principle.
Tip: For multi-screen flows, consider the Navigation Component with a single-activity architecture; it simplifies back stack and arguments passing.