玩转ActivityResultLauncher领略设计之美
ActivityResultLauncher 作为新一代的 RsultApi,其目的用于简化页面中跳转获取返回值以及请求权限。
但 ActivityResultLauncher 在某些场景下确会出现模板过多,不好用的场景,譬如以下这一场景:ActivityResultLauncher 在一个页面中不同的业务场景请求不同的权限的场景。
面对这样的情况,我们是盲目使用第三库去完成请求权限功能?还是通过一些设计思维对封装 ActivityResultLauncher 使用?这一场景下的 ActivityResultLauncher 何去何从?面对这样的场景。我坚决的奉行自己动手丰衣足食,真的是丰衣给足食开门。
ActivityResultLauncher 优缺点
ActivityResultLauncher 作为新一代的 RsultApi,
优点在于通过单一原则,通过单独对应的回调专门处理对应业务逻辑;譬如下面这一段伪代码对比
//旧时代API处理页面跳转逻辑
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode){
getPerssionPhoneCode->{
//do your logic
}
getImageCode->{
//do your logic
}
// more example
}
}
//新一代api处理方式 以登录为例子
class LoginResultContractDemo : ActivityResultContract<Boolean, Boolean>() {
//跳转登录页面
override fun createIntent(context: Context, input: Boolean?): Intent {
return Intent(context, ActivityResultLauncherLoginDemoActivity::class.java)
}
//处理登录页面回调结果
override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
if (resultCode == Activity.RESULT_OK) {
return intent?.getBooleanExtra(
ActivityResultLauncherLoginDemoActivity.LOGIN_RESULT_TAG,
false) ?: false
}
return false
}
}
class ActivityResultLauncherDemoActivity : AppCompatActivity() {
private val loginLauncher =
registerForActivityResult(LoginResultContractDemo()) {
result ->
//结果回调
Toast.makeText(this,
if (result) "登录成功" else "登录失败", Toast.LENGTH_SHORT).show()
}
override fun onCreate(savedInstanceState: Bundle?) {
//启用launcher去进行页面跳转
toLogin.setOnClickListener {
loginLauncher.launch(false)}
}
}
优点在于:
- ActivityResultContract 作为单一职责:负责处理页面跳转传参以及页面结果的回调处理
- ActivityResultCallback 仅负责将 ActivityResultContract 中的目标页面的期望结果进行输出回调即可
- ActivityResultLauncher 负责将 ActivityResultContract 以及 ActivityResultCallback 关联起来
- 使用这三者抛离旧时代API的中回调各种复杂判断通过使用这样的解耦关系使得每一个页面更加简便清爽,并可以进行抽离封装复用。
缺点在于面对通用场景时,单一原则会导致冗余的模板代码。
下面就用常见的请求权限场景展示这个缺点
class ActivityResultLauncherDemoActivity : AppCompatActivity() {
private val userLocationPermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()) {
map ->
//判断是否有存在用户拒绝某些权限
val failMultiplePermission = ArrayList<String>()
map.forEach {
entry ->
if (!entry.value) {
failMultiplePermission.add(entry.key)
}
}
if (failMultiplePermission.isNullOrEmpty()) {
//获取用户位置信息
toUserLocationLogic()
return@registerForActivityResult
}
//用户位置获取失败逻辑
loseUserLocationLogic()
}
private val imagePermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()) {
map ->
//判断是否有存在用户拒绝某些权限
val failMultiplePermission = ArrayList<String>()
map.forEach {
entry ->
if (!entry.value) {
failMultiplePermission.add(entry.key)
}
}
if (failMultiplePermission.isNullOrEmpty()) {
//获取用户位置信息
getImage()
return@registerForActivityResult
}
//用户位置获取失败逻辑
loseImage()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityResultLauncherDemoBinding.inflate(layoutInflater)
bingding.userLocation.setOnClickListenr{
//请求位置权限
userLocationPermission.launcher(arrayof())
}
bingding.image.setOnClickListenr{
//请求位置权限
imagePermission.launcher(arrayof())
}
}
}
面对上面弊端能不能通过一个抽象方式的思维去简化封装呢?
从抽象到实现的一小步
期望中的抽象
我希望这个权限请求库的流程时序是这样的: