本文涉及到的知识点有: 扩展函数, Lambda 表达式的高级应用
在 Android 6.0 之后系统加强了对敏感权限的管理, 一些敏感权限必须要通过动态权限申请来获得, 本文的内容就从这里展开;
一个正常的权限申请流程大致是这样的:
检查是否存在权限
如果不存在则申请, 存在则进入功能
如果用户拒绝则弹出对话框告知用户权限的用处, 并提供跳转到设置页面的功能;
我使用的是网上比较流行的一个权限申请框架 # https://github.com/tbruyelle /RxPermissions , 当然本文的重点并不是如何使用这个库.
如上所述, 我们在一个应用中可能会有很多需要申请不同权限的位置, 我们应该为每处需要敏感权限的位置做类似的处理. 虽然我们使用了 RxPermissions, 但是还是需要在用户拒绝的位置写大量重复的弹窗提示代码, 这一点也不优雅.
我们都知道在 Kotlin 中函数也是可以作为参数传入的, 说道到这里不知道你有没有想起点什么, 我们在使用 Java 编写 Android 代码时时常使用的各种 Listener, 不就是类似这样的一个情况么?
我们调用 setXXXListener 函数, 并且传入一个接口的匿名内部类实现, 这样的操作我们称之为回调, 我们传入的这个 Listener 被中的方法称为回调函数, 在 Kotlin 里我们也可以按照这种方式来书写:
- //Java 中的匿名内部类, 在 Kotlin 中可以使用 object 实现
- mBtnCallback.setOnClickListener(object :View.OnClickListener{
- override fun onClick(v: View?) {
- println("onclick")
- }
也许你在书写类似代码时已经发现了一些端倪:
Lambda 表达式
所有的类似接口, 在 Kotlin 中都有一些新的签名, 这是因为在 Kotlin 里函数也是参数的一种, 在 Java 中只包含一个方法的接口, 在 Kotlin 中都可以使用 Lambda 表达式来达成一样的效果.
这样做最大的好处就是简化代码, 当我们在阅读代码时更加简洁易读, 比如上述代码完全可以简化成:
mBtnCallback.setOnClickListener { println("onclick") }
非但如此, 以前我们在写一些耗时操作时, 通常需要申明一些接口作为回调函数使用, 在调用时再用匿名内部类来操作得到的结果, 现在你可以这样书写了:
fun doSomeThingNeedTime(call: (response:String) -> Unit) { Thread.sleep(4000) var response = "xxxx" // 我们假装这里进行了网络请求 call(response) } doSomeThingNeedTime { response-> println(response) }
实际应用
在我们实际应用时其实十分简单, 下面的代码也许可以帮你加深对其了解:
// 传入权限与权限描述, 在需要权限的功能打开之前调用 fun Activity.rxRequestPermissions(vararg permissions: String, describe: String, onGranted:()->Unit) { val keylistener = DialogInterface.OnKeyListener { _, keyCode, event -> keyCode == KeyEvent.KEYCODE_BACK && event.repeatCount == 0 } var dialog = AlertDialog.Builder(this) .setTitle("权限申请") .setMessage("${describe} 为必选项, 开通后方可正常使用 APP, 请在设置中开启.") .setOnKeyListener(keylistener) .setCancelable(false) .setPositiveButton("去开启") { _, _ -> // JumpPermissionManagement.GoToSetting(this) finish() } .setNegativeButton("结束") { _, _ -> Toast.makeText(this, "${describe} 权限未开启, 不能使用该功能!", Toast.LENGTH_SHORT).show() finish() } .create() val rxPermissions = RxPermissions(this) // 传递 kotlin 的可变长参数给 Java 的可变参数的时候需要使用修饰符 * ; 这个修饰符叫做 Speread Operator // 它只支持展开的 Array 数组, 不支持 List 集合, 它只用于变长参数列表的实参, 不能重载, 它也不是运算符; rxPermissions.request(*permissions) .subscribe {granted -> if (granted) { onGranted() } else { dialog.show() } } }
上述代码就是我写的 Activity 的一个扩展函数, 用于实现我们前文提到的更方便的动态申请权限, 请看我们的函数申明:
fun Activity.rxRequestPermissions(vararg permissions: String, describe: String, onGranted:()->Unit) { }
我们传入的前三个参数是分别是: 要申请的权限 (对于我们的扩展函数而言是一个可变长参数), 第二个参数我们使用 "key = value" 这种形式传递, 第三个参数就一个回调函数 onGranted:()->Unit;
其中 onGranted, 是我们自己命名的函数名, 冒号后面是我们的函数描述即: 没有传入参数, 返回值类型为 Unit. 在我们的扩展函数体中直接将他作为一个函数调用即可, 需要注意的是必须要填写括号, AS 的自动补全并不会补全这个括号, 没有括号时编译器也不会报错.
如何使用
在打开需要申请权限的功能位置, 我们只要写下以下的数行代码即可:
mBtnRecord.setOnClickListener { rxRequestPermissions(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, describe = "相机, 存储, 录音") { startActivityForResult(Intent(this@MainActivity, VideoRecordActivity::class.java), REQUEST_VIDEO) } }
首先传入需要申请的权限, 权限描述 (key = value), 第三个参数为一个 Lambda 表达式, 这里进行的是存在权限时需要执行的操作. Lambda 表达式作为函数的最后的一个参数时, 我们可以把它放在圆括号外书写.
最终使用时的效果如下:
点击时请求权限
未授予全部权限
来源: https://juejin.im/entry/5bebb829e51d4577ec39c62a