APP & programAndroid Other

Android登录拦截的场景-面向切面基于AOP实现

2022-10-30  本文已影响0人  安安_660c

一、了解面向切面AOP

我们学习Java的开始,我们一直就知道 OOP 面向对象,其实 AOP 面向切面,是对OOP的一个补充,AOP采取横向收取机制,取代了传统纵向继承体系重复性代码,把某一类问题集中在一个地方进行处理,比如处理程序中的点击事件、打印日志等。

AOP是编程思想就是把业务逻辑和横切问题进行分离,从而达到解耦的目的,提高代码的重用性和开发效率。OOP的精髓是把功能或问题模块化,每个模块处理自己的家务事。但在现实世界中,并不是所有功能都能完美得划分到模块中。AOP的目标是把这些功能集中起来,放到一个统一的地方来控制和管理。

我记得我最开始接触 AOP 还是在JavaEE的框架SSH的学习中,AspectJ框架,开始流行于后端,现在在Android开发的应用中也越来越广泛了,Android中使用AspectJ框架的应用也有很多,比如点击事件防抖,埋点,权限申请等等不一而足,这里不展开说明,毕竟我们这一期不是专门讲AspectJ的应用的。

简单的说一下AOP的重点概念:

二、集成AOP框架

Java项目集成

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}

组件build.gradle

dependencies {
    implementation 'org.aspectj:aspectjrt:1.9.6'
}

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

// 获取log打印工具和构建配置
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
    if (!variant.buildType.isDebuggable()) {
        // 判断是否debug,如果打release把return去掉就可以
        log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
        // return;
    }
    // 使aspectj配置生效
    JavaCompile javaCompile = variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.8",
                         "-inpath", javaCompile.destinationDir.toString(),
                         "-aspectpath", javaCompile.classpath.asPath,
                         "-d", javaCompile.destinationDir.toString(),
                         "-classpath", javaCompile.classpath.asPath,
                         "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
        log.debug "ajc args: " + Arrays.toString(args)

        MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler);
        //在编译时打印信息如警告、error等等
        for (IMessage message : handler.getMessages(null, true)) {
            switch (message.getKind()) {
                case IMessage.ABORT:
                case IMessage.ERROR:
                case IMessage.FAIL:
                    log.error message.message, message.thrown
                    break;
                case IMessage.WARNING:
                    log.warn message.message, message.thrown
                    break;
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break;
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break;
            }
        }
    }
}

Kotlin项目集成

dependencies {
        classpath 'com.android.tools.build:gradle:3.6.1'

        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

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

项目build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

apply plugin: 'android-aspectjx'

android {
    ...

    // AOP 配置
    aspectjx {
        // 排除一些第三方库的包名(Gson、 LeakCanary 和 AOP 有冲突)
        exclude 'androidx', 'com.google', 'com.squareup', 'com.alipay', 'com.taobao',
                'org.apache',
                'org.jetbrains.kotlin',
                "module-info", 'versions.9'
    }

}

ependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'org.aspectj:aspectjrt:1.9.5'
}

集成AOP踩坑: zip file is empty

和第三方包有冲突,比如Gson,OkHttp等,需要配置排除一下第三方包,

gradle版本兼容问题

AGP版本4.0以上不支持 推荐使用3.6.1

kotlin兼容问题 :

基本都是推荐使用 com.hujiang.aspectjx

编译版本兼容问题:

4.0以上使用KT编译版本为Java11需要改为Java8

组件化兼容问题:

如果在library的moudle中自定义的注解, 想要通过AspectJ来拦截织入, 那么这个@Aspect类必须和自定义的注解在同一moudle中, 否则是没有效果的

等等...

难点就在集成,如何在指定版本的Gradle,Kotlin项目中集成成功。

三、定义注解实现功能

定义标记的注解

//不需要回调的处理
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
}

定义处理类

@Aspect
public class LoginAspect {

    @Pointcut("@annotation(com.guadou.kt_demo.demo.demo3_bottomtabbar_fragment.aop.Login)")
    public void Login() {
    }

     //不带回调的注解处理
    @Around("Login()")
    public void loginJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        YYLogUtils.w("走进AOP方法-Login()");
        Signature signature = joinPoint.getSignature();

        if (!(signature instanceof MethodSignature)){
            throw new RuntimeException("该注解只能用于方法上");
        }

        Login login = ((MethodSignature) signature).getMethod().getAnnotation(Login.class);
        if (login == null) return;

        //判断当前是否已经登录
        if (LoginManager.isLogin()) {
            joinPoint.proceed();
        } else {
            //如果未登录,去登录页面
            LoginManager.gotoLoginPage();
        }
    }
object LoginManager {

    @JvmStatic
    fun isLogin(): Boolean {
        val token = SP().getString(Constants.KEY_TOKEN, "")
        YYLogUtils.w("LoginManager-token:$token")
        val checkEmpty = token.checkEmpty()
        return !checkEmpty
    }

    @JvmStatic
    fun gotoLoginPage() {
        commContext().gotoActivity<LoginDemoActivity>()
    }
}

其实逻辑很简单,就是判断是否登录,看是放行还是跳转到登录页面

使用的逻辑也是很简单,把需要处理的逻辑使用方法抽取,并标记注解即可

    override fun init() {

        mBtnCleanToken.click {
            SP().remove(Constants.KEY_TOKEN)
            toast("清除成功")
        }

        mBtnProfile.click {

           //不带回调的登录方式
           gotoProfilePage2()
        }

    }

    @Login
    private fun gotoProfilePage2() {
        gotoActivity<ProfileDemoActivity>()
    }

来自:https://juejin.cn/post/7132643283083198501

上一篇 下一篇

猜你喜欢

热点阅读