IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 玩转ActivityResultLauncher领略设计之美 -> 正文阅读

[移动开发]玩转ActivityResultLauncher领略设计之美

玩转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())
         }
    }
}

面对上面弊端能不能通过一个抽象方式的思维去简化封装呢?

从抽象到实现的一小步

期望中的抽象

我希望这个权限请求库的流程时序是这样的:

Activity/Fragement IPermissionHelper IPermissionChecker 根据生命周期注册权限申请回调 请求权限 内部处理权限是否请求成功 请求权限成功 请求权限成功 请求权限失败 请求权限失败 根据生命周期解绑权限申请回调 Activity/Fragement IPermissionHelper IPermissionChecker

IPermissionHelper 的构建

Activity/Fragment绑定
请求权限
让checker判断权限请求成功与否
Activity/Fragment解绑
IPermissionHelper
attach
requestPermission
IPermissionChecker
detach

基于上述思路我们可以这样声明 IPermissionHelper

interface IPermissionHelper {

    fun attach(activity: ComponentActivity)

    fun attach(fragment: Fragment)

    fun detach(activity:ComponentActivity)

    fun detach(fragment:Fragment)

    fun requestPermission(
        permission: String,call: ISinglePermissionCheckAction.() -> Unit)

    fun requestPermission(
        permissions:Array<String>,call: IMultilplePermissionCheckAction.() -> Unit)
}

写到这里的时候,同学们肯定有疑问为什么会有:

ISinglePermissionCheckActionIMultilplePermissionCheckAction,这两个权限检查器?其实你可以这样子想在权限申请中有分为单权限请求以及多权限请求这时候,每个权限检查器的目的都是在于判断是否成功然后返回对应的结果。

单权限检查器仅仅关注与当前这一个权限检查器是否用户允许。

多权限检查器不仅需要关注是否全部权限被允许通过还需要告诉开发者有哪些权限用户不允许

IPermissionChecker 思维构建

在这里的权限检查器我希望它能做到这些事情

告知用户权限允许情况
检测判断当前权限情况
通过申请
失败回调
IPermissionHelper
IPermissionChecker
checkAction
okAction
failAction

ISinglePermissionCheckAction 抽象构建

interface ISinglePermissionCheckAction {

    fun checkAction(result: Boolean) {

        if (result) {
            okAction?.invoke()
            return
        }

        failAction?.invoke()
    }

    var okAction: (() -> Unit)?

    var failAction: (() -> Unit)?
}

IMultilplePermissionCheckAction 抽象构建

interface IMultilplePermissionCheckAction {

    fun checkAction(map: Map<String, Boolean>) {

        val failMultiplePermission = ArrayList<String>()

        map.forEach { entry ->

            if (!entry.value) {
                failMultiplePermission.add(entry.key)
            }
        }

        if (failMultiplePermission.isNullOrEmpty()) {

            okAction?.invoke()

            return
        }

        failAction?.invoke(failMultiplePermission)
    }

    var okAction: (() -> Unit)?

    var failAction: ((List<String>) -> Unit)?
}

贯穿抽象的实现

IPermissionHelper 与页面绑定解绑

ActivityResultLauncher 在使用过程中,需要注意的一点是必须在页面处于 Start 生命周期前进行绑定,否则会报错。

那么基于 IPermissionHelper 中的 attach以及detach 我们可以这样实现

class KTSupportPermission() : IPermissionHelper {

    private var singlePermissionLauncher: ActivityResultLauncher<String>? = null
    private var multiplePermissionLauncher: ActivityResultLauncher<Array<String>>? = null

    @Override
    fun attach(activity: ComponentActivity) {

        singlePermissionLauncher = activity.registerForActivityResult(
            ActivityResultContracts.RequestPermission(),
            singlePermissionResultCallBack
        )

        multiplePermissionLauncher = activity.registerForActivityResult(
            ActivityResultContracts.RequestMultiplePermissions(),
            multiplePermissionResultCallback
        )
    }

    @Override
    fun detach(activity: ComponentActivity) {

        singlePermissionLauncher?.unregister()
        multiplePermissionLauncher?.unregister()

        singlePermissionLauncher = null
        multiplePermissionLauncher = null
    }

    @Override
    fun attach(fragment: Fragment) {

        singlePermissionLauncher = fragment.registerForActivityResult(
            ActivityResultContracts.RequestPermission(),
            singlePermissionResultCallBack
        )

        multiplePermissionLauncher = fragment.registerForActivityResult(
            ActivityResultContracts.RequestMultiplePermissions(),
            multiplePermissionResultCallback
        )
    }

    @Override
    fun detach(fragment: Fragment) {

        singlePermissionLauncher?.unregister()
        multiplePermissionLauncher?.unregister()

        singlePermissionLauncher = null
        multiplePermissionLauncher = null
    }
}

IPermissionHelperIPermissionChecker 关联

在上面的小节中我们已经实现 IPermissionChecker,那么我们怎么将 IPermissionHelperIPermissionChecker 进行一个实际的关联吗?还记得上一小节中 ActivityResultLauncher 吗?每一个ActivityResultLauncher 其实都有一个对应的 ActivityResultCallback,那么我们就利用ActivityResultCallback 中的回调接口,将用户允许权限的情况告知到 IPermissionChecker,使用这样的一个方法将它们互相关联起来。

class KTSupportPermission() : IPermissionHelper {

    private val multiplePermissionResultCallback = ActivityResultCallback<Map<String, Boolean>> {

        currentMultilplePermissionCheckAction?.checkAction(it)
    }

    private val singlePermissionResultCallBack = ActivityResultCallback<Boolean> {

        currentSingleCheckAction?.checkAction(it)
    }

  
    private var currentSingleCheckAction: ISinglePermissionCheckAction? 
    									= null
    private var currentMultilplePermissionCheckAction: IMultilplePermissionCheckAction? 
    									= null
    
    @Override
    fun requestPermission(
        permission: String, 
        call: ISinglePermissionCheckAction.() -> Unit) {

        val imp = KTSupportSinglePermissionImp()
        currentSingleCheckAction = imp
        imp.call()

        singlePermissionLauncher?.launch(permission)

    }


    @Override
    fun requestPermission(
        permissions: Array<String>,
        call: IMultilplePermissionCheckAction.() -> Unit) {

        val imp = KTSupportMultiplePermissionImp()
        currentMultilplePermissionCheckAction = imp
        imp.call()

        multiplePermissionLauncher?.launch(permissions)
    }
}

class KTSupportSinglePermissionImp(
    override var okAction: (() -> Unit)? = null,
    override var failAction: (() -> Unit)? = null,
) : ISinglePermissionCheckAction

class KTSupportMultiplePermissionImp(
    override var okAction: (() -> Unit)?=null,
    override var failAction: ((List<String>) -> Unit)?=null,
) : IMultilplePermissionCheckAction

实践调用示例

class MainActivity : AppCompatActivity() {

    private val ktSupportPermission = KTSupportPermission()

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        ktSupportPermission.attach(this)

        binding.getMultipePermission.setOnClickListener {

            val permissions = arrayOf(
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            )
            
            ktSupportPermission
                .requestPermission(permissions) {
                    //理成功后逻辑
                    okAction = {}
                    //处理失败后逻辑
                    failAction = {}
                }
        }

        binding.getSinglePermission.setOnClickListener {

            ktSupportPermission
                .requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
                    //理成功后逻辑
                    okAction = {}
                    //处理失败后逻辑
                    failAction = {}
                }
        }

    }
}

总结

通过本文的学习要区分出新旧时代的优缺点,如何利用抽象方式思维以及DSL语法糖简化代码的封装编写。告别笨重的代码编写方式。

本文中的编写的权限请求现已开源也可通过远程依赖方式进行依赖使用

项目地址:https://github.com/KilleTom/KtJetPackSupportApp

目前KtJetPackSupportApp这个工具库会基于 jetpack 进行不定时更新拓展。方便大家以后的工作以及学习的使用

权限库的使用方式如下:

代码使用如上一小节中的实践调用使用就可

以下是配置依赖方式

//设置支持https://www.jitpack.io maven
allprojects {
		repositories {
			...
			maven { url 'https://www.jitpack.io' }
		}
	}
//需要使用的模块下使用:
dependencies {
	        implementation 'com.github.KilleTom:KtJetPackSupportApp:permission-beta_v1.0.3'
	}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-30 08:51:08  更:2022-04-30 08:51:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 0:01:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码