AOP 技术封装Android权限申请框架

技术调研

相信每位Android开发者,在项目中或多或少也都使用过一些三方权限申请框架,或者直接自己封装的,常见的权限申请方式或框架:

  • PermissionsDispatcher,该框架是基于APT(注解处理器)在编译时生成申请权限的代码,缺点就是只能在Activity 和Fragment中使用,并且APT生成代码会给后期带来APK包体积增大,有时候莫名其妙报红;
  • RxPermission 是基于Rxjava的思想,支持链式调用,使用非常简单方便,缺点也是只能在Activity 和Fragment中使用。
  • 把申请权限的代码封装在BaseXXX中;
  • .................

常见的权限申请框架我就不列举了,这些框架也都基本大同小异,都存在如下缺点:

  • 仅能在Activity和Fragment申请权限。
  • 代码侵入性强。

基础

  • AOP 即:Aspect-Oriented Programming,即面向切面编程。AOP就是把涉及到众多模块的某一类问题进行统一管理。 比如:申请权限的逻辑在多个模块中使用,那么AOP可以把申请权限的逻辑做统一管理。

  • 用过Glide图片加载框架都知道Glide是通过Fragment或Activity监控生命周期的,那么我们是否可以如Glide加载图片监控生命周期,也分装一个没有界面的Fragment或Activity做中间层处理权限呢?下面我们一起来实现。

权限处理PermissionActivity
class PermissionActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //一像素
    window.setGravity(Gravity.LEFT or Gravity.TOP)
    val params = window.attributes
    params.x = 0
    params.y = 0
    params.height = 1
    params.width = 1
    window.attributes = params
    // 获取申请权限数据
    permissions = intent.getStringArrayExtra(PARAM_PERMISSION) ?: arrayOf()
    requestCode = intent.getIntExtra(PARAM_REQUEST_CODE, PARAM_REQUEST_CODE_DEFAULT)

    // 申请权限requestCode不能<0会抛异常
    // permissions也不能空
    // mIPermissionCallback回调
    if (permissions.isEmpty() || requestCode < 0 || mIPermissionCallback == null) {
        finish()
        return
    }

    //检查是否已经获取了权限,即用户已经允许的权限
    if (PermissionUtils.hasSelfPermissions(this, *permissions)) {
        //回调通知用户已经授权
        mIPermissionCallback?.granted()
        this.finish()
        return
    }
    // 申请权限
    ActivityCompat.requestPermissions(this, permissions, requestCode)
}

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)

    //  权限申请成功
    if (PermissionUtils.verifyPermissions(*grantResults)) {
        mIPermissionCallback?.granted()
        finish()
        return
    }

    // 用户拒绝授权,并设置了不再提醒
    if (!PermissionUtils.shouldShowRequestPermissionRationale(this, *permissions)) {
        mIPermissionCallback?.shouldShowRequestPermissionRationale(*permissions)
        finish()
        return
    }

    // 用户拒绝授权
    if (!PermissionUtils.verifyPermissions(*grantResults)) {
        mIPermissionCallback?.denied()
        finish()
        return
    }


    // 用户取消授权
    mIPermissionCallback?.cancel()
    finish()
}

override fun finish() {
    super.finish()
    overridePendingTransition(0, 0)
}

private lateinit var permissions: Array<String>
private var requestCode: Int = PARAM_REQUEST_CODE_DEFAULT


companion object {
    private const val PARAM_PERMISSION = "param_permission"
    private const val PARAM_REQUEST_CODE = "param_request_code"
    private const val PARAM_REQUEST_CODE_DEFAULT = -1
    private var mIPermissionCallback: IPermissionCallback? = null

    @JvmStatic
    fun requestPermissionAction(
        context: Context, permissions: Array<out String>,
        requestCode: Int, callback: IPermissionCallback
    ) = Intent(context, PermissionActivity::class.java).let {
        mIPermissionCallback = callback
        it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
        it.putExtra(PARAM_PERMISSION, permissions)
        it.putExtra(PARAM_REQUEST_CODE, requestCode)
    }.run {
        ActivityCompat.startActivity(context, this, null)
    }
}
}

PermissionActivity 代码不多,和以前权限申请逻辑一样,第一步判断是否已经申请了权限,如果没有则申请权限,当然这里对于权限的处理不过多的介绍。这样封装PermissionActivity 可以解决仅能在Activity和Fragment申请权限的问题,但是和前面说的一样代码侵入性强。所以我们开始引入AOP技术解决问题。

下面开始介绍AOP是如何申请权限的,就是使用一个没有Layout的Activity或Fragment 和AOP技术封装,以下是AOP的代码。

@Aspect
class PermissionAspect {
@Pointcut(
    "execution(@com.youbesun.perform.annotation.Permission * *(..)) && @annotation(permission)"
)
fun permissionMethod(permission: Permission) {//名字和@annotation(permission)保持一致
}


@Around("permissionMethod(permission)")//名字和@annotation(permission)保持一致
@Throws(Throwable::class)
@SuppressWarnings("unused")
fun permissionAspect(
    joinPoint: ProceedingJoinPoint,
    permission: Permission
) {//名字和@annotation(permission)保持一致

    val obj = joinPoint.getThis()//被Aspect的对象
    val context = ContextHelper.findContext(obj)  //你可以拿到上下文对象
    PermissionActivity.requestPermissionAction(
        context,
        permission.value,
        permission.requestCode,
        object : IPermissionCallback {
            override fun granted() {
                joinPoint.proceed(joinPoint.args)
            }

            override fun denied() {
                handleAction(obj, PermissionDenied::class.java)
            }

            override fun shouldShowRequestPermissionRationale(vararg permissions: String) {
                handleAction(obj, ShouldShowRequestRationale::class.java, *permissions)
            }

            override fun cancel() {
                handleAction(obj, PermissionCancel::class.java)
            }
        }
    )
}

@Throws(RuntimeException::class)
private fun handleAction(
    obj: Any,
    annotationClass: Class<out Annotation>,
    vararg permissions: String
) {
    val invokeMethod = findInvokeMethod(obj, annotationClass)
    if (invokeMethod != null) {
        //用户定义了接收shouldShowRequestPermissionRationale的方法,
        // 那么如果方法有返回值,并且是Boolean,那么就是表示是否拦截处理,
        // 一般是shouldShowRequestPermissionRationale方法,返回true表示拦截
        var isIntercepted = invokeMethod.invoke(obj)
        // 如果用户不处理,提示用户那么我们需要跳转系统设置
        val isShowRationale = annotationClass == ShouldShowRequestRationale::class.java
        isIntercepted = (isIntercepted is Boolean) && !isIntercepted
        if (isShowRationale && isIntercepted) {
            PermissionUtils.startAndroidSettings(ContextHelper.findContext(obj), *permissions)
        }
    } else if (annotationClass == ShouldShowRequestRationale::class.java) {
        // 用户不定义接收ShouldShowRequestRationale的方法,那么直接默认跳转系统设置
        PermissionUtils.startAndroidSettings(ContextHelper.findContext(obj), *permissions)
    }
}

private fun findInvokeMethod(obj: Any, annotationClass: Class<out Annotation>): Method? {
    var invokeMethod: Method? = null
    obj.javaClass.declaredMethods.asSequence().forEach {
        if (it.isAnnotationPresent(annotationClass)) {
            it.isAccessible = true
            invokeMethod = it
            return@forEach
        }
    }
    return invokeMethod
}

}

对AOP的处理:

  • 通过Aspectjx 对@Permission注解处进行代码的织入;
  • 然后通过被织入代码的对象反射调用其他方法,这里的对象是要获取Context环境的,因为我们框架需要通过Context启动权限申请的 Activity,我们定义了几个运行时注解@Permission、@PermissionDenied和@ShouldShowRequestRationale注解,分别通过给开发者对权限处理结果的处理;
  • @ShouldShowRequestRationale 需要注意的是被@ShouldShowRequestRationale 注解的方法如果有返回值并且是Boolean类型,那么表示开发者是否拦截权限ShouldShowRequestRationale自己处理,如果没有我们会使用我们自己的处理方式去处理,比如:弹窗让用户选择条状Setttings进行权限的授权。

使用

在root build.gradle引入

classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'

在app build.gradle引入

apply plugin: 'android-aspectjx'

在需要使用android_permission module 中引入:

implementation 'com.github:android_permission_aop:release'

在需要申请权限的方法上加上注解:

@Permission(Manifest.permission.WRITE_EXTERNAL_STORAGE, requestCode = 200)
  fun testPermission() {
   KLogUtil.e("testPermission")
   }

@PermissionDenied(requestCode = 200)
  fun testPermissionDenied() {
  KLogUtil.e("testPermissionDenied")
  }
@PermissionCancel(requestCode = 200)
 fun testPermissionCancel() {
   KLogUtil.e("testPermissionCancel")
 }
 @ShouldShowRequestRationale(requestCode = 200)
 fun testPermissionDeniedAndNotNote():Boolean {
 KLogUtil.e("testPermissionDeniedAndNotNote")
    return true
 }

使用方式非常的简单,只要使用注解对需要申请权限的方法之上添加@Permission注解即可,如果需要做其他处理,你可以选填@PermissionDenied、@ShouldShowRequestRationale和@PermissionCancel等注解分别对用户拒绝权限、用户拒绝权限并勾选禁止、用户取消授权,做不同结果进行处理,按照目前来说,这个框架可以说是非常好用的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

相关阅读更多精彩内容

友情链接更多精彩内容