在以前,我们项目框架通常使用Rxjava+Retrofit+okhttp,所以使用RxPermissions进行动态权限申请就顺理成章了。随着Google新技术的推出,kotlin、jetpack成为大势所趋,如果项目中没有使用Rxjava,那么依赖于Rxjava的RxPermissions也就无法适用了。
RxPermissions 利用了Rxjava的观察者模式,而jetpack也提供了一种可观察的数据存储器类——LiveData,所以可以利用LiveData完成类似RxPermissions的动态权限申请框架,而且LiveData能够感知组件的生命周期,当 Activity 和 Fragment 的生命周期被销毁时,系统会立即退订它们,避免内存泄漏。
如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。
我们需要新建一个Fragment,用这个Fragment来进行权限的申请处理(新建Fragment的用途很广泛,比如Glide就是通过创建一个Fragment,然后通过这个Fragment感知生命周期),需要注意的是如果当前在Fragment里申请权限,需要用fragment.childFragmentManager
,
class LivePermissions {
......
constructor(activity: AppCompatActivity) {
liveFragment = getInstance(activity.supportFragmentManager)
}
constructor(fragment: Fragment) {
liveFragment = getInstance(fragment.childFragmentManager)
}
@Volatile
private var liveFragment: LiveFragment? = null
private fun getInstance(fragmentManager: FragmentManager) =
liveFragment ?: synchronized(this) {
liveFragment ?: if (fragmentManager.findFragmentByTag(TAG) == null) LiveFragment().run {
fragmentManager.beginTransaction().add(this, TAG).commitNow()
this
} else fragmentManager.findFragmentByTag(TAG) as LiveFragment
}
......
}
权限申请方法,在这里调用Fragment的申请申请方法并返回MutableLiveData
,MutableLiveData
是LiveData
的子类,使用setValue
或者postValue
传入数据,通过LiveData.observe(LifecycleOwner,Observer)
传入观察者观察数据更新,这样在Fragment里通过MutableLiveData
将权限申请的结果回调回去。
fun request(vararg permissions: String): MutableLiveData<PermissionResult> {
return this.requestArray(permissions)
}
fun requestArray(permissions: Array<out String>): MutableLiveData<PermissionResult> {
liveFragment!!.requestPermissions(permissions)
return liveFragment!!.liveData
}
创建LiveFragment继承Fragment。在requestPermissions
方法里创建MutableLiveData
并调用Android申请权限的方法,这里在onRequestPermissionsResult
里处理返回结果。
返回结果这里创建了2个ArrayList,分别用来临时保持“拒绝”和“拒绝并不再询问”的权限,最终调用LiveData
的setValue
方法,利用LiveData观察者模式的特性,数据改变时会自动通知观察者。
internal class LiveFragment : Fragment() {
lateinit var liveData :MutableLiveData<PermissionResult>
private val PERMISSIONS_REQUEST_CODE = 100
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true //旋转屏幕时保存Fragment状态
}
@TargetApi(Build.VERSION_CODES.M)
fun requestPermissions(permissions: Array<out String>) {
liveData = MutableLiveData()
requestPermissions(permissions, PERMISSIONS_REQUEST_CODE)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == PERMISSIONS_REQUEST_CODE) {
val denyPermission = ArrayList<String>()
val rationalePermission = ArrayList<String>()
for ((index, value) in grantResults.withIndex()) {
if (value == PackageManager.PERMISSION_DENIED) {
if (shouldShowRequestPermissionRationale(permissions[index])) {
rationalePermission.add(permissions[index])
} else {
denyPermission.add(permissions[index])
}
}
}
if (denyPermission.isEmpty() && rationalePermission.isEmpty()) {
liveData.value = PermissionResult.Grant
} else {
if (rationalePermission.isNotEmpty()) {
liveData.value = PermissionResult.Rationale(rationalePermission.toTypedArray())
} else if (denyPermission.isNotEmpty()) {
liveData.value = PermissionResult.Deny(denyPermission.toTypedArray())
}
}
}
}
}
在最后返回权限的权限处理选项时,使用kotlin的sealed
关键字,来确保在项目里使用when
时能安全的判断每个权限每种可能的情况。
sealed class PermissionResult {
object Grant : PermissionResult() //全部同意
class Deny(val permissions: Array<String>) : PermissionResult() //拒绝且勾选了不再询问,permissions——被拒绝的权限
class Rationale(val permissions: Array<String>) : PermissionResult() //只是拒绝,没有勾选不再询问,permissions——被拒绝的权限
}
代码已托管到github,欢迎star,地址:https://github.com/LGD2009/LivePermissions
项目已发布带jcenter,可直接引用。
implementation 'com.ftd.livepermissions:livepermissions:1.0.0'
//申请权限
LivePermissions(this).request(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
).observe(this, Observer {
when (it) {
is PermissionResult.Grant -> { //权限允许
Toast.makeText(this, "Grant", Toast.LENGTH_SHORT).show()
}
is PermissionResult.Rationale -> { //权限拒绝
it.permissions.forEach {s->
println("Rationale:${s}")//被拒绝的权限
}
Toast.makeText(this, "Rationale", Toast.LENGTH_SHORT)
.show()
}
is PermissionResult.Deny -> { //权限拒绝,且勾选了不再询问
it.permissions.forEach {s->
println("deny:${s}")//被拒绝的权限
}
Toast.makeText(this, "deny", Toast.LENGTH_SHORT).show()
}
}
})