美文网首页Android收藏集
Android面向AOP之AspectJ的使用篇

Android面向AOP之AspectJ的使用篇

作者: Goach | 来源:发表于2018-07-11 14:23 被阅读41次

    前言

    AOP,它不是一门新语言,是一种面向切面的思想。它主要的作用是把一些具有相同属性或者相同功能的代码抽离出来形成一个切面,从而实现面向切面编程!而AspectJ就是基于Java语言实现AOP这种思想的一个框架。

    Java之安装AspectJ

    AspectJ官网下载Jar包,然后在下载目录执行下面的命令安装

     java -jar aspectj-1.x.x.jar 
    

    安装完后如下图


    image.png
    • bin对应的是编译命令,常用ajc.bat
    • doc 放的是一些文档
    • lib里面放的是一些AspectJ的一些库。

    知道大概得结构后,再来看看Android的使用方法。

    Android之集成AspectJ

    project之build.gradle

    dependencies {
            classpath group:'org.aspectj',name:'aspectjtools',version:'1.9.1'
        }
    

    app之build.gradle

    import org.aspectj.tools.ajc.Main
    
    //配置aspectJ
    android.applicationVariants.all{
        //编译Java代码的任务
        JavaCompile javaCompile = it.javaCompile
        javaCompile.doLast {
            println "在编译之后执行"
            //执行 aspectJ 修改字节码的操作
            String[] args = [
                    "-1.7",
                    "-inpath",javaCompile.destinationDir.toString(),
                    "-d",javaCompile.destinationDir.toString(),
                    "-aspectpath",javaCompile.classpath.asPath,
                    "-classpath",javaCompile.classpath.asPath,
                    "-bootclasspath",project.android.bootClasspath.join(File.pathSeparator)
            ]
    
            new Main().runMain(args,false)
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation group:'aspectj',name:'aspectjrt',version:'1.5.4'
    }
    

    一个添加事务的例子

    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="添加事务"
            android:onClick="hello"/>
    
    public void hello(View view){
            Log.d(TAG,"hello aspectJ");
    }
    

    这是一个按钮点击事件,下面通过AOP对它进行添加事务
    第一步

    @Retention(RetentionPolicy.CLASS)
    @Target(ElementType.METHOD)
    public @interface Transaction {
    }
    

    第二步

    @Aspect
    public class TransactionAspect {
        private static final String TAG = "TransactionAspect";
        private static final String TRANSACTION_METHOD =
                "execution(@com.goach.myaspectj.annotation.Transaction * *(..))";
        @Pointcut(TRANSACTION_METHOD)
        public void transactionMethod(){}
    
        @Around("transactionMethod()")
        public void addTransaction(ProceedingJoinPoint joinPoint){
            Log.d(TAG,"开始事务....");
            try {
                joinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            Log.d(TAG,"结束事务....");
        }
    }
    

    第三步

    @Transaction//加上注解
    public void hello(View view){
            Log.d(TAG,"hello aspectJ");
    }
    

    最后执行结果


    image.png

    这里只要添加注解,没动hello里面的一行代码就添加了事务。这就是一个简单的AOP编程

    在上面引出了个@Aspect,Join Points,@Pointcut, @Around这样的概念,

    • @Aspect 修饰一个类,这样就会把这个类当一个Bean来处理
    • Join Points 程序运行执行点,比如上面的execution就是其中的一种类型,还有Call类型等
    • Pointcut 选出我们需要的Join Points,如
    @Pointcut("execution(@com.goach.myaspectj.annotation.Transaction * *(..))")
    

    execution指的是类型,com.goach.myaspectj.annotation.Transaction指的是包名+对应的注解名,第一个* 指的是返回值为任意类型,第二个*指的是方法名为任意类型或者是构造器的话使用,new代替,(..)指的是任意类型的参数。

    • Around advice的类型之一,还有beforeafter,而Around会替代原来的JPoint,如果需要执行原来的JPoint
    try {
          joinPoint.proceed();
    } catch (Throwable throwable) {
         throwable.printStackTrace();
    }
    
    

    还有一些其他用法:

    • 获取方法的注解
    MethodSignature signature = (MethodSignature)joinPoint.getSignature();
    Method method = signature.getMethod();
    Permission permission = method.getAnnotation(Permission.class);
    
    • 获取当前得上下文
    (Context) joinPoint.getTarget()
    
    • 结合RXJava做线程切换
    implementation "io.reactivex.rxjava2:rxandroid:2.0.2"
    

    同上面的定义方法,先定义一个异步注解

    @Retention(RetentionPolicy.CLASS)
    public @interface Async {
    }
    

    和一个同步注解

    @Retention(RetentionPolicy.CLASS)
    @Target(ElementType.METHOD)
    public @interface Main {
    }
    

    再实现异步切面

    @Aspect
    public class AsyncAspect {
        /**
         * Around : doAync 替换,原本被 Async 声明的方法
         */
        @Around("execution(@com.goach.permissions.annotation.Async void *(..))")
        public void doAsync(final ProceedingJoinPoint joinPoint){
            //切换线程
            Completable.create(new CompletableOnSubscribe() {
                @Override
                public void subscribe(CompletableEmitter emitter) throws Exception {
                    //子线程
                    //执行原来的方法
                    try{
                        joinPoint.proceed();
                    }catch (Throwable throwable){
                        throwable.printStackTrace();
                    }
                }
            }).subscribeOn(Schedulers.io()).subscribe();
        }
    }
    

    主线程的切面

    @Aspect
    public class MainAspect {
        @Around("execution(@com.goach.permissions.annotation.Main void * (..))")
        public void doMain(final ProceedingJoinPoint joinPoint){
            //保证在主线程
            if(Looper.myLooper()==Looper.getMainLooper()){
                try {
                    joinPoint.proceed();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
                return;
            }
            //如果不在 切换到主线程
            Completable.create(new CompletableOnSubscribe() {
                @Override
                public void subscribe(CompletableEmitter emitter) throws Exception {
                    try{
                        joinPoint.proceed();
                    }catch (Throwable throwable){
                        throwable.printStackTrace();
                    }
                }
            }).subscribeOn(AndroidSchedulers.mainThread()).subscribe();
        }
    }
    

    最后测试使用

     @Async
        public void readFile(View view){
            Log.e("Main","读取文件:"+Thread.currentThread().toString());
            try {
                Thread.sleep(3000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            showRestult();
        }
        @Async
        public void writeFile(View view){
            Log.e("Main","写入文件:"+Thread.currentThread().toString());
            showRestult();
        }
        @Main
        public void showRestult(){
            Toast.makeText(this,"操作成功", Toast.LENGTH_SHORT).show();
        }
    

    这样就是一个线程切换的例子了。其实AspectJ还可以做动态权限处理,埋点统计等等之类的。

    其他资料

    AspectJ官网
    Spring AOP 实现原理与 CGLIB 应用
    Android中的AOP编程

    相关文章

      网友评论

        本文标题:Android面向AOP之AspectJ的使用篇

        本文链接:https://www.haomeiwen.com/subject/symtpftx.html