4、Intent与页面跳转:显式Intent与隐式Intent的使用,Activity之间的数据传递
各位同学,今天我们来聊聊Android开发里最基础、也最核心的一个话题——页面跳转。说白了,就是怎么从一个界面跑到另一个界面去。
我记得刚入行那会儿,有个同事把整个App做成了一个单Activity结构,所有逻辑全塞在一个文件里。那代码,啧啧,简直不忍直视。后来我接手重构,第一件事就是把页面拆开,用Intent把它们串起来。嗯,这才是Android该有的样子。
4.1 什么是Intent?
Intent,翻译过来是“意图”。你想想看,你想从A页面到B页面,总得有个“意图”告诉系统吧?Intent就是干这个的。
它有两种玩法:显式Intent和隐式Intent。我个人的习惯是:能显式就显式,除非你真的需要系统帮你“匹配”目标。
4.2 显式Intent
显式Intent,就是直接指定要跳转到哪个Activity。就像你打车,直接告诉司机“去北京南站”,清清楚楚。
// 最简单的显式跳转
val intent = Intent(this, TargetActivity::class.java)
startActivity(intent)
我在项目中遇到过一个问题:有同事在Fragment里直接用了this作为Context,结果崩溃了。记住,Fragment里要用requireContext()或者activity。
FLAG_ACTIVITY_NEW_TASK标志。
4.3 隐式Intent
隐式Intent就有点意思了。你不指定具体目标,而是描述“你想干什么”,让系统帮你找合适的Activity来处理。就像你喊一声“我要去火车站”,系统会看看哪个司机愿意接单。
// 打开浏览器
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.example.com")
startActivity(intent)
这里有个坑,我曾经踩过:如果手机上没有任何App能处理这个Intent,你的App会直接崩溃。所以一定要做安全检查:
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
} else {
// 提示用户没有可用的应用
}
4.4 Activity之间的数据传递
跳转页面只是第一步,很多时候你还需要带点数据过去。比如从商品列表页跳到详情页,总得告诉详情页“你要展示哪个商品”吧?
4.4.1 使用putExtra
最直接的方式,就是用putExtra往Intent里塞数据:
// 发送方
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("product_id", 1001)
intent.putExtra("product_name", "Android性能优化实战")
startActivity(intent)
// 接收方
val productId = intent.getIntExtra("product_id", 0)
val productName = intent.getStringExtra("product_name")
嗯,这里要注意:getStringExtra可能返回null,最好给个默认值。我习惯用Kotlin的Elvis运算符:
val name = intent.getStringExtra("product_name") ?: "未知商品"
4.4.2 使用Bundle
当你要传的数据比较多时,一个个putExtra就显得有点啰嗦了。这时候用Bundle会更清爽:
// 发送方
val bundle = Bundle().apply {
putInt("product_id", 1001)
putString("product_name", "Android性能优化实战")
putDouble("price", 59.9)
}
val intent = Intent(this, DetailActivity::class.java)
intent.putExtras(bundle)
startActivity(intent)
// 接收方
val bundle = intent.extras
if (bundle != null) {
val id = bundle.getInt("product_id")
val name = bundle.getString("product_name")
val price = bundle.getDouble("price")
}
4.5 startActivityForResult与ActivityResultLauncher
有时候你跳转到另一个页面,不只是为了展示,还想等它返回一个结果。比如你打开相机拍照,拍完后要把照片拿回来。
4.5.1 老方式:startActivityForResult
这是Android早期的方式,现在已经被标记为废弃了。但很多老项目里还在用,我简单提一下:
// 发送方
val intent = Intent(this, EditActivity::class.java)
startActivityForResult(intent, REQUEST_CODE_EDIT)
// 接收结果
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_EDIT && resultCode == RESULT_OK) {
val result = data?.getStringExtra("result_data")
// 处理结果
}
}
说实话,这个方式问题很多。比如Fragment和Activity的onActivityResult容易搞混,而且代码分散,不好维护。我曾经在一个项目里排查了半天,才发现是Fragment里调了startActivityForResult,但结果被Activity拦截了。
4.5.2 新方式:ActivityResultLauncher
Android官方后来推出了Activity Result API,用起来舒服多了:
// 第一步:注册launcher
private val editLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
val data = result.data
val resultText = data?.getStringExtra("result_data") ?: ""
// 处理结果
}
}
// 第二步:启动
val intent = Intent(this, EditActivity::class.java)
editLauncher.launch(intent)
你看,代码集中了,而且类型安全。再也不用担心requestCode冲突了。
4.6 知识体系总览
说了这么多,我们来画个图,把今天的内容串起来:
4.7 避坑指南
最后,我把自己这些年踩过的坑总结一下,你们遇到了直接对照着查:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 跳转后崩溃 | 目标Activity未在Manifest注册 | 检查AndroidManifest.xml |
| 隐式Intent无响应 | 没有App能处理该Intent | 用resolveActivity检查 |
| 数据传不过去 | key拼写错误或类型不匹配 | 定义常量统一管理key |
| onActivityResult不触发 | Fragment和Activity混淆 | 改用ActivityResultLauncher |
| 传递大对象卡顿 | 用了Serializable | 改用Parcelable |
好了,这一章的内容就到这里。Intent这东西,说白了就是Android的“交通系统”。你掌握了它,页面之间的流转就畅通无阻了。下次遇到页面跳转的问题,记得回来翻翻这张图。
公众号:蓝海资料掘金营,微信deep3321