Android AOP框架AspectJ使用详解
前言
之前了解过android的aop框架,用法主要用来打日志;现在有一个需求需要函数在新线程中执行,并且函数主体执行完之后,在ui线程返回结果。想到手写的话,每次都要new thread的操作,比较麻烦;因此就尝试用注解的方法解决这个问题。
aspectj的使用核心就是它的编译器,它就做了一件事,将aspectj的代码在编译期插入目标程序当中,运行时跟在其它地方没什么两样,因此要使用它最关键的就是使用它的编译器去编译代码ajc。ajc会构建目标程序与aspectj代码的联系,在编译期将aspectj代码插入被切出的pointcut中,已达到aop的目的。
因此,无论在什么ide上(如果使用命令行就可以直接使用ajc编译了),问题就是让ide使用ajc作为编译器编译代码。
代码实现
注解使用
代码主要通过tracelog、runonnewthread、runonnewthreadwithuicallback这三个注解与aop容器关联。使用方法如下:
@tracelog @runonnewthread public void checkandrestartdownloadtask(final boolean isautocache) { downloadmanager.getinstance().startservice(isautocache); } @tracelog @runonnewthreadwithuicallback public boolean isshowtipsforfirstvideocache(dbquerycallback<boolean> callback) { if (!preferenceclient.is_first_video_cache_done.getboolean() && (downloadmanager.getinstance().getfinishedtasksize(true, false) > 0 || downloadmanager.getinstance().getfinishedtasksize(true, true) > 0)) { preferenceclient.is_first_video_cache_done.setboolean(true); return true; } return false; }
checkandrestartdownloadtask方法,希望方法体在一个新的线程执行并打印方法执行的log;isshowtipsforfirstvideocache方法,希望方法体在一个新的线程执行,并将函数的结果通过dbquerycallback这个回调回传给ui线程,同时打印方法执行的log。
aop容器识别这三个注解,并实现注解解释器。
@aspect public class tudoudownloadaspect { public static final string tag = tudoudownloadaspect.class.getsimplename(); private static final string thread_callback_point_method = "execution(@com.download.common.aspect.runonnewthreadwithuicallback * *(.., com.download.common.callback.dbquerycallback))"; private static final string thread_callback_point_constructor = "execution(@com.download.common.aspect.runonnewthreadwithuicallback *.new(.., com.download.common.callback.dbquerycallback))"; private static final string thread_point_method = "execution(@com.download.common.aspect.runonnewthread * *(..))"; private static final string thread_point_constructor = "execution(@com.download.common.aspect.runonnewthread *.new(..))"; private static final string log_point_method = "execution(@com.download.common.aspect.tracelog * *(..))"; private static final string log_point_constructor = "execution(@com.download.common.aspect.tracelog *.new(..))"; @pointcut(thread_callback_point_method) public void methodannotatedwiththread(){} @pointcut(thread_callback_point_constructor) public void constructorannotatedwiththread(){} @pointcut(thread_point_method) public void methodannotatedwithnewthread(){} @pointcut(thread_point_constructor) public void constructorannotatedwithnewthread(){} @pointcut(log_point_method) public void methodannotatedwithlog(){} @pointcut(log_point_constructor) public void constructorannotatedwithlog(){} /** * @runonnewthreadwithuicallback 的注解解释器 * */ @around("methodannotatedwiththread() || constructorannotatedwiththread()") public object wrapnewthreadwithcallback(final proceedingjoinpoint joinpoint) throws throwable { log.v(tag, "in wrapnewthreadwithcallback"); object[] objs = joinpoint.getargs(); final dbquerycallback callback = (dbquerycallback) objs[objs.length-1]; new thread(new runnable() { @override public void run() { try { final object obj = joinpoint.proceed(); downloadclient.getinstance().mainhandler.post(new runnable() { @override public void run() { if (obj != null) callback.querysuccess(obj); else callback.queryfail(); } }); } catch (throwable throwable) { throwable.printstacktrace(); } } }).start(); return null; } /** * @runonnewthread 的注解解释器 * */ @around("methodannotatedwithnewthread() || constructorannotatedwithnewthread()") public void wrapnewthread(final proceedingjoinpoint joinpoint) throws throwable { log.v(tag, "in wrapnewthread"); new thread(new runnable() { @override public void run() { try { joinpoint.proceed(); } catch (throwable throwable) { throwable.printstacktrace(); } } }).start(); } /** * @tracelog 的注解解释器 * */ @before("methodannotatedwithlog() || constructorannotatedwithlog()") public void wrapwithlog(joinpoint joinpoint) throws throwable { log.v(tag, "before->" + joinpoint.gettarget().tostring() + "---" + joinpoint.getsignature().getname()); } }
- @aspect:声明一个aop容器
- @pointcut:声明一个切入点
- @around:将函数主体包裹起来,在函数主体前、后插入代码
- @before:在函数主体执行之前插入代码
使用gradle脚本加载aop容器
buildscript { repositories { mavenlocal() maven { url "https://jitpack.io" } } dependencies { classpath 'org.aspectj:aspectjtools:1.8.+' //aspectj脚本依赖 } } dependencies { compile 'org.aspectj:aspectjrt:1.8.+' //aspectj 代码依赖 } //aspectj aop容器加载脚本 final def log = project.logger final def variants = project.android.libraryvariants variants.all { variant -> 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)] log.debug "ajc args: " + arrays.tostring(args) messagehandler handler = new messagehandler(true); new main().run(args, handler); 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: log.warn message.message, message.thrown break; case imessage.info: log.info message.message, message.thrown break; case imessage.debug: log.debug message.message, message.thrown break; } } } }
备注
@runonnewthreadwithuicallback这个注解的匹配规则需要函数的最后一个参数为dbquerycallback(必须要有一个回调参数,不然怎么回传给ui线程~)。函数的返回值必须和dbquerycallback的泛型类型一致,因为需要将返回值传入回调当中;
new thread(new runnable() { @override public void run() { try { final object obj = joinpoint.proceed(); downloadclient.getinstance().mainhandler.post(new runnable() { @override public void run() { if (obj != null) callback.querysuccess(obj); else callback.queryfail(); } }); } catch (throwable throwable) { throwable.printstacktrace(); } } }).start();
注意final object obj = joinpoint.proceed();,执行了函数体以后,我们默认取到的是一个object类型的返回值,所以不能用基本数据类型(bool用boolean,int用interger)。还有一点,java中的null是可以转化为任意类型的,所以就算在函数体直接返回null,执行final object obj = joinpoint.proceed();,这个类型转化也是不会有问题。亲测有效,可以放心使用
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。