One screen is rarely enough. A login screen hands the username to a profile, a product list hands the chosen item to a details page, a quiz hands the final score to a results screen. In Android, that hand-off uses an Intent, and the data rides inside a Bundle.
An Intent is a message asking Android to do something — most often "start this Activity". You create it, optionally attach data, and hand it over.
Intent intent = new Intent(MainActivity.this, ResultActivity.class);
startActivity(intent);Two arguments: the current context (MainActivity.this) and the class of the Activity you want to open.
Before any of this works, the second Activity must exist in your manifest. Android Studio adds this automatically when you create an Activity through the menus, but if you wrote the class by hand you have to add it yourself:
<application ... >
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Every activity must be declared -->
<activity android:name=".ResultActivity" />
</application>Forgetting this gives you an ActivityNotFoundException at runtime.
Note that ResultActivity has no <intent-filter>. That block marks an entry point for the app — a launcher icon. A screen you reach from inside your own app does not need one, and adding a second LAUNCHER filter by mistake gives your app two icons in the app drawer.
Attach values to the Intent before starting the Activity:
Intent intent = new Intent(MainActivity.this, ResultActivity.class);
intent.putExtra("STUDENT_NAME", "Amir");
intent.putExtra("SCORE", 8);
startActivity(intent);Each putExtra() is a key–value pair. The key is a string you choose; it is how the second Activity will ask for the value back.
The values arrive in a Bundle:
public class ResultActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_result);
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
String name = bundle.getString("STUDENT_NAME");
int score = bundle.getInt("SCORE");
TextView textResult = findViewById(R.id.textResult);
textResult.setText("Well done, " + name + "! Score: " + score);
}
}
}getIntent() returns the Intent that started this Activity; getExtras() gives you the Bundle of everything packed into it.
Mismatched keys. The key must be identical on both sides, character for character. "SCORE" and "Score" are different keys.
The failure is nastier than a crash: a missing key returns null for objects, or the default value for primitives. So getInt("Score") quietly returns 0 and your app shows a score of zero with no error at all.
The fix is to declare keys as constants rather than typing the string twice:
public class ResultActivity extends AppCompatActivity {
public static final String EXTRA_NAME = "STUDENT_NAME";
public static final String EXTRA_SCORE = "SCORE";
// ...
}intent.putExtra(ResultActivity.EXTRA_NAME, "Amir");
intent.putExtra(ResultActivity.EXTRA_SCORE, 8);Now a typo is a compile error instead of a silent wrong answer.
Mismatched types. Put an int, get an int. Calling getString() on a key you stored with an int returns null, not "8". The getter must match the putter:
| Put | Get |
|---|---|
putExtra(key, "text") | getString(key) |
putExtra(key, 8) | getInt(key) |
putExtra(key, 3.5) | getDouble(key) |
putExtra(key, true) | getBoolean(key) |
You can also pass a default for the missing case: bundle.getInt("SCORE", -1) returns -1 rather than 0 when the key is absent, which makes the failure visible.
Sometimes the second screen needs to send something back — a picked date, a confirmation. The modern approach is the Activity Result API.
In the first Activity, register a launcher:
private final ActivityResultLauncher<Intent> resultLauncher =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == RESULT_OK
&& result.getData() != null) {
String answer = result.getData()
.getStringExtra("ANSWER");
textStatus.setText("You chose: " + answer);
}
}
});Launch it instead of startActivity():
Intent intent = new Intent(MainActivity.this, ChoiceActivity.class);
resultLauncher.launch(intent);And in the second Activity, send the answer back before finishing:
Intent data = new Intent();
data.putExtra("ANSWER", "True");
setResult(RESULT_OK, data);
finish();registerForActivityResult() must be called during Activity setup — as a field initialiser or in onCreate() — not inside a click listener. Older tutorials use startActivityForResult() and onActivityResult(); that pair still works but is deprecated.
Everything above uses an explicit intent — you name the exact class to open.
An implicit intent describes what you want done and lets Android find an app that can do it:
// Open a web page in whatever browser the user has
Intent browse = new Intent(Intent.ACTION_VIEW,
Uri.parse("https://developer.android.com"));
startActivity(browse);
// Share some text
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("text/plain");
share.putExtra(Intent.EXTRA_TEXT, "My quiz score: 8/10");
startActivity(Intent.createChooser(share, "Share your score"));Use explicit intents inside your own app, implicit ones to hand work to other apps.
putExtra() attaches key–value dataAndroidManifest.xmlLAUNCHER intent-filtergetIntent().getExtras() and null-check the Bundle