Android 四大组件桥梁 —— Intent (意图) 详解

目录

[一、Intent 概述](#一、Intent 概述)

[二、Intent 的类型(显式和隐式)](#二、Intent 的类型(显式和隐式))

[1.显式 Intent](#1.显式 Intent)

[2.隐式 Intent](#2.隐式 Intent)

[三、Intent 的核心组成部分](#三、Intent 的核心组成部分)

[1. Action(动作)](#1. Action(动作))

[2. Data(数据)](#2. Data(数据))

3.Type(数据类型)

4.Category(类别)

5.Extras(附加数据)

6.Flags(标志)

[四、Intent Filter(意图过滤器)](#四、Intent Filter(意图过滤器))

五、Intent传递简单数据

[1.使用 putExtra() 方法逐个添加](#1.使用 putExtra() 方法逐个添加)

[2. 使用 Bundle 对象打包数据](#2. 使用 Bundle 对象打包数据)

[3. 传递数组和集合](#3. 传递数组和集合)

[六、使用 Intent 传递复杂数据](#六、使用 Intent 传递复杂数据)

[1.传递 Parcelable 对象(性能更高)](#1.传递 Parcelable 对象(性能更高))

[2.传递 Serializable 对象(更简单,但性能较差)](#2.传递 Serializable 对象(更简单,但性能较差))

[七、获取 Activity 的结果:startActivityForResult](#七、获取 Activity 的结果:startActivityForResult)


一、Intent 概述

Intent(意图)是 Android 程序中在不同组件之间(如 Activity、Service、BroadcastReceiver)传递消息的对象。它可以在运行时绑定不同的组件,是组件间通信的核心。

主要作用

  1. 启动组件:启动一个 Activity、Service 或发送一个 Broadcast。

  2. 传递数据:在组件之间携带数据。

  3. 声明目标组件:可以明确指定要启动的组件,也可以只描述要执行的动作,由系统来选择合适的组件。

  • startActivity (Intent)/startActivityForResult(Intent):来启动一个Activity
  • startService (Intent)/bindService(Intent):来启动一个Service
  • sendBroadcast:发送广播到指定BroadcastReceiver

二、Intent 的类型(显式和隐式)

1.显式 Intent

  • 定义明确指定了要启动的组件类名(Activity.class, Service.class)。通常用于启动应用内的组件,因为你知道具体的类名。
  • 核心属性ComponentName
java 复制代码
// 启动同一个应用内的 AnotherActivity
Intent explicitIntent = new Intent(MainActivity.this, AnotherActivity.class);
startActivity(explicitIntent);

// 或者通过 setComponent 方法
Intent intent = new Intent();
intent.setComponent(new ComponentName(MainActivity.this, "com.example.app.AnotherActivity"));
startActivity(intent);

// 启动一个 Service
Intent serviceIntent = new Intent(MainActivity.this, MyService.class);
startService(serviceIntent);
  • 系统处理: 系统不进行解析直接启动指定的组件 。如果该组件存在且声明在Manifest中,则启动;否则抛出ActivityNotFoundException或类似异常。

2.隐式 Intent

  • 定义 :不指定具体的组件类名,而是声明一个要执行的动作(Action),并附加一些数据(Data)、类别(Category)等信息。系统会根据这些信息在所有应用的 AndroidManifest.xml 中注册的**<intent-filter>**进行匹配,找到并启动最适合的组件。
  • 核心属性Action, Data, Category, Type, Extras(用于传额外数据)等。
java 复制代码
// 打开一个网页
Intent implicitIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com"));
startActivity(implicitIntent);

// 发送邮件
Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
emailIntent.setData(Uri.parse("mailto:contact@example.com"));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "邮件主题");
startActivity(emailIntent);

// 分享文本
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, "这是要分享的文本");
startActivity(Intent.createChooser(shareIntent, "分享到"));
  • 系统处理:
  1. 查询PackageManager: 系统向PackageManager查询所有已安装应用AndroidManifest.xml文件。
  2. 匹配<intent-filter> 对于每个组件(Activity/Service/BroadcastReceiver)声明的<intent-filter>,系统尝试匹配Intent的内容:
    • Action: Intent的**Action必须在filter声明的<action>**列表中。
    • Data: Intent的Data (Uri & MIME Type) 必须匹配filter声明的<data>元素。Uri匹配Scheme, Host, Port, Path;Type匹配或*/*通配。
    • Category: Intent携带的所有Category必须在filter声明的<category>列表中(filter可以声明额外的Category)。CATEGORY_DEFAULT通常需要显式声明在filter中。
  3. 过滤结果: 所有满足上述条件的组件被筛选出来。
  4. 选择目标:
    • Activity/Service: 如果有多个匹配项,系统通常会显示一个选择器对话框(Resolver Activity) 让用户选择(除非设置了Intent.createChooser()强制显示,或设置了Intent.FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK等特殊标志,或目标应用设置了android:autoVerify="true"并通过了App Links验证)。
    • BroadcastReceiver: 所有匹配的Receiver都会收到广播(有序广播按优先级顺序)。

三、Intent 的核心组成部分

一个 Intent 对象包含了许多信息,系统根据这些信息来决定哪个组件应该被启动。

1. Action(动作)

一个字符串,表示要执行的通用动作。例如:

  • Intent.ACTION_VIEW: 显示数据给用户。

  • Intent.ACTION_EDIT: 编辑数据。

  • Intent.ACTION_DIAL: 拨号。

  • Intent.ACTION_SEND: 发送数据(分享)。

  • Intent.CUSTOM_ACTION也可以使用自定义的 Action 字符串。例如

    com.google.app.myapp.CUSTOM_ACTION,

2. Data(数据)

数据通常与 Action 配合使用,用**Uri 对象**表示要操作的数据。

  • ACTION_VIEW + content://contacts/people/1: 显示 ID 为 1 的联系人信息。

  • ACTION_EDIT + file://sdcard/example.txt: 编辑 SD 卡上的文本文件。

  • ACTION_CALL + tel:123456: 直接呼叫号码(需要 CALL_PHONE 权限)。

3.Type(数据类型)

指定 Data 所指向数据的 MIME 类型如果设置了 Type,系统通常会忽略 Data 中的 URI 的 MIME 类型推断

java 复制代码
intent.setType("image/png");
// 注意:setData 和 setType 会互相清除对方,所以如果要同时设置,请使用 setDataAndType
intent.setDataAndType(imageUri, "image/png");

4.Category(类别)

一个字符串,提供了关于目标组件的额外信息。一个 Intent 可以添加多个 Category。

  • Intent.CATEGORY_LAUNCHER表示该 Activity 是任务的初始 Activity,会出现在应用启动器中。

  • Intent.CATEGORY_DEFAULT : 如果希望组件能够被隐式 Intent 启动,通常需要声明这个 Category。

  • Intent.CATEGORY_BROWSABLE: 表示目标 Activity 允许本身被浏览器启动。

5.Extras(附加数据)

键值对形式存放的额外信息,用于在组件间传递数据。使用 putExtra() 方法添加。

  • 系统定义了很多 EXTRA_* 常量,如 Intent.EXTRA_EMAIL, Intent.EXTRA_STREAM

  • 也可以使用自定义的键。

java 复制代码
// 传递数据
intent.putExtra("KEY_NAME", "Alice");
intent.putExtra("KEY_AGE", 25);

// 在目标 Activity 中获取
String name = getIntent().getStringExtra("KEY_NAME");
int age = getIntent().getIntExtra("KEY_AGE", 0); // 0 是默认值

6.Flags(标志)

用于指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务)以及启动之后如何对待它。这个后面说 Activity 的时候会详细讲解。

  • Intent.FLAG_ACTIVITY_NEW_TASK: 在一个新任务中启动 Activity。

  • Intent.FLAG_ACTIVITY_CLEAR_TOP: 如果目标 Activity 已在当前任务中运行,则不会启动该 Activity 的新实例,而是会清除其上的所有其他 Activity,并将其置于任务顶部。

  • Intent.FLAG_ACTIVITY_SINGLE_TOP: 如果要启动的 Activity 是当前 Activity(位于返回栈的顶部),则不会创建新实例。

四、Intent Filter(意图过滤器)

为了让一个组件(如 Activity)能够响应特定的隐式 Intent,必须在 AndroidManifest.xml 中为该组件声明 <intent-filter>

一个 <intent-filter> 可以定义:

  • <action> : 至少一个,声明组件能响应的动作

  • <category> : 零个或多个,声明组件所属的类别。如果没有指定类别,则只响应没有类别的 Intent。但 CATEGORY_DEFAULT 几乎总是需要。

  • <data> : 零个或多个,声明组件能处理的数据类型和 URI 模式

示例:一个 Activity 声明自己可以处理"分享"动作和文本类型。

XML 复制代码
<activity android:name=".ShareActivity">
    <intent-filter>
        <!-- 指定动作 -->
        <action android:name="android.intent.action.SEND" />
        <!-- 指定类别,使其能被隐式 Intent 启动 -->
        <category android:name="android.intent.category.DEFAULT" />
        <!-- 指定数据类型 -->
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

五、Intent传递简单数据

1.使用 putExtra() 方法逐个添加

java 复制代码
// 发送数据
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("name", "张三");
intent.putExtra("age", 25);
intent.putExtra("score", 95.5f);
intent.putExtra("isStudent", true);
startActivity(intent);

// 接收数据
Intent intent = getIntent();
String name = intent.getStringExtra("name");
int age = intent.getIntExtra("age", 0); // 第二个参数为默认值
float score = intent.getFloatExtra("score", 0.0f);
boolean isStudent = intent.getBooleanExtra("isStudent", false);

2. 使用 Bundle 对象打包数据

java 复制代码
// 发送数据
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "李四");
bundle.putInt("age", 30);
bundle.putDouble("salary", 8000.50);
bundle.putBoolean("isEmployed", true);
intent.putExtras(bundle);
startActivity(intent);

// 接收数据
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
    String name = bundle.getString("name");
    int age = bundle.getInt("age");
    double salary = bundle.getDouble("salary");
    boolean isEmployed = bundle.getBoolean("isEmployed");
}

3. 传递数组和集合

java 复制代码
// 发送数据
Intent intent = new Intent(MainActivity.this, SecondActivity.class);

// 基本类型数组
intent.putExtra("intArray", new int[]{1, 2, 3, 4, 5});
intent.putExtra("stringArray", new String[]{"A", "B", "C"});

// ArrayList
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Kotlin");
list.add("Dart");
intent.putStringArrayListExtra("languageList", list);

startActivity(intent);

// 接收数据
int[] intArray = getIntent().getIntArrayExtra("intArray");
String[] stringArray = getIntent().getStringArrayExtra("stringArray");
ArrayList<String> languageList = getIntent().getStringArrayListExtra("languageList");

六、使用 Intent 传递复杂数据

除了基本类型,还可以通过 Intent 传递可序列化(Serializable)或可打包(Parcelable)的对象。类似bitmap默认实现Parcelable接口,直接传递即可

1.传递 Parcelable 对象(性能更高)

  1. 让你的类实现 Parcelable 接口

  2. 实现 describeContents()writeToParcel(Parcel dest, int flags) 方法。

  3. 创建一个名为 **CREATOR**的静态字段。

java 复制代码
public class User implements Parcelable {
    private String name;
    private int age;

    // ... 构造方法、getter/setter ...

    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
}

4.传递和接收:

java 复制代码
// 发送方
User user = new User("Bob", 30);
Intent intent = new Intent(this, TargetActivity.class);
intent.putExtra("user_key", user);
startActivity(intent);

// 接收方
User receivedUser = getIntent().getParcelableExtra("user_key");

通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射 成你的对象。也可以将Parcel看成是一个流,通过writeToParcel把对象写到流里面, 在通过createFromParcel从流里读取对象,只不过这个过程需要你来实现,因此写的 顺序和读的顺序必须一致。

2.传递 Serializable 对象(更简单,但性能较差)

java 复制代码
// 让类实现 Serializable 接口
public class User implements Serializable {
    // ...
}

// 传递
intent.putExtra("user_key", user);

// 接收
User receivedUser = (User) getIntent().getSerializableExtra("user_key");

两种序列化方式的比较:

  • 1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
  • 2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
  • 3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的 持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable。

七、获取 Activity 的结果:startActivityForResult

注意 :此方法已被标记为 deprecated,推荐使用新的 Activity Result API,但理解其原理仍有价值)

旧方式

java 复制代码
// 启动 Activity,并期待返回结果
private static final int REQUEST_CODE_PICK_CONTACT = 1;

Intent pickContactIntent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(pickContactIntent, REQUEST_CODE_PICK_CONTACT);

// 在源 Activity 中重写 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) {
            // 处理返回的数据 Intent 'data'
            Uri contactUri = data.getData();
            // ... 处理联系人 URI ...
        }
    }
}

新方式(Activity Result API)

  1. 注册一个 Activity Result Launcher

  2. 启动它

java 复制代码
// 在 Activity 或 Fragment 中
private ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        result -> {
            // 这里是回调
            if (result.getResultCode() == Activity.RESULT_OK) {
                Intent data = result.getData();
                // 处理返回的 Intent data
            }
        });

// 当需要启动 Activity 并获取结果时
Intent intent = new Intent(this, TargetActivity.class);
someActivityResultLauncher.launch(intent);
相关推荐
用户2018792831674 小时前
MVP架构模式:餐厅点餐的有趣故事
android
用户2018792831675 小时前
MVVM 架构模式:咖啡馆的智能点餐系统
android
用户2018792831675 小时前
浅析Android MVC架构
android
AsiaLYF6 小时前
kotlin中MutableStateFlow和MutableSharedFlow的区别是什么?
android·开发语言·kotlin
2501_916008897 小时前
iOS 发布全流程详解,从开发到上架的流程与跨平台使用 开心上架 发布实战
android·macos·ios·小程序·uni-app·cocoa·iphone
4Forsee7 小时前
【Android】浅析 Android 的 IPC 跨进程通信机制
android·java
叶羽西7 小时前
如何区分Android、Android Automotive、Android Auto
android
用户2018792831677 小时前
用 “奶茶店订单系统” 讲懂 MVI 架构
android
LiuYaoheng8 小时前
【Android】布局优化:include、merge、ViewStub的使用及注意事项
android·java