跳到主要内容

Intent 与数据传递

问题

Intent 的作用是什么?显式 Intent 和隐式 Intent 有什么区别?如何在组件间传递数据?

答案

1. Intent 类型

// 显式 Intent —— 指定目标组件
val explicitIntent = Intent(this, DetailActivity::class.java)
explicitIntent.putExtra("id", 123)
startActivity(explicitIntent)

// 隐式 Intent —— 声明动作,由系统匹配
val implicitIntent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://example.com")
}
startActivity(implicitIntent)

// 隐式 Intent 发送前应检查是否有匹配的 Activity
if (implicitIntent.resolveActivity(packageManager) != null) {
startActivity(implicitIntent)
}

2. 数据传递方式

Bundle / putExtra

// 发送
val intent = Intent(this, DetailActivity::class.java).apply {
putExtra("name", "Alice")
putExtra("age", 25)
putExtra("tags", arrayListOf("android", "kotlin"))
}
startActivity(intent)

// 接收
val name = intent.getStringExtra("name")
val age = intent.getIntExtra("age", 0)
val tags = intent.getStringArrayListExtra("tags")

Parcelable(推荐)

// @Parcelize —— Kotlin 插件自动生成 Parcelable 实现
@Parcelize
data class User(
val name: String,
val age: Int,
val email: String
) : Parcelable

// 传递
intent.putExtra("user", User("Alice", 25, "a@b.com"))

// 接收
val user = intent.getParcelableExtra<User>("user")
// Android 13+
val user = intent.getParcelableExtra("user", User::class.java)

Serializable

data class Config(
val theme: String,
val fontSize: Int
) : Serializable

// 传递
intent.putExtra("config", Config("dark", 16))
方式性能使用便利性推荐度
Parcelable快(内存序列化)@Parcelize 很方便⭐⭐⭐
Serializable慢(反射序列化)简单⭐⭐
Bundle 基本类型最快类型不安全⭐⭐⭐
Bundle 大小限制

Intent 传递的 Bundle 数据经过 Binder,总大小限制约 1MB(包括所有正在进行的事务)。大数据应通过 ViewModel、数据库或文件传递。

3. Activity Result API

取代已废弃的 startActivityForResult

class MyActivity : AppCompatActivity() {

// 注册结果回调
private val launcher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
val data = result.data?.getStringExtra("result")
// 处理返回结果
}
}

// 启动
fun openDetail() {
launcher.launch(Intent(this, DetailActivity::class.java))
}
}

// 内置 Contract 示例
// 选择图片
private val pickImage = registerForActivityResult(
ActivityResultContracts.GetContent()
) { uri: Uri? ->
uri?.let { imageView.setImageURI(it) }
}

// 请求权限
private val requestPermission = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) { /* 权限已授予 */ }
}

// 调用
pickImage.launch("image/*")
requestPermission.launch(Manifest.permission.CAMERA)

4. PendingIntent

PendingIntent 是对 Intent 的封装,允许其他应用或系统在未来某个时间点代替你执行操作:

// 创建 PendingIntent
val intent = Intent(context, DetailActivity::class.java).apply {
putExtra("from", "notification")
}

val pendingIntent = PendingIntent.getActivity(
context,
0, // requestCode
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

// 用于通知
val notification = NotificationCompat.Builder(context, "channel_id")
.setContentTitle("新消息")
.setContentText("你收到一条消息")
.setContentIntent(pendingIntent) // 点击通知打开 Activity
.setAutoCancel(true)
.build()
PendingIntent Flag(Android 12+)

Android 12 要求必须显式指定 FLAG_IMMUTABLEFLAG_MUTABLE

  • FLAG_IMMUTABLE:推荐,PendingIntent 创建后不可修改
  • FLAG_MUTABLE:仅在需要系统修改 Intent 内容时使用(如直接回复通知)
<!-- AndroidManifest.xml -->
<activity android:name=".DetailActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="example.com"
android:pathPrefix="/detail" />
</intent-filter>
</activity>
// 接收 Deep Link
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

intent?.data?.let { uri ->
val id = uri.getQueryParameter("id")
loadDetail(id)
}
}

常见面试问题

Q1: Parcelable 和 Serializable 的区别?

答案

特性ParcelableSerializable
性能快(直接内存读写)慢(使用反射 + IO)
实现方式需要手动实现(或 @Parcelize只需实现接口
适用场景Android 组件间传递跨平台、持久化
存储介质内存内存 / 磁盘

Kotlin 使用 @Parcelize 注解后,Parcelable 的实现成本和 Serializable 一样低,因此推荐始终使用 Parcelable

Q2: 隐式 Intent 的匹配规则是什么?

答案

隐式 Intent 需要同时匹配 Intent Filter 的三个条件:

  1. action:Intent 的 action 必须匹配 Filter 中的某一个 action
  2. category:Intent 的所有 category 必须在 Filter 中都有(DEFAULTstartActivity 自动添加的)
  3. data:URI(scheme、host、path)和 MIME type 的匹配

三个条件都满足才能匹配成功。

Q3: startActivityForResult 为什么被废弃?

答案

问题:

  1. onActivityResult 在 Activity 中是一个大的 switch-case,难以维护
  2. requestCode 管理混乱
  3. 代码分散在两处(发起处和回调处)

Activity Result API 的优势:

  1. 回调和发起代码在一起(registerForActivityResult + launch
  2. 类型安全的 Contract
  3. 可在 Fragment 中使用,不依赖 Activity

Q4: PendingIntent 的 Flag 有什么区别?

答案

Flag行为
FLAG_UPDATE_CURRENT更新已有 PI 的 extras
FLAG_CANCEL_CURRENT取消已有 PI,创建新的
FLAG_NO_CREATE不创建,仅返回已有的 PI
FLAG_ONE_SHOT只能使用一次
FLAG_IMMUTABLEPI 创建后不可修改(Android 12+ 推荐)
FLAG_MUTABLE允许系统修改 PI 的 Intent

Q5: 如何安全地传递大数据?

答案

Intent/Bundle 有约 1MB 的限制。大数据传递方案:

  1. ViewModel — 同一 Activity 内的 Fragment 共享
  2. 数据库/文件 — 传递 ID,接收方从存储中读取
  3. ContentProvider — 跨进程大数据共享
  4. Singleton/全局缓存 — 内存中缓存(注意生命周期)
// 传递 ID 而非完整数据
intent.putExtra("user_id", userId)

// 在目标 Activity 中通过 ID 获取
val user = repository.getUserById(userId)

相关链接