Hiten's Blog.

初探AOP在Android中的使用

字数统计: 1.1k阅读时长: 5 min
2016/06/27 Share

前些时间在学习Spring时看到AOP,即面向切面编程,这个概念很新鲜,感觉用起来特别牛逼,无奈我是相见恨晚啊,在网上搜一下关于Android AOP的可行方案,大约有两种,一个是阿里的开源项目Dexposed,另一个是AspectJ,在我拜读了看见了邓凡平老师的深入理解Android之AOP后,觉得用AspectJ
比较符合Java的本质吧,这个纯属自己的观点,哈哈哈!

如何集成

AspectJ的使用核心就是它的编译器,它就做了一件事,将AspectJ的代码在编译期插入目标程序当中,运行时跟在其它地方没什么两样,因此要使用它最关键的就是使用它的编译器去编译代码ajc。ajc会构建目标程序与AspectJ代码的联系,在编译期将AspectJ代码插入被切出的PointCut中,已达到AOP的目的。因此,无论在什么IDE上(如果使用命令行就可以直接使用ajc编译了),问题就是让IDE使用ajc作为编译器编译代码。

编译方法参考大吊fernandocejas的博客,具体可以参考它的博客,我直接写出集成过程(基于AS IDE)
1、在目标module的build.gradle下增加依赖

classpath依赖

1
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
       
    }
}

jar包依赖

1
compile 'org.aspectj:aspectjrt:1.8.9'

2、增加gradle执行方法

1
final def variants = project.android.applicationVariants
variants.all { variant ->
//    LibraryPlugin plugin = project.plugins.getPlugin(LibraryPlugin)
    JavaCompile javaCompile = variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.5",
                         "-inpath", javaCompile.destinationDir.toString(),
                         "-aspectpath", javaCompile.classpath.asPath,
                         "-d", javaCompile.destinationDir.toString(),
                         "-classpath", javaCompile.classpath.asPath,
                         "-bootclasspath", project.android.bootClasspath.join(
                File.pathSeparator)]

        MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler)

        def log = project.logger
        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:
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break;
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break;
            }
        }
    }
}

写代码

现在gradle配置完成,开始写代码,比如我现在要给一个网络请求的方法增加一些公共参数或者对参数进行加密,而这些参数可配置,加密方法设置有开关,但是这些方法又不想写在网络层,用AOP或许可以解决,看代码:

1
2
3
//一个用于网络请求加密的Aspect
@Aspect public class HttpRequestAspect {
//定义切点 @Pointcut("execution(* a.b.c.http.HttpRequest.*(..))") public void httpRequest() { } @Around("httpRequest()") public Object create(ProceedingJoinPoint jp) throws Throwable { Object[] args = jp.getArgs(); if (args != null && args.length >= 3) { //获取参数 String url = (String) args[0]; RequestParams requestParams = (RequestParams) args[1]; //添加必选参数 long timeToken = TimeToken.getTimeToken(); requestParams.put("base_info","xxxx"); requestParams.put("app_key", "zzzzzzzzzzzzzzzzz"); requestParams.put("interface_version", "3.0.2"); requestParams.put("app_version","xxx"); requestParams.put("time_token", timeToken); requestParams.put("user_latlon","xxx.xxx,xxx.xxxxxx"); //加密判断 Object arg = args[2]; boolean dontEncrypt = arg instanceof HashMap; if (!dontEncrypt) { RequestParams encryptParams = SignUtil.clientEncrypt(url, requestParams); args[1] = encryptParams; } else { args[1] = requestParams; } } return jp.proceed(args); } }

其中HttpRequest是执行网络请求的类,封装了同步异步的get,post等方法

1
2
3
4
5
public class HttpRequest {

    /**
     * Http get请求
     *
     * @param url    请求地址
     * @param params 请求参数
     * @param tag    tag
     * @return
     * @throws IOException
     */
    public Response get(String url, RequestParams params, Object tag) throws IOException {
        Request.Builder builder = new Request.Builder();
        builder.url(UrlUtil.formatGetUrl(url, params));
        builder.tag(tag);
        builder.get();
        Request request = builder.build();
        return HttpManager.getInstance().execute(request);
    }

    /**
     * Http get请求
     *
     * @param url    请求地址
     * @param params 请求参数
     * @return
     * @throws IOException
     */
    public void get(String url, RequestParams params, Callback callback, Object tag) throws IOException {
        Request.Builder builder = new Request.Builder();
        builder.url(UrlUtil.formatGetUrl(url, params));
        builder.tag(tag);
        builder.get();
        Request request = builder.build();
        HttpManager.getInstance().enqueue(request, callback);
    }

    /**
     * Http Post提交表单
     *
     * @param url    请求地址
     * @param params 请求参数
     * @param tag    tag
     * @return 返回HttpResponse对象
     * @throws IOException
     */
    public Response post(String url, RequestParams params, Object tag) throws IOException {
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        builder.tag(tag);
        if (params != null) {
            FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder();
            final Iterator<Map.Entry<String, String>> entryIterator = params.entrySet().iterator();
            while (entryIterator.hasNext()) {
                final Map.Entry<String, String> entry = entryIterator.next();
                formEncodingBuilder.add(entry.getKey(), entry.getValue());
            }
            builder.post(formEncodingBuilder.build());
        }
        Request request = builder.build();
        return HttpManager.getInstance().execute(request);
    }

......

}

流程分析,在请求HttpRequest对象的里边的方法时,Aspectj通过@Pointcut的execution配置,匹配到目标方法,在 @Around(“httpRequest()”)方法里能获取到目标方法的每个参数,改变参数后通过执行jp.proceed(args)继续走目标方法
由于上述代码是从项目中摘取出来的,没办法全部粘贴,等有时间再单独写一个demo,深入解析Aspectj。

CATALOG
  1. 1. 如何集成
  2. 2. 写代码