4、Intent与页面跳转:显式Intent、隐式Intent、传递数据、startActivityForResult与ActivityResultLauncher
页面跳转,是Android开发里最基础也最频繁的操作。说白了,就是从一个界面跑到另一个界面去。你想想看,一个App如果只有一个页面,那跟一张静态海报有什么区别?
我刚开始做Android时,觉得跳转嘛,不就是startActivity一下?后来踩的坑多了才发现,这里面的门道还真不少。今天咱们就把Intent这块彻底捋清楚。
4.1 Intent是什么?
Intent,翻译过来是“意图”。你打算做什么事,就把这个“意图”告诉系统。系统根据你的意图,找到合适的组件去执行。
我个人习惯把Intent理解成“信使”。它携带了你要去的目的地,以及你要带过去的数据。系统收到这封信,就知道该启动哪个Activity、Service或者BroadcastReceiver。
Intent分两种:显式Intent和隐式Intent。咱们一个一个说。
4.2 显式Intent
显式Intent,就是明确指定要启动哪个组件。比如我要从MainActivity跳到DetailActivity,直接告诉系统:启动DetailActivity。
写法很简单:
// 创建显式Intent
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
startActivity(intent);
这种写法在同一个App内部跳转时最常用。我在项目中几乎都用这种方式,因为明确、直接、不容易出错。
4.3 隐式Intent
隐式Intent就不一样了。你不指定具体要启动哪个组件,而是描述你要做什么。系统根据你的描述,去匹配能处理这个“意图”的组件。
比如你要打开一个网页:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://www.example.com"));
startActivity(intent);
系统会找到浏览器(或者能处理网页链接的App)来打开这个链接。
隐式Intent的核心是三个要素:
- Action:你要做什么(比如ACTION_VIEW、ACTION_SEND)
- Data:你要操作的数据(比如网址、电话号码)
- Category:额外的类别信息(比如DEFAULT、BROWSABLE)
在AndroidManifest.xml中,目标Activity需要声明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>
if (intent.resolveActivity(getPackageManager()) != null) { ... }
4.4 传递数据
跳转时带点数据过去,这是家常便饭。Intent提供了putExtra方法,可以携带各种类型的数据。
// 发送方
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("user_id", 1001);
intent.putExtra("user_name", "张三");
startActivity(intent);
// 接收方(在DetailActivity中)
int userId = getIntent().getIntExtra("user_id", 0);
String userName = getIntent().getStringExtra("user_name");
如果数据比较复杂,可以用Bundle打包:
Bundle bundle = new Bundle();
bundle.putString("key1", "value1");
bundle.putInt("key2", 123);
intent.putExtras(bundle);
我个人建议:传递的数据不要太多。如果超过5个字段,或者数据结构复杂,考虑用序列化对象或者直接走数据库/网络。
4.5 startActivityForResult 与 ActivityResultLauncher
有时候你跳转到另一个页面,不只是展示信息,还需要它返回一个结果。比如从相册选一张照片,或者从联系人列表选一个人。
老办法是用startActivityForResult。嗯,这个方法现在已经被标记为废弃了,但很多老项目还在用,咱们也得了解。
// 老方式(已废弃,但理解原理)
startActivityForResult(intent, REQUEST_CODE_PICK_CONTACT);
// 在onActivityResult中处理返回结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_PICK_CONTACT) {
if (resultCode == RESULT_OK) {
String contact = data.getStringExtra("contact");
// 处理联系人信息
}
}
}
为什么废弃了?因为这种方式容易出问题:
- requestCode和resultCode容易搞混
- onActivityResult里代码一多,维护起来很痛苦
- Fragment和Activity的处理逻辑不一致
现在官方推荐用ActivityResultLauncher。这是AndroidX Activity 1.2.0引入的新API,用起来清爽多了。
// 新方式:ActivityResultLauncher
private ActivityResultLauncher<Intent> pickContactLauncher;
// 在初始化时注册
pickContactLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
String contact = data.getStringExtra("contact");
// 处理联系人信息
}
}
);
// 启动时调用
pickContactLauncher.launch(intent);
你看,代码结构清晰多了。启动和回调写在一起,逻辑一目了然。
4.6 知识体系总览
下面这张图,把Intent相关的核心知识点串起来了。你可以把它当作一个快速参考。
4.7 实战建议
最后,我总结几条实战中的经验:
- 能用显式就别用隐式:自己App内部的跳转,显式Intent更安全、更可控。
- 隐式Intent一定要做安全检查:调用前用resolveActivity检查,避免崩溃。
- 数据传递别贪多:超过1MB的数据,走文件或者数据库。
- 新项目用ActivityResultLauncher:别再用startActivityForResult了,维护起来太痛苦。
- Intent的key建议定义成常量:比如在目标Activity里定义companion object或者静态常量,避免拼写错误。
嗯,Intent这块内容就这些。看似简单,但用好它,你的App跳转逻辑会清晰很多。下次咱们聊点更进阶的内容。
公众号:蓝海资料掘金营,微信deep3321