玩转ActivityResultLauncher领略设计之美

2,252 阅读10分钟

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

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

从抽象到实现的一小步

期望中的抽象

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

#mermaid-svg-gCpVepiSBXemaIZj {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-gCpVepiSBXemaIZj .error-icon{fill:#552222;}#mermaid-svg-gCpVepiSBXemaIZj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gCpVepiSBXemaIZj .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-gCpVepiSBXemaIZj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gCpVepiSBXemaIZj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gCpVepiSBXemaIZj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gCpVepiSBXemaIZj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gCpVepiSBXemaIZj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gCpVepiSBXemaIZj .marker.cross{stroke:#333333;}#mermaid-svg-gCpVepiSBXemaIZj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gCpVepiSBXemaIZj .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gCpVepiSBXemaIZj text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-gCpVepiSBXemaIZj .actor-line{stroke:grey;}#mermaid-svg-gCpVepiSBXemaIZj .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-gCpVepiSBXemaIZj .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-gCpVepiSBXemaIZj #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-gCpVepiSBXemaIZj .sequenceNumber{fill:white;}#mermaid-svg-gCpVepiSBXemaIZj #sequencenumber{fill:#333;}#mermaid-svg-gCpVepiSBXemaIZj #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-gCpVepiSBXemaIZj .messageText{fill:#333;stroke:#333;}#mermaid-svg-gCpVepiSBXemaIZj .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gCpVepiSBXemaIZj .labelText,#mermaid-svg-gCpVepiSBXemaIZj .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-gCpVepiSBXemaIZj .loopText,#mermaid-svg-gCpVepiSBXemaIZj .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-gCpVepiSBXemaIZj .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gCpVepiSBXemaIZj .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gCpVepiSBXemaIZj .noteText,#mermaid-svg-gCpVepiSBXemaIZj .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-gCpVepiSBXemaIZj .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gCpVepiSBXemaIZj .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gCpVepiSBXemaIZj .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gCpVepiSBXemaIZj .actorPopupMenu{position:absolute;}#mermaid-svg-gCpVepiSBXemaIZj .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-gCpVepiSBXemaIZj .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gCpVepiSBXemaIZj .actor-man circle,#mermaid-svg-gCpVepiSBXemaIZj line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-gCpVepiSBXemaIZj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Activity/Fragement IPermissionHelper IPermissionChecker 根据生命周期注册权限申请回调 请求权限 内部处理权限是否请求成功 请求权限成功 请求权限成功 请求权限失败 请求权限失败 根据生命周期解绑权限申请回调 Activity/Fragement IPermissionHelper IPermissionChecker

IPermissionHelper 的构建

#mermaid-svg-a0d8lLqwGA5gFEU4 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-a0d8lLqwGA5gFEU4 .error-icon{fill:#552222;}#mermaid-svg-a0d8lLqwGA5gFEU4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a0d8lLqwGA5gFEU4 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-a0d8lLqwGA5gFEU4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a0d8lLqwGA5gFEU4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a0d8lLqwGA5gFEU4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a0d8lLqwGA5gFEU4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a0d8lLqwGA5gFEU4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a0d8lLqwGA5gFEU4 .marker.cross{stroke:#333333;}#mermaid-svg-a0d8lLqwGA5gFEU4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a0d8lLqwGA5gFEU4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-a0d8lLqwGA5gFEU4 .cluster-label text{fill:#333;}#mermaid-svg-a0d8lLqwGA5gFEU4 .cluster-label span{color:#333;}#mermaid-svg-a0d8lLqwGA5gFEU4 .label text,#mermaid-svg-a0d8lLqwGA5gFEU4 span{fill:#333;color:#333;}#mermaid-svg-a0d8lLqwGA5gFEU4 .node rect,#mermaid-svg-a0d8lLqwGA5gFEU4 .node circle,#mermaid-svg-a0d8lLqwGA5gFEU4 .node ellipse,#mermaid-svg-a0d8lLqwGA5gFEU4 .node polygon,#mermaid-svg-a0d8lLqwGA5gFEU4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a0d8lLqwGA5gFEU4 .node .label{text-align:center;}#mermaid-svg-a0d8lLqwGA5gFEU4 .node.clickable{cursor:pointer;}#mermaid-svg-a0d8lLqwGA5gFEU4 .arrowheadPath{fill:#333333;}#mermaid-svg-a0d8lLqwGA5gFEU4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-a0d8lLqwGA5gFEU4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-a0d8lLqwGA5gFEU4 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-a0d8lLqwGA5gFEU4 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-a0d8lLqwGA5gFEU4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-a0d8lLqwGA5gFEU4 .cluster text{fill:#333;}#mermaid-svg-a0d8lLqwGA5gFEU4 .cluster span{color:#333;}#mermaid-svg-a0d8lLqwGA5gFEU4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-a0d8lLqwGA5gFEU4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

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 思维构建

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

#mermaid-svg-cFKUsHhi89OyAHcn {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-cFKUsHhi89OyAHcn .error-icon{fill:#552222;}#mermaid-svg-cFKUsHhi89OyAHcn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cFKUsHhi89OyAHcn .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-cFKUsHhi89OyAHcn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cFKUsHhi89OyAHcn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cFKUsHhi89OyAHcn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cFKUsHhi89OyAHcn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cFKUsHhi89OyAHcn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cFKUsHhi89OyAHcn .marker.cross{stroke:#333333;}#mermaid-svg-cFKUsHhi89OyAHcn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cFKUsHhi89OyAHcn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cFKUsHhi89OyAHcn .cluster-label text{fill:#333;}#mermaid-svg-cFKUsHhi89OyAHcn .cluster-label span{color:#333;}#mermaid-svg-cFKUsHhi89OyAHcn .label text,#mermaid-svg-cFKUsHhi89OyAHcn span{fill:#333;color:#333;}#mermaid-svg-cFKUsHhi89OyAHcn .node rect,#mermaid-svg-cFKUsHhi89OyAHcn .node circle,#mermaid-svg-cFKUsHhi89OyAHcn .node ellipse,#mermaid-svg-cFKUsHhi89OyAHcn .node polygon,#mermaid-svg-cFKUsHhi89OyAHcn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cFKUsHhi89OyAHcn .node .label{text-align:center;}#mermaid-svg-cFKUsHhi89OyAHcn .node.clickable{cursor:pointer;}#mermaid-svg-cFKUsHhi89OyAHcn .arrowheadPath{fill:#333333;}#mermaid-svg-cFKUsHhi89OyAHcn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cFKUsHhi89OyAHcn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cFKUsHhi89OyAHcn .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-cFKUsHhi89OyAHcn .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-cFKUsHhi89OyAHcn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cFKUsHhi89OyAHcn .cluster text{fill:#333;}#mermaid-svg-cFKUsHhi89OyAHcn .cluster span{color:#333;}#mermaid-svg-cFKUsHhi89OyAHcn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cFKUsHhi89OyAHcn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

告知用户权限允许情况

检测判断当前权限情况

通过申请

失败回调

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,那么我们怎么将 IPermissionHelper 与_IPermissionChecker_ 进行一个实际的关联吗?还记得上一小节中 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语法糖简化代码的封装编写。告别笨重的代码编写方式。

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

项目地址:github.com/KilleTom/Kt…

目前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'
	}

本文转自 blog.csdn.net/qq_29856589…,如有侵权,请联系删除。