Android AOP框架AspectJ使用详解
前言
之前了解过android的AOP框架,用法主要用来打日志;现在有一个需求需要函数在新线程中执行,并且函数主体执行完之后,在UI线程返回结果。想到手写的话,每次都要newThread的操作,比较麻烦;因此就尝试用注解的方法解决这个问题。
AspectJ的使用核心就是它的编译器,它就做了一件事,将AspectJ的代码在编译期插入目标程序当中,运行时跟在其它地方没什么两样,因此要使用它最关键的就是使用它的编译器去编译代码ajc。ajc会构建目标程序与AspectJ代码的联系,在编译期将AspectJ代码插入被切出的PointCut中,已达到AOP的目的。
因此,无论在什么IDE上(如果使用命令行就可以直接使用ajc编译了),问题就是让IDE使用ajc作为编译器编译代码。
代码实现
注解使用
代码主要通过TraceLog、RunOnNewThread、RunOnNewThreadWithUICallback这三个注解与AOP容器关联。使用方法如下:
@TraceLog
@RunOnNewThread
publicvoidcheckAndRestartDownloadTask(finalbooleanisAutoCache){
DownloadManager.getInstance().startService(isAutoCache);
}
@TraceLog
@RunOnNewThreadWithUICallback
publicBooleanisShowTipsForFirstVideoCache(DBQueryCallbackcallback){
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);
returntrue;
}
returnfalse;
}
checkAndRestartDownloadTask方法,希望方法体在一个新的线程执行并打印方法执行的Log;isShowTipsForFirstVideoCache方法,希望方法体在一个新的线程执行,并将函数的结果通过DBQueryCallback这个回调回传给UI线程,同时打印方法执行的Log。
AOP容器识别这三个注解,并实现注解解释器。
@Aspect
publicclassTudouDownloadAspect{
publicstaticfinalStringTAG=TudouDownloadAspect.class.getSimpleName();
privatestaticfinalStringTHREAD_CALLBACK_POINT_METHOD=
"execution(@com.download.common.aspect.RunOnNewThreadWithUICallback**(..,com.download.common.callback.DBQueryCallback))";
privatestaticfinalStringTHREAD_CALLBACK_POINT_CONSTRUCTOR=
"execution(@com.download.common.aspect.RunOnNewThreadWithUICallback*.new(..,com.download.common.callback.DBQueryCallback))";
privatestaticfinalStringTHREAD_POINT_METHOD=
"execution(@com.download.common.aspect.RunOnNewThread**(..))";
privatestaticfinalStringTHREAD_POINT_CONSTRUCTOR=
"execution(@com.download.common.aspect.RunOnNewThread*.new(..))";
privatestaticfinalStringLOG_POINT_METHOD=
"execution(@com.download.common.aspect.TraceLog**(..))";
privatestaticfinalStringLOG_POINT_CONSTRUCTOR=
"execution(@com.download.common.aspect.TraceLog*.new(..))";
@Pointcut(THREAD_CALLBACK_POINT_METHOD)
publicvoidmethodAnnotatedWithThread(){}
@Pointcut(THREAD_CALLBACK_POINT_CONSTRUCTOR)
publicvoidconstructorAnnotatedWithThread(){}
@Pointcut(THREAD_POINT_METHOD)
publicvoidmethodAnnotatedWithNewThread(){}
@Pointcut(THREAD_POINT_CONSTRUCTOR)
publicvoidconstructorAnnotatedWithNewThread(){}
@Pointcut(LOG_POINT_METHOD)
publicvoidmethodAnnotatedWithLog(){}
@Pointcut(LOG_POINT_CONSTRUCTOR)
publicvoidconstructorAnnotatedWithLog(){}
/**
*@RunOnNewThreadWithUICallback的注解解释器
**/
@Around("methodAnnotatedWithThread()||constructorAnnotatedWithThread()")
publicObjectwrapNewThreadWithCallback(finalProceedingJoinPointjoinPoint)throwsThrowable{
Log.v(TAG,"inwrapNewThreadWithCallback");
Object[]objs=joinPoint.getArgs();
finalDBQueryCallbackcallback=(DBQueryCallback)objs[objs.length-1];
newThread(newRunnable(){
@Override
publicvoidrun(){
try{
finalObjectobj=joinPoint.proceed();
DownloadClient.getInstance().mainHandler.post(newRunnable(){
@Override
publicvoidrun(){
if(obj!=null)
callback.querySuccess(obj);
else
callback.queryFail();
}
});
}catch(Throwablethrowable){
throwable.printStackTrace();
}
}
}).start();
returnnull;
}
/**
*@RunOnNewThread的注解解释器
**/
@Around("methodAnnotatedWithNewThread()||constructorAnnotatedWithNewThread()")
publicvoidwrapNewThread(finalProceedingJoinPointjoinPoint)throwsThrowable{
Log.v(TAG,"inwrapNewThread");
newThread(newRunnable(){
@Override
publicvoidrun(){
try{
joinPoint.proceed();
}catch(Throwablethrowable){
throwable.printStackTrace();
}
}
}).start();
}
/**
*@TraceLog的注解解释器
**/
@Before("methodAnnotatedWithLog()||constructorAnnotatedWithLog()")
publicvoidwrapWithLog(JoinPointjoinPoint)throwsThrowable{
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代码依赖
}
//AspectJAOP容器加载脚本
finaldeflog=project.logger
finaldefvariants=project.android.libraryVariants
variants.all{variant->
JavaCompilejavaCompile=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"ajcargs:"+Arrays.toString(args)
MessageHandlerhandler=newMessageHandler(true);
newMain().run(args,handler);
for(IMessagemessage:handler.getMessages(null,true)){
switch(message.getKind()){
caseIMessage.ABORT:
caseIMessage.ERROR:
caseIMessage.FAIL:
log.errormessage.message,message.thrown
break;
caseIMessage.WARNING:
log.warnmessage.message,message.thrown
break;
caseIMessage.INFO:
log.infomessage.message,message.thrown
break;
caseIMessage.DEBUG:
log.debugmessage.message,message.thrown
break;
}
}
}
}
备注
@RunOnNewThreadWithUICallback这个注解的匹配规则需要函数的最后一个参数为DBQueryCallback(必须要有一个回调参数,不然怎么回传给UI线程~)。函数的返回值必须和DBQueryCallback的泛型类型一致,因为需要将返回值传入回调当中;
newThread(newRunnable(){
@Override
publicvoidrun(){
try{
finalObjectobj=joinPoint.proceed();
DownloadClient.getInstance().mainHandler.post(newRunnable(){
@Override
publicvoidrun(){
if(obj!=null)
callback.querySuccess(obj);
else
callback.queryFail();
}
});
}catch(Throwablethrowable){
throwable.printStackTrace();
}
}
}).start();
注意finalObjectobj=joinPoint.proceed();,执行了函数体以后,我们默认取到的是一个Object类型的返回值,所以不能用基本数据类型(bool用Boolean,int用Interger)。还有一点,Java中的null是可以转化为任意类型的,所以就算在函数体直接返回null,执行finalObjectobj=joinPoint.proceed();,这个类型转化也是不会有问题。亲测有效,可以放心使用
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。