美文网首页
手撸动态权限申请库

手撸动态权限申请库

作者: kwbsky | 来源:发表于2019-12-30 17:22 被阅读0次

自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'

相关文章

网友评论

      本文标题:手撸动态权限申请库

      本文链接:https://www.haomeiwen.com/subject/hoymoctx.html