Android面向切面AOP架构设计简析

按照惯例,谈一个框架时我们先说明一下这东西到底是啥、干什么的,首先AOP面向切面和我们通常意义上写的代码不太一样,Java是OOP面向对象,所有的代码都是符合某个功能的,是分门别类好的,但是我们在实际的安卓开发过程中OOP的设计思想是比较难处理一些问题的,比如模块埋点、鉴权以及一些简单但是重复性比较高的代码,如我们要查看个人资料页面就必须先登录,查看个人消息也需要登录。

if(isLogin){
        你的业务逻辑
}else{
        打开登录页面
}

像上面的这种代码会大量的出现在我们的项目中,当然这是比较不太优雅的实现方法,还有像代码埋点,如果说用户登录这个还能勉强做个工具类,但是埋点就真的是毫无办法了,这个时候我们用到AOP就能够优雅的解决问题了。 这个时候就有必要提到一个框架AspectJ,它可以在代码编译期插入代码来实现你的业务需求,这是我的理解,当然如果在网上复制一大段关于它的描述没意思,概念都不是人看的,直接上代码看运行效果,相信大家会有一个比较清晰的认识。 首先我们需要对Gradle进行配置

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'
        //   下面两个是框架的依赖
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

上面是Project的gradle的依赖,以下是app的gradle配置

apply plugin: 'com.android.application'
android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "demo.zhongshi.com.myapplication"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    buildToolsVersion '28.0.2'
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    // 只需下一行的依赖
    implementation 'org.aspectj:aspectjrt:1.8.9'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
// 下面是AspectJ的编译配置文件
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
final def log = project.logger

android.applicationVariants.all{ variant ->
    if (!variant.buildType.isDebuggable()) {
        log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
        return
    }

    JavaCompile javaCompile = variant.javaCompiler
    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)
        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
            }
        }
    }
}

众所周知,我们用javac可以把java编译成class文件,当然我们如果一个类只写了一行代码,class文件是不会生成其他无关的字节码的,这是常识,但是AspectJ是可以生成属于自己规则的字节码,它是遵循java编译的规则并做了自己的处理,所使用的编译器是 Ajc,整个的使用步骤可以分为三个部分,切点、切面、处理,我们先通过一个小demo来看下它的具体玩法,然后再谈这个问题。 新建一个注解,标识为运行期作用,用于对方法切点

/**
 * 利用注解来切点标示方法,作用在运行期
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckLogin {
    String value();
}

主页.png

然后我们看下下面按钮的点击方法处理

@CheckLogin("startPersonalData")
    private void startPersonalData() {
        Log.e("--->","进入个人资料页面");
    }

最后是AspectJ对切点和切面的处理

@Aspect
public class CheckLoginAspectJ {
    /**
     * 找到指定注解的切点
     */
    @Pointcut("execution(@demo.zhongshi.com.myapplication.CheckLogin * *(..))")
    public void executeCheckLogin(){}

    /**
     * 切面
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("executeCheckLogin()")
    public Object checkLogin(ProceedingJoinPoint point) throws Throwable{
        MethodSignature signature = (MethodSignature) point.getSignature();
        CheckLogin checkLogin = signature.getMethod().getAnnotation(CheckLogin.class);
        if(null != checkLogin){
            boolean isLogin = Constants.isLogin();
            if(isLogin){
                Log.e("--->", "当前已登录");
                return point.proceed();
            }else {
                Log.e("--->", "请登陆账号");
                Context context = (Context) point.getThis();
                Intent intent = new Intent(context, LoginActivity.class);
                context.startActivity(intent);
                return null;
            }
        }
        Log.e("--->", "注解为空");
        return point.proceed();
    }
}

当没有登录时的日志输出和显示页面为

未登陆时的打印.png

登录页面.png

然后点击上面的按钮,手动将变量设置为true

登录后的日志输出.png

相信看到这里大家应该有了比较清晰的认识,已经晚上11:42了,明天还要上班,就不深入说明这个框架了,明后两天再深入剖析一下框架的用法和实现原理。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小尘哥的专栏

springboot+druid+mybatis plus的多数据源配置

参考上面的方法,可以随意配置三四五六七八九十个数据源都没问题,有问题欢迎随时来撩!

8934
来自专栏向治洪

Android 应用安装过程分析

在之前的文章中,我们对PakageManagerService启动流程分析 做了简单的介绍,并对PMS系统的启动流程做了详细的解析。上面只是说到了Android...

7059
来自专栏Lambda

spring AOP日志管理

Spring AOP 完成日志记录 SpringAOPAspectJsecurity日志记录 Spring AOP 完成日志记录 1、技术目标 掌握S...

3536
来自专栏熊二哥

Spring.NET的AOP怎么玩

之前公司一直不让使用第三方组件,因此AOP方面的组建一直不能使用,很多面向切面的应用只能通过自己写一些GenericMethod的泛型方法来解决,有一些呆板。由...

1935
来自专栏coding...

Flutter 简易新闻项目目标效果对比简介代码代码地址

使用flutter快速开发 Android 和 iOS 的简易的新闻客户端 API使用的是 showapi(易源数据) 加载热门微信文章

1812
来自专栏小尘哥的专栏

springboot+druid+mybatis plus的多数据源配置

参考上面的方法,可以随意配置三四五六七八九十个数据源都没问题,有问题欢迎随时来撩!

1922
来自专栏MasiMaro 的技术博文

windows 驱动开发入门——驱动中的数据结构

最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书——《独钓寒江 windows安全编程》 和...

1452
来自专栏落影的专栏

Audio Unit播放aac/m4a/mp3等文件

前言 相关文章: 使用VideoToolbox硬编码H.264 使用VideoToolbox硬解码H.264 使用AudioToolbox编码AAC 使...

94010
来自专栏Java 源码分析

SpringBoot 笔记(十一):Servlet容器

2532
来自专栏java相关

基于注解实现SpringBoot多数据源配置

1383

扫码关注云+社区

领取腾讯云代金券