AOP 技術(shù)封裝Android權(quán)限申請框架

技術(shù)調(diào)研

相信每位Android開發(fā)者,在項(xiàng)目中或多或少也都使用過一些三方權(quán)限申請框架,或者直接自己封裝的,常見的權(quán)限申請方式或框架:

  • PermissionsDispatcher,該框架是基于APT(注解處理器)在編譯時(shí)生成申請權(quán)限的代碼,缺點(diǎn)就是只能在Activity 和Fragment中使用,并且APT生成代碼會給后期帶來APK包體積增大,有時(shí)候莫名其妙報(bào)紅;
  • RxPermission 是基于Rxjava的思想,支持鏈?zhǔn)秸{(diào)用,使用非常簡單方便,缺點(diǎn)也是只能在Activity 和Fragment中使用。
  • 把申請權(quán)限的代碼封裝在BaseXXX中;
  • .................

常見的權(quán)限申請框架我就不列舉了,這些框架也都基本大同小異,都存在如下缺點(diǎn):

  • 僅能在Activity和Fragment申請權(quán)限。
  • 代碼侵入性強(qiáng)。

基礎(chǔ)

  • AOP 即:Aspect-Oriented Programming,即面向切面編程。AOP就是把涉及到眾多模塊的某一類問題進(jìn)行統(tǒng)一管理。 比如:申請權(quán)限的邏輯在多個(gè)模塊中使用,那么AOP可以把申請權(quán)限的邏輯做統(tǒng)一管理。

  • 用過Glide圖片加載框架都知道Glide是通過Fragment或Activity監(jiān)控生命周期的,那么我們是否可以如Glide加載圖片監(jiān)控生命周期,也分裝一個(gè)沒有界面的Fragment或Activity做中間層處理權(quán)限呢?下面我們一起來實(shí)現(xiàn)。

權(quán)限處理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
    // 獲取申請權(quán)限數(shù)據(jù)
    permissions = intent.getStringArrayExtra(PARAM_PERMISSION) ?: arrayOf()
    requestCode = intent.getIntExtra(PARAM_REQUEST_CODE, PARAM_REQUEST_CODE_DEFAULT)

    // 申請權(quán)限r(nóng)equestCode不能<0會拋異常
    // permissions也不能空
    // mIPermissionCallback回調(diào)
    if (permissions.isEmpty() || requestCode < 0 || mIPermissionCallback == null) {
        finish()
        return
    }

    //檢查是否已經(jīng)獲取了權(quán)限,即用戶已經(jīng)允許的權(quán)限
    if (PermissionUtils.hasSelfPermissions(this, *permissions)) {
        //回調(diào)通知用戶已經(jīng)授權(quán)
        mIPermissionCallback?.granted()
        this.finish()
        return
    }
    // 申請權(quán)限
    ActivityCompat.requestPermissions(this, permissions, requestCode)
}

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

    //  權(quán)限申請成功
    if (PermissionUtils.verifyPermissions(*grantResults)) {
        mIPermissionCallback?.granted()
        finish()
        return
    }

    // 用戶拒絕授權(quán),并設(shè)置了不再提醒
    if (!PermissionUtils.shouldShowRequestPermissionRationale(this, *permissions)) {
        mIPermissionCallback?.shouldShowRequestPermissionRationale(*permissions)
        finish()
        return
    }

    // 用戶拒絕授權(quán)
    if (!PermissionUtils.verifyPermissions(*grantResults)) {
        mIPermissionCallback?.denied()
        finish()
        return
    }


    // 用戶取消授權(quán)
    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 代碼不多,和以前權(quán)限申請邏輯一樣,第一步判斷是否已經(jīng)申請了權(quán)限,如果沒有則申請權(quán)限,當(dāng)然這里對于權(quán)限的處理不過多的介紹。這樣封裝PermissionActivity 可以解決僅能在Activity和Fragment申請權(quán)限的問題,但是和前面說的一樣代碼侵入性強(qiáng)。所以我們開始引入AOP技術(shù)解決問題。

下面開始介紹AOP是如何申請權(quán)限的,就是使用一個(gè)沒有Layout的Activity或Fragment 和AOP技術(shù)封裝,以下是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)
        // 如果用戶不處理,提示用戶那么我們需要跳轉(zhuǎn)系統(tǒng)設(shè)置
        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的方法,那么直接默認(rèn)跳轉(zhuǎn)系統(tǒng)設(shè)置
        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注解處進(jìn)行代碼的織入;
  • 然后通過被織入代碼的對象反射調(diào)用其他方法,這里的對象是要獲取Context環(huán)境的,因?yàn)槲覀兛蚣苄枰ㄟ^Context啟動權(quán)限申請的 Activity,我們定義了幾個(gè)運(yùn)行時(shí)注解@Permission、@PermissionDenied和@ShouldShowRequestRationale注解,分別通過給開發(fā)者對權(quán)限處理結(jié)果的處理;
  • @ShouldShowRequestRationale 需要注意的是被@ShouldShowRequestRationale 注解的方法如果有返回值并且是Boolean類型,那么表示開發(fā)者是否攔截權(quán)限ShouldShowRequestRationale自己處理,如果沒有我們會使用我們自己的處理方式去處理,比如:彈窗讓用戶選擇條狀Setttings進(jìn)行權(quán)限的授權(quán)。

使用

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

在需要申請權(quán)限的方法上加上注解:

@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
 }

使用方式非常的簡單,只要使用注解對需要申請權(quán)限的方法之上添加@Permission注解即可,如果需要做其他處理,你可以選填@PermissionDenied、@ShouldShowRequestRationale和@PermissionCancel等注解分別對用戶拒絕權(quán)限、用戶拒絕權(quán)限并勾選禁止、用戶取消授權(quán),做不同結(jié)果進(jìn)行處理,按照目前來說,這個(gè)框架可以說是非常好用的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容