前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android面向切面AOP架构设计简析

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

作者头像
萬物並作吾以觀復
发布2018-10-10 10:09:45
7060
发布2018-10-10 10:09:45
举报

按照惯例,谈一个框架时我们先说明一下这东西到底是啥、干什么的,首先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了,明天还要上班,就不深入说明这个框架了,明后两天再深入剖析一下框架的用法和实现原理。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.09.17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档