AOP development of programming Android

I. Introduction:

AOP (Aspect-Oriented Programming, Aspect Oriented Programming), can be described as OOP (Object-Oriented Programing, object-oriented programming) supplement and perfect. AOP Using a technique known as "cross" technique, the internal cross-sectional decapsulates the object, and those that affect the behavior of the public classes encapsulated into a plurality of reusable modules, and entitled "Aspect", i.e., aspect. The so-called "respect", simply put, is unrelated to those with business, but as a logical or liability business modules together called encapsulated, easy to duplicate code to reduce system and reduce the degree of coupling between modules, and facilitate future may operability and maintainability. Android related AOP Commonly used methods JNI HOOK and static weaving, paper, woven into a static one way to explain the use of AspectJ. (Weaving at compile time, the compiler section directly to the target byte code files in the form of bytecode, which requires special Java compiler.)

Second, the use of scenarios

For the back-end Java is the most common scenario is logging should check the user permissions, parameter validation, transaction processing, cache, etc., while for Android is also more extensive (both equally applicable to Java for Android ), for example, are common log, check the permissions apply, to determine the user logged in, check the network status, and so on filtering duplicate clicks, you can customize what you need based on the actual needs of the project some business scenarios.

Third, practice

1, adding a dependency
apply plugin: 'android-aspectjx'

dependencies {
    ...
    compile 'org.aspectj:aspectjrt:1.8.9'
}
复制代码

Project joined with gradle script directory

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

Examples of the tangent point of the expression:

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

Simple Simple examples include the following two:

@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());
    <!--to do somethings-->
    joinPoint.proceed();
    <!--to do somethings-->
}
//注意:@annotation(xxx)必须与下面的的参数值对应
匹配带有Async注解的方法,并获取注解中的值,去做相应处理。
复制代码
3, Practical Applications
A scene, use caching to achieve comment

1. Define Cache notes as follows:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Cache {

    String key(); //缓存的key

    int expiry() default -1; // 过期时间,单位是秒
}
复制代码

2, write achieve Aspect

@Aspect
public 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;
    }
}
复制代码

Here reference ACache the project cache implementation, only a Acache file, personally I think it is quite easy to use, of course, you can go to achieve. 3, the test

    public static void main(String[] args) {
        initData();
        getUser();
    }
    
    //缓存数据
    @Cache(key = "userList")
    private ArrayList<User> initData() {
        ArrayList<User> 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<User> users = ACache.get(this).getAsList("userList", User.class);
        Log.e(TAG, "getUser: "+users);
    }
    
复制代码
Scene Two, the use of annotations implement caching removed

1, annotation CacheEvict defined as follows:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CacheEvict {

    //需要移除的key
    String key();

    // 缓存的清除是否在方法之前执行, 默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
    boolean beforeInvocation() default false;
    
    //是否清空所有缓存
    boolean allEntries() default false;
}
复制代码

2, write achieve Aspect

@Aspect
public 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, the test

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

We can see the results:

Finally I wrote a recommendation based on the comments AOP framework AopArms , usage and simple, reference Android-based annotation AOP framework , which is commonly used in the preparation of the Android developers a set of annotations, such as logging, asynchronous processing, caching, SP, delayed operation, regular tasks, retry mechanism, try-catch safety mechanism, filtration frequent clicks, etc., there will be more follow-up more powerful annotation features to join, welcomed the star.

Reproduced in: https: //juejin.im/post/5ce749b6f265da1bba58de15

Guess you like

Origin blog.csdn.net/weixin_34015566/article/details/91460165