Android面向AOP之AspectJ的使用篇

前言

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编程

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,961评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,444评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,009评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,082评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,101评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,271评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,738评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,395评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,539评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,434评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,481评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,160评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,749评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,816评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,038评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,548评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,140评论 2 341

推荐阅读更多精彩内容

  • 基本知识 其实, 接触了这么久的 AOP, 我感觉, AOP 给人难以理解的一个关键点是它的概念比较多, 而且坑爹...
    永顺阅读 8,066评论 5 114
  • 因为工作需求,自己去了解一下aop并做下的记录,当然大部分都是参考他人博客以及官方文档。 目录 [关于 AOP](...
    forip阅读 2,267评论 1 20
  • 文章对应的项目地址aop-tech,运行一下sample,结合代码和文章,你会收获更多。 熟悉程序开发的都知道OO...
    sososeen09阅读 36,372评论 2 36
  • 这两天,一长相帅气的朋友跟娇美动人的老婆吵了一架,然后,又千方百计哄老婆高兴,并且赔礼道歉,还给老婆去商场买...
    a23c1c121f1a阅读 313评论 2 2
  • 清晨,阳光照得我无法睁眼。 波光粼粼的湖水,铺满毛茸茸的阳光,温暖。 而在夏日的公交车上,我忽然想睁眼看看,窗外那...
    Sunflower的夏天阅读 341评论 0 0