2\.
定义协议
新建一个Contract类,继承自ActivityResultContract<I,O>
,其中,I
是输入的类型,O
是输出的类型。需要实现2个方法,createIntent
和parseResult
,输入类型I
作为createIntent
的参数,输出类型O
作为parseResult
方法的返回值,在下面的例子中,输入输出类型都是String:
class MyActivityResultContract: ActivityResultContract<String,String>(){
override fun createIntent(context: Context, input: String?): Intent {
return Intent(context,SecondActivity::class.java).apply {
putExtra(“name”,input)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): String? {
val data = intent?.getStringExtra(“result”)
return if (resultCode == Activity.RESULT_OK && data != null) data
else null
}
}
复制代码
如上代码,我们在createIntent
方法中创建了Intent,并且携带了参数name
,在parseResult
方法中,获取了返回的数据result
。
3\.
注册协议,获取启动器-ActivityResultLauncher
注册协议,使用registerForActivityResult
方法,该方法由ComponentActivity
或者Fragment
提供,接受2个参数,第一个参数就是我们定义的Contract协议,第二个参数是一个回调ActivityResultCallback<O>
,其中O
就是前面Contract的输出类型。代码如下:
private val myActivityLauncher = registerForActivityResult(MyActivityResultContract()){result ->
Toast.makeText(applicationContext,result,Toast.LENGTH_SHORT).show()
textView.text = “回传数据:$result”
}
复制代码
如上代码,注册了MyActivityResultContract
,registerForActivityResult
方法的返回值是ActivityResultLauncher
, 因此我们定义了一个myActivityLauncher
,回调方法中,result
就是从上一个界面传回的值。这里我们简单的用Toast显示。
4.
最后,调用启动器的launch方法开启界面跳转
MainActivity
中添加一个Button,点击Button时,调用launch
方法跳转:
button.setOnClickListener {
// 开启页面跳转
myActivityLauncher.launch(“Hello,技术最TOP”)
}
复制代码
SecondActivity
的代码很简单:
class SecondActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.second_layout)
val name = intent.getStringExtra(“name”)
textView3.text = “接收到的数据为:$name”
button2.setOnClickListener {
val intent = Intent().apply {
putExtra(“result”,“Hello,依然范特西稀,我是回传的数据!”)
}
setResult(Activity.RESULT_OK,intent)
finish()
}
}
}
复制代码
以上3步,就实现了使用新的Activity Results API
来完成Activity之间的数据传递,并获取Activity返回的数据
看一下效果:
[图片上传中…(image-606abe-1603979016399-4)]
这就完了吗?
[图片上传中…(image-ac0011-1603979016399-3)]
你可能会有疑问,虽然确实减少了代码耦合,但是使用并不简单啊。
确实,但这并没有完!!!
预定义的Contract
大伙都看出来,新的Activity Results API
使用起来好像有点麻烦,每次都得定义Contract。Google肯定考虑到了这个问题的,于是,Google 预定义了很多Contract,把你们能想到的使用场景基本上都想到了,它们都定义在类ActivityResultContracts
中,有以下这些Contract:
StartActivityForResult()
RequestMultiplePermissions()
RequestPermission()
TakePicturePreview()
TakePicture()
TakeVideo()
PickContact()
CreateDocument()
OpenDocumentTree()
OpenMultipleDocuments()
OpenDocument()
GetMultipleContents()
GetContent()
复制代码
下面分别介绍一下这些Contract:
-
StartActivityForResult
: 通用的Contract,不做任何转换,Intent作为输入,ActivityResult作为输出,这也是最常用的一个协定。 -
RequestMultiplePermissions
: 用于请求一组权限 -
RequestPermission
: 用于请求单个权限 -
TakePicturePreview
: 调用MediaStore.ACTION_IMAGE_CAPTURE
拍照,返回值为Bitmap图片 -
TakePicture
: 调用MediaStore.ACTION_IMAGE_CAPTURE
拍照,并将图片保存到给定的Uri地址,返回true表示保存成功。 -
TakeVideo
: 调用MediaStore.ACTION_VIDEO_CAPTURE
拍摄视频,保存到给定的Uri地址,返回一张缩略图。 -
PickContact
: 从通讯录APP获取联系人 -
GetContent
: 提示用选择一条内容,返回一个通过ContentResolver#openInputStream(Uri)
访问原生数据的Uri地址(content://形式) 。默认情况下,它增加了Intent#CATEGORY_OPENABLE
, 返回可以表示流的内容。 -
CreateDocument
: 提示用户选择一个文档,返回一个(file:/http:/content:)开头的Uri。 -
OpenMultipleDocuments
: 提示用户选择文档(可以选择多个),分别返回它们的Uri,以List的形式。 -
OpenDocumentTree
: 提示用户选择一个目录,并返回用户选择的作为一个Uri返回,应用程序可以完全管理返回目录中的文档。
上面这些预定义的Contract中,除了StartActivityForResult
和RequestMultiplePermissions
之外,基本都是处理的与其他APP交互,返回数据的场景,比如,拍照,选择图片,选择联系人,打开文档等等。使用最多的就是StartActivityForResult
和RequestMultiplePermissions
了。
有了这些预定义的Contract, Activity之间传递数据就简单多了,比如,前面的例子,可以简化成这样:
1\. 注册协议,获取
ActivityResultLauncher:
private val myActivityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ activityResult ->
if(activityResult.resultCode == Activity.RESULT_OK){
val result = activityResult.data?.getStringExtra(“result”)
Toast.makeText(applicationContext,result,Toast.LENGTH_SHORT).show()
textView.text = “回传数据:$result”
}
}
复制代码
2\. 构造需要传递的数据,启动页面跳转
button.setOnClickListener {
val intent = Intent(this,SecondActivity::class.java).apply {
putExtra(“name”,“Hello,技术最TOP”)
}
myActivityLauncher.launch(intent)
}
复制代码
OK,就是这么简单!!!
在比如,我们的权限,申请,请看代码:
request_permission.setOnClickListener {
requestPermission.launch(permission.BLUETOOTH)
}
request_multiple_permission.setOnClickListener {
requestMultiplePermissions.launch(
arrayOf(
permission.BLUETOOTH,
permission.NFC,
permission.ACCESS_FINE_LOCATION
)
)
}
// 请求单个权限
private val requestPermission =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
// Do something if permission granted
if (isGranted) toast(“Permission is granted”)
else toast(“Permission is denied”)
}
// 请求一组权限
private val requestMultiplePermissions =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions : Map<String, Boolean> ->
// Do something if some permissions granted or denied
permissions.entries.forEach {
// Do checking here
}
}
复制代码
有了这个,我们就可以抛弃所有的第三方权限请求框架,只需要将这两个Contract放到BaseActivity中,或者抽取到一个单独的类中,就能随时随地申请权限。是不是很方便!!!
[图片上传中…(image-13ff11-1603979016399-2)]
在非Activity/Fragment的类中接收Activity的结果
在Activity和Fragment中,我们能直接使用registerForActivityResult
API ,那是因为ConponentActivity
和Fragment
基类实现了ActivityResultCaller
接口,在非Activity/Fragment中,如果我们想要接收Activity回传的数据,可以直接使用 ActivityResultRegistry
来实现。
比如,用一个单独的类来实现协议的注册和启动器的启动:
-
class MyLifecycleObserver(private val registry : ActivityResultRegistry)
-
DefaultLifecycleObserver {
lateinit var getContent : ActivityResultLauncher
fun onCreate(owner: LifecycleOwner) {
getContent = registry.register(“key”, owner, GetContent()) { uri ->
// Handle the returned Uri
}
}
fun selectImage() {
getContent(“image/*”)
}
}
class MyFragment : Fragment() {
lateinit var observer : MyLifecycleObserver
override fun onCreate(savedInstanceState: Bundle?) {
// …
observer = MyLifecycleObserver(requireActivity().activityResultRegistry)
lifecycle.addObserver(observer)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val selectButton = view.findViewById(R.id.select_button)
selectButton.setOnClickListener {
// Open the activity to select an image
observer.selectImage()
}
}
}
复制代码
示例中,我们在MyLifecycleObserver
中实现协议注册和启动器启动,为什么要实现LifecycleObserver
呢?因为,使用生命周期组件,LifecycleOwner
会在 Lifecycle
被销毁时自动移除已注册的启动器。不过,如果 LifecycleOwner 不存在,则每个 ActivityResultLauncher
类都允许您手动调用 unregister()
作为替代。但在使用ActivityResultRegistry时,Google官方强烈建议我们使用可接受 LifecycleOwner
作为参数的 API。
Activity和Fragment中为什么不需要手动调用unregister()
呢?,因为ComponentActivity
和Fragment已经实现了LifecycleObserver
。
ComponentActivity
源码在这里:
[图片上传中…(image-21540-1603979016398-1)]
Fragment中源码在这里:
[图片上传中…(image-2e938f-1603979016398-0)]
总结
新的Activity Result API提供了一种执行许多常见任务的简便方法,比如我们调用第三方APP 获取数据,请求权限、拍照、选图片、获取联系人等等。除此之外,降低了代码的耦合,减少了样板代码(比如,定义requestCode常量)。另外,startActivityForResult
和onActivityResult
已经被废弃,官方也是强烈建议使用这种方式来进行数据传递并获取Activity返回的数据。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/da4174ae5e1a56ccd5bf9da04f456e06.jpeg)
最后
由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
src=“https://i-blog.csdnimg.cn/blog_migrate/da4174ae5e1a56ccd5bf9da04f456e06.jpeg” />
最后
由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。