Activity与Intent:Android组件的灵魂

说实话,Activity和Intent是Android开发里最基础也最核心的概念。我刚开始学Android时,总觉得Activity就是个“页面”,Intent就是个“跳转工具”。但做了几个项目后才发现——嗯,理解得还是太浅了。

今天我们就来彻底搞懂这几个东西。我会结合自己踩过的坑,帮你把知识点吃透。

Activity生命周期:你的页面会经历什么?

每个Activity从创建到销毁,都会经历一系列回调。我习惯把这七个方法记成“一生七步”:

回调方法 触发时机 典型用途
onCreate() Activity首次创建 初始化布局、绑定数据
onStart() Activity可见但不可交互 注册广播、开始动画
onResume() Activity可见且可交互 开始摄像头、恢复音乐播放
onPause() Activity失去焦点但部分可见 保存草稿、暂停动画
onStop() Activity完全不可见 释放资源、取消网络请求
onRestart() Activity从停止状态重新启动 重新加载数据
onDestroy() Activity被销毁 清理内存、解绑服务

核心要点:onCreate()和onDestroy()只调用一次,onPause()和onResume()可能被频繁调用。别在onPause()里做耗时操作——我曾经在这里写了个数据库保存,结果用户切应用时卡了半秒,被测试骂惨了。

我的习惯:在onCreate()里只做轻量初始化,比如findViewById。网络请求、数据库读取这些放到onResume()里,配合一个“是否首次加载”的布尔值控制,避免重复请求。

Intent:显式与隐式调用

Intent说白了就是“消息传递员”。它分两种:显式Intent和隐式Intent。

显式Intent:指名道姓

直接指定要启动的Activity类名。就像你打电话前先拨号——目标明确。

// 显式调用:直接指定目标Activity
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);

这种写法简单粗暴,适合应用内部跳转。我在项目里90%的场景都用显式Intent——因为可读性强,别人一看就知道你要跳到哪里。

隐式Intent:广撒网

不指定具体类名,而是通过action、category、data等条件让系统帮你匹配。就像你喊一声“谁帮我拿杯水”——系统会找到能处理这个请求的Activity。

// 隐式调用:打开浏览器
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://www.example.com"));
startActivity(intent);

隐式Intent需要在AndroidManifest.xml里配置intent-filter:

<activity android:name=".WebActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="https" />
    </intent-filter>
</activity>

避坑指南:我曾经在隐式Intent里忘了加category,结果系统死活找不到目标Activity。记住:隐式Intent至少需要一个action和一个category(通常用DEFAULT)。另外,如果系统找不到匹配的Activity,会抛出ActivityNotFoundException——记得用try-catch包一下。

Activity之间的数据传递

数据传递是Activity通信的核心。我总结了三种常用方式:

1. 使用putExtra()

最基础的方式,通过Intent携带数据:

// 发送方
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("user_id", 1001);
intent.putExtra("user_name", "张三");
startActivity(intent);

// 接收方
Intent intent = getIntent();
int userId = intent.getIntExtra("user_id", -1);
String userName = intent.getStringExtra("user_name");

2. 使用Bundle

当数据较多时,我习惯用Bundle打包:

// 发送方
Bundle bundle = new Bundle();
bundle.putInt("user_id", 1001);
bundle.putString("user_name", "张三");
bundle.putSerializable("user_info", userObject);
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtras(bundle);
startActivity(intent);

// 接收方
Bundle extras = getIntent().getExtras();
if (extras != null) {
    int userId = extras.getInt("user_id");
    String userName = extras.getString("user_name");
    UserInfo info = (UserInfo) extras.getSerializable("user_info");
}

3. 获取返回结果(startActivityForResult)

有时候你需要从目标Activity拿回数据。比如选照片后返回图片路径:

// 发送方
Intent intent = new Intent(this, PickerActivity.class);
startActivityForResult(intent, REQUEST_CODE_PICKER);

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_PICKER && resultCode == RESULT_OK) {
        String result = data.getStringExtra("selected_item");
        // 处理返回的数据
    }
}

// 接收方(PickerActivity中)
Intent resultIntent = new Intent();
resultIntent.putExtra("selected_item", "item_123");
setResult(RESULT_OK, resultIntent);
finish();

我的建议:现在官方推荐用Activity Result API替代startActivityForResult,但老项目里startActivityForResult依然常见。如果你维护的是新项目,建议用registerForActivityResult——代码更简洁,而且避免了onActivityResult里一堆if-else。

启动模式:控制Activity的“存在方式”

启动模式决定了Activity在任务栈里的行为。Android提供了四种模式,我一个个说。

Standard(标准模式)

每次启动都创建新实例,不管栈里有没有。就像每次点外卖都重新做一份——哪怕你刚吃完同样的菜。

<activity android:name=".StandardActivity" android:launchMode="standard" />

这种模式最常用,但要注意:如果频繁启动同一个Activity,栈里会堆积大量实例。我在一个新闻App里就踩过这个坑——用户连续点开10篇文章,按返回键要按10次才能退出。

SingleTop(栈顶复用)

如果目标Activity已经在栈顶,就不创建新实例,而是回调onNewIntent()。否则创建新实例。

<activity android:name=".TopActivity" android:launchMode="singleTop" />

适合通知栏点击跳转的场景。比如用户正在看消息列表,又来了一条新消息通知——点击通知后,复用栈顶的列表页,刷新数据即可。

SingleTask(栈内复用)

如果目标Activity在栈里存在,就把它上面的所有Activity出栈,然后复用这个实例。如果不存在,就创建新实例并放在栈顶。

<activity android:name=".MainActivity" android:launchMode="singleTask" />

这个模式我常用在主界面。比如用户从首页跳到详情页,再跳到设置页,然后点击返回首页——这时候应该把首页上面的页面全部清掉,直接显示首页。SingleTask完美解决这个问题。

SingleInstance(单实例模式)

这个模式最特殊。目标Activity会单独占用一个任务栈,而且整个系统里只有一个实例。

<activity android:name=".PlayerActivity" android:launchMode="singleInstance" />

适合播放器、拨号器这类全局唯一的页面。我记得有个音乐App项目,播放页面用了SingleInstance——用户从任何地方打开播放器,都回到同一个播放界面,体验很统一。

启动模式对比表:

模式 是否创建新实例 是否复用已有实例 典型场景
Standard 普通页面跳转
SingleTop 栈顶有时不创建 栈顶时复用 通知栏跳转
SingleTask 栈内有时不创建 栈内时复用 主界面、首页
SingleInstance 全局唯一 全局复用 播放器、拨号器

避坑指南:我曾经在SingleTask模式下忘了处理onNewIntent(),结果页面复用后数据没刷新。记住:只要启动模式不是Standard,都要在onNewIntent()里处理新传入的Intent数据。另外,SingleInstance模式下不同应用共享同一个实例——如果处理不好,会出现数据混乱。

知识体系总览

下面这张图总结了本章的核心知识点。我建议你把它保存下来,复习时对照着看:

Activity与Intent 生命周期 onCreate → onStart → onResume onPause → onStop → onDestroy onRestart(从停止恢复) Intent 显式:指定类名 隐式:action + category + data 数据传递:putExtra / Bundle 启动模式 Standard:每次都新建 SingleTop:栈顶复用 SingleTask:栈内复用 SingleInstance:全局唯一 核心:理解任务栈 + 回调时机 = 写出稳定的Android应用

Activity和Intent是Android开发的基石。生命周期决定了你的页面何时创建、何时销毁;Intent决定了页面之间如何通信;启动模式则控制了页面的“存在方式”。这三者配合好了,你的App才能流畅、稳定、用户体验好。

我个人建议:刚开始学的时候,先把Standard模式和显式Intent用熟。等项目做多了,再慢慢尝试SingleTask和隐式Intent。别一口气吃成胖子——我见过太多新手一上来就搞SingleInstance,结果把自己绕晕了。

好了,这一章的内容就到这里。记住:多写代码,多调试,多踩坑——踩过的坑才是真正学到的东西。


公众号:蓝海资料掘金营,微信deep3321