废话不多说,先看效果
代码语言:text复制 override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_act)
//单个权限
requestPermission(Manifest.permission.READ_PHONE_STATE) { g, d, ad ->
Log.v("granted", "READ_PHONE_STATEis granted $g")
}
//组合权限
requestPermission(
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_CONTACTS
) { g, d, ad ->
Log.v("granted", "READ_PHONE_STATE READ_CONTACTS is granted $g")
}
}
android6.0以上需要动态请求权限,这里不多废话了,想达到一个简单高效的权限使用方式,就跟我一步一步去封装。
之前比较好用的权限库是rxPermissions,但是由于目前的项目都不太想引入rxjava使用,因此我们用Kotlin封装一个属于自己的权限库。
首先我们可以参考下rxPermissions库,代码如下:
代码语言:text复制private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {
RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);
boolean isNewInstance = rxPermissionsFragment == null;
if (isNewInstance) {
rxPermissionsFragment = new RxPermissionsFragment();
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager
.beginTransaction()
.add(rxPermissionsFragment, TAG)
.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
return rxPermissionsFragment;
}
这里我们发现他使用了一个fragment作为媒介接收请求结果,无论在activity或者在fragment中请求时,都会将这个fragment添加到当前页面进行处理,这个思路有点类似glide的生命周期监控,我们可以参考这个思路也创建一个fragment进行操作。
整个fragment中代码部分仅有权限申请回调处理和对rxjava的生命周期处理,这里我们借用这个思路创建自己的permissionFragment即可。
由于lifecycle库拥有自带的生命周期处理,因此我们仅需要写出申请权限的回调即可。
代码如下:
代码语言:text复制class PermissionFragment : Fragment() {
companion object {
private const val TAG = "PermissionFragment"
fun getPermissionFragment(
activity: FragmentActivity,
block: (granted: Boolean, deniedList: Array<String>, alwaysDeniedList: Array<String>) -> Unit
): PermissionFragment {
var permissionFragment = findPermissionsFragment(activity)
if (permissionFragment == null) {
permissionFragment = PermissionFragment()
activity.supportFragmentManager.commit(true) {
add(permissionFragment, TAG)
}
}
permissionFragment.block = block
return permissionFragment
}
private fun findPermissionsFragment(activity: FragmentActivity): PermissionFragment? {
return activity.supportFragmentManager.findFragmentByTag(TAG) as? PermissionFragment
}
}
//参数说明
//granted :申请的权限是否都成功了
//deniedList:被拒绝的权限
//alwaysDeniedList:被点了不再询问并拒绝的权限
//这里的参数回调可以根据需求自行封装。
private var block: (granted: Boolean, deniedList: Array<String>, alwaysDeniedList: Array<String>) -> Unit =
{ _, _, _ -> }
private val launcher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { result ->
lifecycleScope.launchWhenResumed {
dealResult(requireActivity(), result, block)
requireActivity().supportFragmentManager.commit(true) {
remove(this@PermissionFragment)
}
}
}
fun requestPermission(
permissions: Array<String>
) {
lifecycleScope.launchWhenResumed {
launcher.launch(permissions)
}
}
/**
* 处理请求权限结果
*/
private suspend fun dealResult(
activity: FragmentActivity,
result: Map<String, Boolean>,
block: (granted: Boolean, deniedList: Array<String>, alwaysDeniedList: Array<String>) -> Unit,
) = withContext(Dispatchers.IO) {
val allDeniedList = result.filterValues { !it }.mapNotNull { it.key }
val alwaysDeniedList =
allDeniedList.filter {
ActivityCompat.shouldShowRequestPermissionRationale(
activity,
it
)
}
val deniedList = allDeniedList - alwaysDeniedList.toSet()
var granted = true
if (allDeniedList.isNotEmpty()) {
granted = false
}
withContext(Dispatchers.Main) {
block(granted, deniedList.toTypedArray(), alwaysDeniedList.toTypedArray())
}
}
}
到这一步,我们完成了fragment的开发,但是想要更简单的使用,我们需要借助kotlin扩展方法来使用,我们封装一个更方便的扩展方法以便调用,代码如下:
代码语言:text复制fun LifecycleOwner.requestPermission(
block: (granted: Boolean, deniedList: Array<String>, alwaysDeniedList: Array<String>) -> Unit,
permissions: Array<String>
) {
runCatching {
when (this@requestPermission) {
is FragmentActivity -> {
PermissionFragment.getPermissionFragment(this@requestPermission, block)
.requestPermission(permissions)
}
is Fragment -> {
PermissionFragment.getPermissionFragment(
this@requestPermission.requireActivity(),
block
)
.requestPermission(permissions)
}
else -> {
throw RuntimeException("requestPermission LifecycleOwner必须是activity或fragment")
}
}
}.onFailure {
block(false, permissions, arrayOf())
}
}
上述封装了一个初步可用的扩展方法,但是为了更方便使用,我们进一步封装,单个,两个组合,三个组合的方法,更方便使用,代码如下:
代码语言:text复制//单个权限申请
fun LifecycleOwner.requestPermission(
p1: String,
block: (granted: Boolean, deniedList: Array<String>, alwaysDeniedList: Array<String>) -> Unit
) {
requestPermission(block, arrayOf(p1))
}
//两个个权限组合申请
fun LifecycleOwner.requestPermission(
p1: String,
p2: String,
block: (granted: Boolean, deniedList: Array<String>, alwaysDeniedList: Array<String>) -> Unit
) {
requestPermission(block, arrayOf(p1, p2))
}
//三个权限组合申请
fun LifecycleOwner.requestPermission(
p1: String,
p2: String,
p3: String,
block: (granted: Boolean, deniedList: Array<String>, alwaysDeniedList: Array<String>) -> Unit
) {
requestPermission(block, arrayOf(p1, p2, p3))
}
//四个权限组合申请
fun LifecycleOwner.requestPermission(
p1: String,
p2: String,
p3: String,
p4: String,
block: (granted: Boolean, deniedList: Array<String>, alwaysDeniedList: Array<String>) -> Unit
) {
requestPermission(block, arrayOf(p1, p2, p3, p4))
}
至此,权限Util已经能达到文章开头的使用效果。
//本代码均未完整测试,建议自行测试后使用。
项目地址