嘿,朋友!看到你在探索 Android 开发的基石——Activity 和 Intent。别担心,这俩东西听起来高大上,其实就像日常生活中的”快递员”和”收件人”。今天咱们不整那些枯燥的教科书式讲解,我用最接地气的方式,带你把这套核心机制彻底搞明白。
为什么 Activity 和 Intent 是 Android 的”心脏”?
想象一下,你是一个餐厅老板(开发一个 App),你的厨房里有各种菜(功能模块)。Activity 就是你的”服务员”——每个 Activity 代表一个屏幕或一个界面。而 Intent 就是”点单小纸条”,告诉服务员:我要去厨房做红烧肉(启动另一个 Activity),或者帮我送外卖(发送数据给外部应用)。
我见过太多新手在这里卡壳,不是因为难,而是因为没人把”为什么”讲清楚。今天我就用真实项目和代码例子,让你边看边动手,保证你学会后能自己设计各种页面跳转逻辑。
第一部分:Activity 的生死日记——从创建到销毁
先别急着写代码,咱们得理解 Activity 到底是什么。Activity 是 Android 四大组件之一,它代表一个用户界面屏幕。每个 Activity 都是一个独立的窗口,管理自己的生命周期(从出生到死亡)。
生命周期详解:Activity 的”八步人生”
我带过不少学员,他们最容易混淆的就是生命周期方法。其实很简单,记住这个比喻:Activity 就像一个人的日常生活。
- ** onCreate() **——出生时刻。Activity 第一次被创建,你在这里初始化一切:设置布局、绑定数据、读取配置。这就像你刚出生,父母给你起名、办户口。
- ** onStart() **——准备出门。Activity 即将变得可见,但还没和用户交互。就像你穿上衣服,准备出门见朋友。
- ** onResume() **——开始社交。Activity 进入前台,和用户交互。这是最活跃的时期,你的 App 正在被使用。
- ** onPause() **——临时离开。另一个 Activity 覆盖了当前 Activity,但当前 Activity 还部分可见(比如弹出对话框)。你在这里保存数据、暂停动画。
- ** onStop() **——完全隐藏。Activity 不再可见,用户可能切换到了别的 App。你在这里释放一些资源,比如停止后台服务。
- ** onDestroy() **——生命终结。Activity 被销毁,彻底从内存中移除。你在这里清理所有资源。
- ** onRestart() **——死而复生。Activity 从停止状态重新启动,准备再次显示。
- ** onRestoreInstanceState() **——找回记忆。系统恢复之前的状态,比如屏幕旋转后找回输入框内容。
实战代码:在日志中追踪生命周期
来,咱们写点代码,亲眼看看这些方法怎么被调用。创建一个新的 Android 项目,在 MainActivity 中添加以下代码:
package com.example.activitylifecycle;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private static final String TAG = "ActivityLifecycle";
private TextView lifecycleText;
private int resumeCount = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lifecycleText = findViewById(R.id.lifecycleText);
Button nextButton = findViewById(R.id.nextButton);
Log.d(TAG, "onCreate: Activity 出生啦!");
if (savedInstanceState != null) {
Log.d(TAG, "onCreate: 恢复状态,之前停过");
}
nextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 使用 Intent 跳转到 SecondActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
updateLifecycleText("已创建");
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart: 准备可见");
}
@Override
protected void onResume() {
super.onResume();
resumeCount++;
Log.d(TAG, "onResume: 开始交互,第" + resumeCount + "次");
updateLifecycleText("运行中 (第" + resumeCount + "次)");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause: 临时离开,保存状态");
updateLifecycleText("暂停 - 保存中...");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop: 完全隐藏");
updateLifecycleText("停止 - 不可见");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart: 死而复生");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: 生命终结,清理资源");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d(TAG, "onRestoreInstanceState: 找回记忆");
}
private void updateLifecycleText(String status) {
lifecycleText.setText("当前状态: " + status);
}
}
对应的 XML 布局 activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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"
android:background="#F5F5F5">
<TextView
android:id="@+id/lifecycleText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="初始状态"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center"
android:padding="20dp"
android:background="#FFFFFF"
android:elevation="4dp" />
<Button
android:id="@+id/nextButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="跳转到第二个页面"
android:textSize="16sp"
android:padding="12dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="提示:观察 Logcat 中的生命周期日志\n点击按钮后按返回键,看看发生了什么"
android:textSize="14sp"
android:textColor="#666666"
android:lineSpacingMultiplier="1.2" />
</LinearLayout>
现在运行 App,打开 Logcat 过滤器为 ActivityLifecycle,然后:
- 点击按钮跳转到第二个 Activity
- 按返回键回到主界面
- 旋转屏幕
- 按 Home 键回到桌面
- 重新打开 App
你会看到日志中清晰地记录了每个生命周期的变化。记住这个实验,以后遇到问题,先看日志!
第二部分:Intent 的快递艺术——三种配送方式
Intent(意图)是 Android 中对象,用来表达”我想做什么”。它是 Activity 之间通信的桥梁,也是 App 间通信的纽带。I ent 就像快递员,有三种配送方式:
1. 显式 Intent:直送上门
最直接的配送方式。你明确告诉系统:我要去 A 页面。这就像你给朋友发短信说”我去你家了”,对方必须知道你要去他家。
适用场景:App 内部的页面跳转,比如从首页跳转到详情页。
// 方式一:通过类对象直接指定
Intent intent = new Intent(this, DetailActivity.class);
startActivity(intent);
// 方式二:构造函数指定组件
ComponentName component = new ComponentName(this, SettingActivity.class);
Intent intent = new Intent();
intent.setComponent(component);
startActivity(intent);
2. 隐式 Intent:广播寻找
你告诉系统:我要做某件事,谁合适谁就来。系统会查找所有能处理这个意图的组件,如果有多个候选,会弹出选择框让用户决定。
适用场景:跨 App 操作,比如发送短信、打开网页、选择图片。
// 示例1:打开网页
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("https://www.example.com"));
startActivity(intent);
// 示例2:发送短信
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("smsto:13800138000")); // 目标号码
intent.putExtra("sms_body", "你好,这是测试短信");
startActivity(intent);
// 示例3:选择图片(调用系统相册)
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE);
对应的 AndroidManifest.xml 中如何声明接收隐式 Intent:
<!-- 声明能处理特定意图的 Activity -->
<activity android:name=".WebBrowserActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
</activity>
3. 粘性 Intent:老朋友的快递
这种 Intent 会”粘”在系统中,即使接收者还没准备好,之后也可以收到。不过要注意,粘性 Intent 在现代 Android 版本中限制越来越严,建议谨慎使用。
// 发送粘性 Intent(需要权限)
Intent stickyIntent = new Intent("com.example.MY_ACTION");
stickyIntent.putExtra("key", "value");
sendStickyBroadcast(stickyIntent);
// 接收粘性 Intent
Intent intent = registerReceiver(null,
new IntentFilter("com.example.MY_ACTION"));
第三部分:实战项目——完整电商 App 的页面流转
理论说得再多,不如动手做一个完整项目。我带你做一个简化版的电商 App,包含以下页面:
- 登录页面(LoginActivity)
- 主页面(MainActivity,显示商品列表)
- 商品详情(ProductDetailActivity)
- 购物车页面(CartActivity)
- 设置页面(SettingsActivity)
项目结构:
com.example.ecommerce/
├── LoginActivity.java
├── MainActivity.java
├── ProductDetailActivity.java
├── CartActivity.java
├── SettingsActivity.java
├── model/
│ └── Product.java
├── res/layout/
│ ├── activity_login.xml
│ ├── activity_main.xml
│ ├── activity_product_detail.xml
│ ├── activity_cart.xml
│ └── activity_settings.xml
└── AndroidManifest.xml
1. 数据模型:Product.java
package com.example.ecommerce.model;
public class Product {
private int id;
private String name;
private double price;
private String description;
private String imageUrl;
public Product(int id, String name, double price, String description, String imageUrl) {
this.id = id;
this.name = name;
this.price = price;
this.description = description;
this.imageUrl = imageUrl;
}
// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getImageUrl() { return imageUrl; }
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
}
2. 登录页面:LoginActivity.java
package com.example.ecommerce;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class LoginActivity extends AppCompatActivity {
private EditText etUsername;
private EditText etPassword;
private Button btnLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
etUsername = findViewById(R.id.etUsername);
etPassword = findViewById(R.id.etPassword);
btnLogin = findViewById(R.id.btnLogin);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
// 跳转设置页面(隐式 Intent 示例)
Button btnSettings = findViewById(R.id.btnSettings);
btnSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClassName("com.example.ecommerce",
"com.example.ecommerce.SettingsActivity");
startActivity(intent);
}
});
}
private void login() {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "用户名和密码不能为空", Toast.LENGTH_SHORT).show();
return;
}
// 模拟登录验证
if ("admin".equals(username) && "123456".equals(password)) {
// 登录成功,跳转到主页面
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("username", username); // 传递用户名
startActivity(intent);
finish(); // 关闭登录页面
} else {
Toast.makeText(this, "用户名或密码错误", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onResume() {
super.onResume();
// 每次回到登录页,清空输入框
etUsername.setText("");
etPassword.setText("");
}
}
3. 主页面:MainActivity.java(商品列表)
”`java package com.example.ecommerce;
import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.example.ecommerce.model.Product; import java.util.ArrayList; import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE_PRODUCT_DETAIL = 1001;
private static final int REQUEST_CODE_CART = 1002;
private List<Product> products;
private LinearLayout productsContainer;
private TextView tvUsername;
private Button btnCart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取登录时传递的用户名
String username = getIntent().getStringExtra("username");
if (username != null) {
tvUsername = findViewById(R.id.tvUsername);
tvUsername.setText("欢迎, " + username);
}
// 初始化商品数据
initProducts();
// 商品列表容器
productsContainer = findViewById(R.id.productsContainer);
renderProducts();
// 购物车按钮
btnCart = findViewById(R.id.btnCart);
btnCart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 使用 startActivityForResult 打开购物车
Intent intent = new Intent(MainActivity.this, CartActivity.class);
startActivityForResult(intent, REQUEST_CODE_CART);
}
});
}
private void initProducts() {
products = new ArrayList<>();
products.add(new Product(1, "iPhone 15", 7999.00,
"苹果最新旗舰手机,A17 Pro 芯片", "https://example.com/iphone.jpg"));
products.add(new Product(2, "MacBook Pro", 12999.00,
"专业级笔记本,M3 芯片", "https://example.com/macbook.jpg"));
products.add(new Product(3, "AirPods Pro", 1899.00,
"主动降噪耳机,空间音频", "https://example.com/airpods.jpg"));
products.add(new Product(4, "iPad Air", 4799.00,
"轻薄便携,M1 芯片", "https://example.com/ipad.jpg"));
}
private void renderProducts() {
productsContainer.removeAllViews();
for (final Product product : products) {
// 为每个商品创建卡片布局
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.VERTICAL);
card.setPadding(16, 16, 16, 16);
card.setBackgroundTintList(android.content.res.ColorStateList.valueOf(0xFFFFFFFF));
TextView tvName = new TextView(this);
tvName.setText(product.getName());
tvName.setTextSize(18);
tvName.setTextColor(0xFF000000);
tvName.setPadding(0, 0, 0, 8);
TextView tvPrice = new TextView(this);
tvPrice.setText("¥" + String.format("%.2f", product.getPrice()));
tvPrice.setTextSize(16);
tvPrice.setTextColor(0xFFFF4444);
TextView tvDesc = new TextView(this);
tv
