安卓:使用Jetpack中的LiveData实现一个超级简洁的权限申请工具

810 阅读2分钟

前言

随着谷歌的Jetpack在国外普及使用,国内越来越多的开发者开始使用 Kotlin + Jetpack去构建自己的项目。而LiveData是谷歌在Jetpack中为开发者提供的基于观察者模式实现的数据存储器。LiveData不仅可以实现数据观察监听,同时通过LifeCycles内部感知Activity和Fragment的生命周期,在生命周期结束时自动回收,释放资源,防止内存泄漏。

Lifecycles包含有关Activity与Fragment生命周期状态的信息,并允许其他对象观察此状态,
用于观察组件(Activtiy、Fragment)的生命周期状态。

功能实现

通过添加Fragment方式来实现权限的申请,许多框架都是通过添加Fragment的方法实现的,例如:Glide就是通过添加Fragment的方式来感知生命周期的变化。

LiveDataFragment

LiveDataFragment是继承自Fragment,是权限申请的直接发起者,同时也是权限申请结果的回调的直接接收者,并将结果赋值到LiveData的Value上。代码如下:

class LiveDataFragment : Fragment() {

    val liveData by lazy {
        MutableLiveData<PermissionResult>()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        retainInstance = true
    }

    @TargetApi(Build.VERSION_CODES.M)
    fun requestPermission(permissions: Array<out String>) {
        requestPermissions(permissions, PERMISSIONS_REQUEST_CODE)
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == PERMISSIONS_REQUEST_CODE) {
            val denyPermissions = mutableListOf<PermissionBean>()
            grantResults.forEachIndexed { index, result ->
                if (result == PackageManager.PERMISSION_DENIED) {
                    val permission = permissions[index]
                    val isRetryEnable = shouldShowRequestPermissionRationale(permission)
                    denyPermissions.add(PermissionBean(permission, isRetryEnable))
                }
            }
            if (denyPermissions.isEmpty()) {
                liveData.value = PermissionResult.Grant
            } else {
                liveData.value = PermissionResult.Deny(denyPermissions)
            }
        }
    }

    companion object {
        const val PERMISSIONS_REQUEST_CODE = 100;
    }
}

PermissionResult

PermissionResult是权限申请结果数据接收类,将PermissionBean存储到集合中。

sealed class PermissionResult {
    object Grant : PermissionResult()
    class Deny(val permissions: List<PermissionBean>) : PermissionResult()
}

PermissionBean

PermissionBean是单个权限申请状态的存储对象,被PermissionResult持有。

/**
 * @param permissionName 权限名称
 * @param isRetryEnable 被拒权限是否可以再次申请
 */
data class PermissionBean(var permissionName: String, var isRetryEnable: Boolean)

requestPermission

请求权限的方法,使用了Kotlin的语法糖,基于Activity和Fragment的扩展方法,同时也是顶部方法,在任何可以获取到Activity和Fragment的地方都可以执行。

const val TAG = "permission"

fun Fragment.requestPermission(vararg permissions: String) =
    getInstance(childFragmentManager).apply { requestPermission(permissions) }.liveData

fun AppCompatActivity.requestPermission(vararg permissions: String) =
    getInstance(supportFragmentManager).apply { requestPermission(permissions) }.liveData

private fun getInstance(manager: FragmentManager) = synchronized(manager) {
    val findFragment = manager.findFragmentByTag(TAG)
    if (findFragment == null) LiveDataFragment().apply {
        manager.beginTransaction().add(this, TAG).commitNow()
    }
    else findFragment as LiveDataFragment
}

使用方法

使用时,将requestPermission方法复制到一个文件的顶部,即可在Activity或者Fragment中使用requestPermission来请求权限,如图:

代码如下:

class MainActivity : AppCompatActivity() {

    companion object {
        const val TAG = "permissionnnnn"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tvTest.setOnClickListener {
            requestPermission(
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.CAMERA,
                Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.ACCESS_FINE_LOCATION
            ).observe(this, Observer {
                when (it) {
                    is PermissionResult.Grant -> {
                        Log.d(TAG, "申请权限成功")
                    }
                    is PermissionResult.Deny -> {
                        it.permissions.forEach { per ->
                            Log.d(
                                TAG, "拒绝的权限:${per.permissionName} 是否可以重复申请:${per.isRetryEnable}"
                            )
                        }
                    }
                }
            })
        }
    }
    
}

写在最后

身为一名搬砖的技术人员,只有通过持续学习,才能让自己的砖搬得越来越好。