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内部跳转时最常用。我在项目中几乎都用这种方式,因为明确、直接、不容易出错。

我的习惯:显式Intent的第二个参数,我建议用.class字面量,不要用字符串拼接。这样编译期就能检查类型,避免运行时崩溃。

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>
我曾经踩过的坑:隐式Intent如果没有匹配到任何组件,startActivity会直接崩溃。所以调用前最好用resolveActivity检查一下:
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个字段,或者数据结构复杂,考虑用序列化对象或者直接走数据库/网络。

重要提醒:Intent传递的数据大小有限制,一般建议不要超过1MB。我在项目中遇到过传递大Bitmap导致崩溃的情况,后来改用Uri传递文件路径才解决。

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);

你看,代码结构清晰多了。启动和回调写在一起,逻辑一目了然。

我建议:新项目直接用ActivityResultLauncher。老项目重构时,也优先迁移到这个方案。虽然改起来有点麻烦,但长期维护成本低很多。

4.6 知识体系总览

下面这张图,把Intent相关的核心知识点串起来了。你可以把它当作一个快速参考。

Intent 知识体系 Intent 显式Intent 隐式Intent 指定目标组件(类名) 同一App内部跳转 编译期类型安全 Action + Data + Category 跨App调用(如打开网页) 需配置intent-filter 数据传递:putExtra / Bundle / 序列化

4.7 实战建议

最后,我总结几条实战中的经验:

  1. 能用显式就别用隐式:自己App内部的跳转,显式Intent更安全、更可控。
  2. 隐式Intent一定要做安全检查:调用前用resolveActivity检查,避免崩溃。
  3. 数据传递别贪多:超过1MB的数据,走文件或者数据库。
  4. 新项目用ActivityResultLauncher:别再用startActivityForResult了,维护起来太痛苦。
  5. Intent的key建议定义成常量:比如在目标Activity里定义companion object或者静态常量,避免拼写错误。

嗯,Intent这块内容就这些。看似简单,但用好它,你的App跳转逻辑会清晰很多。下次咱们聊点更进阶的内容。


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