博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android开发之AOP编程
阅读量:6881 次
发布时间:2019-06-27

本文共 5722 字,大约阅读时间需要 19 分钟。

一、简介:

AOP(Aspect-Oriented Programming,面向切面编程),可谓是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。AOP技术利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低 模块间的耦合度,并有利于未来的可操作性和可维护性。Android相关AOP常用的方法有 JNI HOOK 和 静态织入,本文以静态织入的方式之一AspectJ来进行讲解使用。(在编译期织入,切面直接以字节码的形式编译到目标字节码文件中,这要求使用特殊的 Java 编译器。)

二、使用场景

对于Java后端来说最常见的场景应该就是日志记录、检查用户权限、参数校验、事务处理、缓存等等了,而对于Android来说也是比较广泛的(适用与Java的同样都适用于Android),例如常见的有记录日志、检查权限申请、判断用户登录状态、检查网络状态、过滤重复点击等等,可以根据项目实际需要的一些业务场景来自定义你所需要的。

三、实践

1、添加依赖
apply plugin: 'android-aspectjx'dependencies {    ...    compile 'org.aspectj:aspectjrt:1.8.9'}复制代码

项目跟目录的gradle脚本中加入

buildscript {    repositories {        mavenCentral()    }    dependencies {        //此处推荐该库,由沪江出品,可免去配置各种复杂任务        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'    }}复制代码
2、AOP注解与使用
@Aspect:声明切面,标记类@Pointcut(切点表达式):定义切点,标记方法@Before(切点表达式):前置通知,切点之前执行@Around(切点表达式):环绕通知,切点前后执行@After(切点表达式):后置通知,切点之后执行@AfterReturning(切点表达式):返回通知,切点方法返回结果之后执行@AfterThrowing(切点表达式):异常通知,切点抛出异常时执行复制代码

切点表达式的示例:

如:execution(@com.xxx.aop.TimeLog *.(..))切点表达式的组成:execution(@注解 访问权限 返回值的类型 包名.函数名(参数))复制代码

下面简单列举两个简单例子:

@Before("execution(@com.xxx.aop.activity * *(..))")public void before(JoinPoint point) {    Log.i(TAG, "method excute before...");}匹配activity包下的所有方法,在方法执行前输出日志复制代码
@Around("execution(@cn.com.xxx.Async * *(..))")public void doAsyncMethod(ProceedingJoinPoint joinPoint) {    Log.i(TAG, "method excute before...");    joinPoint.proceed();    Log.i(TAG, "method excute after...");}匹配带有Async注解的方法,在方法执行前后分别输出日志复制代码
@Around("execution(@cn.com.xxx.Async * *(..)) && @annotation(async)")public void doAsyncMethod(ProceedingJoinPoint joinPoint, Async async) {    Log.i(TAG, "value>>>>>"+async.value());    
joinPoint.proceed();
}//注意:@annotation(xxx)必须与下面的的参数值对应匹配带有Async注解的方法,并获取注解中的值,去做相应处理。复制代码
3、实际应用举例
场景一、利用注解实现缓存处理

1、定义注解Cache如下:

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface Cache {    String key(); //缓存的key    int expiry() default -1; // 过期时间,单位是秒}复制代码

2、编写Aspect实现

@Aspectpublic class CacheAspect {    private static final String POINTCUT_METHOD = "execution(@cn.com.xxx.annotation.Cache * *(..))";    @Pointcut(POINTCUT_METHOD)    public void onCacheMethod() {    }    @Around("onCacheMethod() && @annotation(cache)")    public Object doCacheMethod(ProceedingJoinPoint joinPoint, Cache cache) throws Throwable {        //获取注解中的key        String key = cache.key();        //获取注解中的过期时间        int expiry = cache.expiry();        //执行当前注解的方法(放行)        Object result = joinPoint.proceed();        //方法执行后进行缓存(缓存对象必须是方法返回值)        ACache aCache = ACache.get(AopArms.getContext());        if (expiry>0) {            aCache.put(key,(Serializable)result,expiry);        } else {            aCache.put(key,(Serializable)result);        }        return result;    }}复制代码

此处引用该项目中的缓存实现,仅一个Acache文件,个人觉得还是比较好用的,当然你也可以自己去实现。 3、测试

public static void main(String[] args) {        initData();        getUser();    }        //缓存数据    @Cache(key = "userList")    private ArrayList
initData() { ArrayList
list = new ArrayList<>(); for (int i=0; i<5; i++){ User user = new User(); user.setName("艾神一不小心:"+i); user.setPassword("密码:"+i); list.add(user); } return list; } //获取缓存 private void getUser() { ArrayList
users = ACache.get(this).getAsList("userList", User.class); Log.e(TAG, "getUser: "+users); } 复制代码
场景二、利用注解实现缓存移除

1、定义注解CacheEvict如下:

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface CacheEvict {    //需要移除的key    String key();    // 缓存的清除是否在方法之前执行, 默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除    boolean beforeInvocation() default false;        //是否清空所有缓存    boolean allEntries() default false;}复制代码

2、编写Aspect实现

@Aspectpublic class CacheEvictAspect {    private static final String POINTCUT_METHOD = "execution(@cn.com.xxx.annotation.CacheEvict * *(..))";    //切点位置,所有的CacheEvict处    @Pointcut(POINTCUT_METHOD)    public void onCacheEvictMethod() {    }        //环绕处理,并拿到CacheEvict注解值    @Around("onCacheEvictMethod() && @annotation(cacheEvict)")    public Object doCacheEvictMethod(ProceedingJoinPoint joinPoint, CacheEvict cacheEvict) throws Throwable {        String key = cacheEvict.key();        boolean beforeInvocation = cacheEvict.beforeInvocation();        boolean allEntries = cacheEvict.allEntries();        ACache aCache = ACache.get(AopArms.getContext());        Object result = null;        if (allEntries){            //如果是全部清空,则key不需要有值            if (!TextUtils.isEmpty(key))                throw new IllegalArgumentException("Key cannot have value when cleaning all caches");            aCache.clear();        }        if (beforeInvocation){            //方法执行前,移除缓存            aCache.remove(key);            result = joinPoint.proceed();        }else {            //方法执行后,移除缓存,如果出现异常缓存就不会清除(推荐)            result = joinPoint.proceed();            aCache.remove(key);        }        return result;    }}复制代码

3、测试

public static void main(String[] args) {        removeUser();        getUser();    }        //移除缓存数据    @CacheEvict(key = "userList")    private void removeUser() {        Log.e(TAG, "removeUser: >>>>");    }        //获取缓存    private void getUser() {        ArrayList
users = ACache.get(this).getAsList("userList", User.class); Log.e(TAG, "getUser: "+users); }复制代码

可以看到执行结果:

最后推荐本人写的一个基于AOP的注解框架,用法及其简单,参考,里面编写了Android开发中常用的一套注解,如日志、异步处理、缓存、SP、延迟操作、定时任务、重试机制、try-catch安全机制、过滤频繁点击等,后续还会有更多更强大的注解功能加入,欢迎star。

转载于:https://juejin.im/post/5ce749b6f265da1bba58de15

你可能感兴趣的文章
前后台交互
查看>>
LINQ&EF任我行(二)--LinQ to Object (转)
查看>>
Python之旅.第五章.面向对象
查看>>
Unity坐标系 左手坐标系 图
查看>>
python获取昨日日期
查看>>
13.1.2 拷贝赋值运算符、析构函数、三/五法则、阻止拷贝
查看>>
2013年蓝桥杯题目与解答
查看>>
HTML5仿微信公众号界面
查看>>
海康威视 - 萤石云开放平台 js 版
查看>>
关于分销平台
查看>>
剑指offer---12-**--数值的整数次方
查看>>
PAT - L2-010. 排座位(并查集)
查看>>
HDU - 5269【SBBBBBB Trie】
查看>>
sql server 日志文件结构及误操作数据找回
查看>>
JUnit 3一个例子就懂
查看>>
Mongodb相关 (Shell命令 / mongoose)
查看>>
Web API的Log问题
查看>>
leetcode Second Highest Salary
查看>>
【LeetCode每天一题】Word Break()
查看>>
关于centerOS下修改网络连接
查看>>