【问题标题】:Pass different functions with different method signatures to a function as a parameter in kotlin将具有不同方法签名的不同函数作为kotlin中的参数传递给函数
【发布时间】:2020-09-13 13:55:57
【问题描述】:

我正在开发一个使用 kotlin 作为主要语言的 android 应用程序。我需要为应用程序的某些部分询问一些运行时权限。因此,我决定编写一个单独的静态函数来检查权限并运行一个方法,而不是编写类似的样板代码来请求权限。这是静态函数

fun permissionExecution(childFragment: Fragment, permission: String, expression: ????) {

    Dexter.withActivity(childFragment.requireActivity())
        .withPermission(permission)
        .withListener(object : PermissionListener {
            override fun onPermissionGranted(response: PermissionGrantedResponse?) {
                expression()
            }

            override fun onPermissionRationaleShouldBeShown(
                permission: PermissionRequest?,
                token: PermissionToken
            ) {
                token.continuePermissionRequest()
            }

            override fun onPermissionDenied(response: PermissionDeniedResponse) {
                if(response.isPermanentlyDenied) {
                    openSettings(childFragment)
                }
            }

        }).check()
}

当我传入不带参数的方法时,这段代码可以完美运行。但是在某些情况下,我想传入具有不同参数类型的方法。

  1. 像这样调用方法permissionExecution(childfragment, permission, foo(string))
  2. 像这样调用同样的方法permissionExecution(childfrgment, permission, bas(string, Int))

permissionExecution() 方法中的 espression 参数可以使用什么类类型

【问题讨论】:

  • 那么,当onPermissionGranted 时,您想从不同的屏幕执行一些 lambda,对吗? onPermissionGranted 里面只有expression() 吗?
  • 我会说重载函数,因为如果你动态地获取函数,你仍然需要在调用它之前通过检查知道里面的东西。
  • @sonnet,是的。让我这样解释一下,写入外部存储,读取手机联系人,使用手机摄像头等过程都需要不同的android访问权限,这需要类似的请求权限方法。首先,您检查权限,如果授予您运行一段代码(即表达式方法)。大多数时候,由于方法签名,每个表达式方法都不同。我正在寻找表达式的全部方法签名。
  • @AnimeshSahu,编写重载将需要我为每个方法签名编写单独的签名,这是我首先要避免的。正在调用的函数除了需要调用之外,对permissionExecution 函数没有影响。
  • 我不明白您为什么需要更改expression 的签名。 () -> Unit 很好,因为你的函数 permissionExecution 很常见。只需从其他类传递您想要的 lambda。

标签: kotlin android-permissions higher-order-functions


【解决方案1】:

通过调整解决方案here,我找到了一个更好的方法,首先,创建一个这样的界面

interface RunExpression{
fun expression()}

然后使用函数签名中的接口

fun permissionExecution(childFragment: Fragment, permission : String, runExpression: RunExpression) {

Dexter.withActivity(childFragment.requireActivity())
    .withPermission(permission)
    .withListener(object : PermissionListener {
        override fun onPermissionGranted(response: PermissionGrantedResponse?) {
            runExpression.expression()
        }

        override fun onPermissionRationaleShouldBeShown(
            permission: PermissionRequest?,
            token: PermissionToken
        ) {
            token.continuePermissionRequest()
        }

        override fun onPermissionDenied(response: PermissionDeniedResponse) {
            if(response.isPermanentlyDenied) {
                openSettings(childFragment)
            }
        }

    }).check()}

然后在我想调用函数的地方,我将实现接口的匿名对象保存到变量中

val startMyActivity = object : RunExpression {
            override fun expression() {
                startActivityForResult(intent, PICK_CONTACT)
            }
        }

然后用变量调用函数

permissionExecution(this, Manifest.permission.READ_CONTACTS, startMyActivity)

【讨论】:

    【解决方案2】:

    这是一个不太优雅的解决方案。我用这样的单一方法接口创建了一个包装类

    class Permissions(val childFragment: Fragment, private val permission: String, val runExpression: RunExpression) {
    interface RunExpression{
        fun expression()
    }
    
    fun permissionExecution() {
    
        Dexter.withActivity(childFragment.requireActivity())
            .withPermission(permission)
            .withListener(object : PermissionListener {
                override fun onPermissionGranted(response: PermissionGrantedResponse?) {
                    runExpression.expression()
                }
    
                override fun onPermissionRationaleShouldBeShown(
                    permission: PermissionRequest?,
                    token: PermissionToken
                ) {
                    token.continuePermissionRequest()
                }
    
                override fun onPermissionDenied(response: PermissionDeniedResponse) {
                    if(response.isPermanentlyDenied) {
                        openSettings(childFragment)
                    }
                }
    
            }).check()
    }
    

    }

    然后每次我需要权限时都像这样调用类

    1.

    Permissions(this, Manifest.permission.READ_CONTACTS, object : Permissions.RunExpression {
                    override fun expression() {
                        startActivityForResult(intent, PICK_CONTACT)
                    }
                }).permissionExecution()
    

    2.

    Permissions(this, Manifest.permission.READ_CONTACTS, object : Permissions.RunExpression {
                    override fun expression() {
                        writeFileToLocation(file, locationPath)
                    }
                }).permissionExecution()
    

    【讨论】:

    • 您可以对您在发布问题的代码中使用的签名执行相同的操作。有什么区别?
    • @AnimeshSahu,我上面提供的解决方案使用具有单一方法接口的类来实现。但我的询问是想知道是否存在一种方法,我可以使用带有方法签名 permissionExecution(childFragment: Fragment, permission: String, expression: ????) 的函数,其中 ???? 是代表任意方法的类型。
    • 你可以放同样的签名???如() -> Unit 获得完全相同的结果,请致电permissionExecution(childfragment, permission) { startActivityForResult(...) }
    • 以这种方式调用它仅适用于具有签名 method1() 的方法和形式 method2(first, second) 的方法2 将需要另一个重载签名 (firstType, secondType) -> Unit 并且对于具有不同签名的所有方法,我需要为他们写另一个不是我想要的重载
    • 感谢您指出这一点,我现在完全明白了。猜猜我应该后退一点,看看@Tenfour04提供的解决方案
    【解决方案3】:

    在 lambda 参数的定义中包含函数参数是没有意义的。您已经拥有调用这些其他函数所需的一切:

    permissionExecution(myFragment, Manifest.permission.RECORD_AUDIO) {
        foo(myString)
    }
    
    permissionExecution(myFragment, Manifest.permission.CAMERA) {
        bar(myString, myInt)
    }
    

    如果您需要PermissionGrantedResponse 来确定这些参数是什么,您可以将其定义为函数输入:

    fun permissionExecution(childFragment: Fragment, permission: String, expression: (PermissionGrantedResponse) -> Unit) {
    
        Dexter.withActivity(childFragment.requireActivity())
            .withPermission(permission)
            .withListener(object : PermissionListener {
                override fun onPermissionGranted(response: PermissionGrantedResponse) {
                    expression(response)
                }
        //...
    }
    
    //...
    
    permissionExecution(myFragment, Manifest.permission.RECORD_AUDIO) { response ->
        foo(response.permissionName)
    }
    

    【讨论】:

    • 谢谢,这正是我要找的
    【解决方案4】:

    如果我理解正确,您已经实现了一个通用的静态函数,它检查权限并将一些 lambda 传递给它,当授予权限时将调用该函数。我不明白为什么你这些 lambdas 需要一些参数。下面的实现是你想要的吗?

    class CameraFragment {
    
      fun onCreateView() {
        permissionExecution(childFragment = arg1, permission = "perm", expression = {
          // open camera
        })
      }
    }
    
    class LocationActivity {
      fun onCreate() {
        permissionExecution(childFragment = arg1, permission = "perm", expression = {
          fetchLocation()
        })
      }
    
      fun fetchLocation() {
        // get location, do stuff
      }
    }
    

    【讨论】:

    • 感谢您的关注。但是让我重申一下问题的要点,permisssionExecution 方法是静态方法。我对一种表达式类型感兴趣,只要它的返回类型是单位,它就可以使用任何输入参数类型的任何函数。
    • 没有这样的东西。具有不同数量参数的函数的共同祖先是AnypermissionExecution 函数如何知道为这些参数传递什么?
    • 感谢您的关注。但是让我重申一下问题的要点,permisssionExecution 方法是静态方法。我对一个表达式类型感兴趣,只要它的返回类型是单位,它就可以使用任何输入参数类型的任何函数。表达式可以是startActivityForResult(intent, PICK_CONTACT)writeFileToLocation(file, locationPath) 等。
    • @ilatyphi95 如果您在问题中编写了一些实际用例,即使使用伪代码,那么这将有助于人们更好地理解您想要实现的目标。
    猜你喜欢
    • 2022-01-10
    • 2018-01-10
    • 2020-01-26
    • 1970-01-01
    • 2014-12-12
    • 2015-11-30
    • 1970-01-01
    • 2013-01-30
    • 2012-10-27
    相关资源
    最近更新 更多