自6.0版本开始,系统增加了权限申请。申请了以后,在Activity中回调onRequestPermissionsResult方法,然后做结果判断。但是这种方法侵入性太强,后面出来了EasyPermission,直接在入参传入的回调中便可得到结果。他的实现原理是在当前activity中创建fragment去请求权限,便可在fragment的回调中得到结果。思路和glide的生命周期管理是一样的。
上周领导让我修改一个sdk,因为代码比较老,所以需要增加动态权限这块内容。考虑到sdk需要尽可能做到调用简单,侵入性低,我决定手撸一个动态权限申请库。因为需求简单,所以整个库的实现也非常简单。
首先需要一个外观类供客户端调用。
class DynamicPermission private constructor() {
private val requestManagerRetriever: RequestManagerRetriever = RequestManagerRetriever()
private object Holder {
val INSTANCE = DynamicPermission()
}
fun requestPermissions(context: Context, permissions: Array<String>, grant: () -> Unit, deny: () -> Unit) {
assertActivity(context)
val requestManager = requestManagerRetriever.fetchRequestManager(context)
requestManager?.requestPermission(context, permissions, grant, deny)
}
private fun assertActivity(context: Context) {
if (context !is Activity) {
throw IllegalArgumentException("Context must be subclass of Activity")
}
}
companion object {
val instance: DynamicPermission
get() = Holder.INSTANCE
}
}
由于是在当前activity中创建fragment,所以传入的context必须为activity及其子类。fragment中创建childframent这种情况我们不考虑。
为了节省内存便于管理,一个activity肯定只能对应一个fragment。因为fragment有app包下的和v4包下的,所以两种情况都需要考虑到。
v4包下的:
class SupportRequestManagerFragment : Fragment() {
internal var requestManager: RequestManager? = null
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
requestManager?.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
app包下的:
class RequestManagerFragment : Fragment() {
internal var requestManager: RequestManager? = null
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
requestManager?.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
RequestManager是核心类,起承上启下的作用。一方面接受外观类的委托去请求权限,另一方面又处理fragment的权限处理结果的回调。
class RequestManager : Listener {
private val call = PermissionCall()
private var grant: (() -> Unit)? = null
private var deny: (() -> Unit)? = null
internal fun requestPermission(context: Context, permissions: Array<String>, grant: () -> Unit, deny: () -> Unit) {
this.grant = grant
this.deny = deny
when(!needRequest(context, permissions)) {
true -> grant.invoke()
else -> call.requestPermission(context, permissions)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (requestCode == PERMISSION_REQUEST_CODE) {
if (PermissionHandler.checkEachPermissionsGranted(grantResults)) {
grant?.invoke()
} else {
deny?.invoke()
}
}
}
private fun needRequest(context: Context, permissions: Array<String>): Boolean {
return !(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || !PermissionHandler.checkPermission(context as Activity, permissions))
}
}
先说权限请求。如果api版本小于23或者权限已经都授予了,就直接调用grant方法,否则call#requestPermission。
class PermissionCall {
private val mainHandler = Handler(Looper.getMainLooper())
internal fun requestPermission(context: Context, permissions: Array<String>) {
when(context){
is FragmentActivity -> {
mainHandler.post {
val fragment = context.supportFragmentManager.findFragmentByTag("tag_fragment")
if (fragment != null) {
PermissionHandler.requestPermissions(context, permissions, fragment)
}
}
}
is Activity -> {
mainHandler.post {
val fragment = context.fragmentManager.findFragmentByTag("tag_fragment")
if (fragment != null) {
PermissionHandler.requestPermissions(context, permissions, fragment)
}
}
}
}
}
}
很简单,就是分两种fragment对应的activity进行判断,然后真正去请求权限:
internal fun requestPermissions(context: Context, permissions: Array<String>, fragment: Fragment) {
if (checkPermission(context as Activity, permissions)) {
fragment.requestPermissions(permissions, PERMISSION_REQUEST_CODE)
}
}
internal fun requestPermissions(context: Context, permissions: Array<String>?, fragment: android.app.Fragment) {
if (checkPermission(context as Activity, permissions)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
fragment.requestPermissions(permissions, PERMISSION_REQUEST_CODE)
}
}
}
这里需要注意的一点,请求的代码需要加入主线程的队列中,而不是立即执行。为什么?因为fragment的添加过程是加入主线程队列中的。如果不太了解的同学,需要自行去学习一下。
到这里整个结构就说完了,是不是很简单?最后还要说一下requestManager是如何创建和被管理的。
class RequestManagerRetriever {
private val handlingSupportTransaction = HashMap<FragmentManager, SupportRequestManagerFragment>()
private val mainHandler = Handler(Looper.getMainLooper())
private val handlingTransaction = HashMap<android.app.FragmentManager, RequestManagerFragment>()
internal fun fetchRequestManager(context: Context?): RequestManager? {
if (context != null && onUiThread()) {
if (context is FragmentActivity) {
return fetchRequestManager(context as FragmentActivity?)
} else if (context is Activity) {
return fetchRequestManager(context as Activity?)
}
}
return null
}
private fun fetchRequestManager(activity: FragmentActivity?): RequestManager? {
if (activity != null && !activity.isDestroyed) {
val fragment = fetchSupportFragment(activity.supportFragmentManager)
var requestManager: RequestManager? = fragment.requestManager
if (requestManager == null) {
requestManager = RequestManager()
fragment.requestManager = requestManager
}
return requestManager
}
return null
}
private fun fetchRequestManager(activity: Activity?): RequestManager? {
if (activity != null && !activity.isDestroyed) {
val fragment = fetchFragment(activity.fragmentManager)
var requestManager = fragment.requestManager
if (requestManager == null) {
requestManager = RequestManager()
fragment.requestManager = requestManager
}
return requestManager
}
return null
}
private fun fetchSupportFragment(fm: FragmentManager): SupportRequestManagerFragment {
var fragment = fm.findFragmentByTag(TAG) as SupportRequestManagerFragment?
if (fragment == null) {
fragment = handlingSupportTransaction[fm]
if (fragment == null) {
fragment = SupportRequestManagerFragment()
handlingSupportTransaction[fm] = fragment
fm.beginTransaction().add(fragment, TAG).commitAllowingStateLoss()
mainHandler.post { handlingSupportTransaction.remove(fm) }
}
}
return fragment
}
private fun fetchFragment(fm: android.app.FragmentManager): RequestManagerFragment {
var fragment: RequestManagerFragment? = fm.findFragmentByTag(TAG) as RequestManagerFragment
if (fragment == null) {
fragment = handlingTransaction[fm]
if (fragment == null) {
fragment = RequestManagerFragment()
handlingTransaction[fm] = fragment
fm.beginTransaction().add(fragment, TAG).commitAllowingStateLoss()
mainHandler.post { handlingTransaction.remove(fm) }
}
}
return fragment
}
private fun onUiThread(): Boolean {
return Looper.myLooper() == Looper.getMainLooper()
}
companion object {
private val TAG = "tag_fragment"
}
}
就是分两种情况去获取activity对应的fragment。因为要一一对应,所以不是每次都去创建fragment,而是判断如果没有再去创建,有的话通过tag取出。这里要特别注意一点,前面说过fragment的添加需要加入到主线程队列,不会马上执行,所以可能会碰到一种情况,添加fragment的代码已经被加入了队列但并未执行,此时findFragmentByTag返回的fragment为空,然后fetchSupportFragment方法又被调用了一次,这时候就会重复去添加。这里我借鉴了glide的处理办法,用一个map去存fragment,只要fragment创建了就保存。下一次再要获取的时候先看一下map中有没有,如果通过tag没有获取到但是map中有的话说明刚好是上面分析的这种情况,即在添加中。那么相应的,添加完成后还要把map清空,也需要放入消息队列中。
这样整个库就完成了。
项目已放到github:https://github.com/birdwang/DynamicPermission.git
android studio 依赖: implementation 'com.github.birdwang:map:1.0.0'
网友评论