前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android AOP框架AspectJ使用详解

Android AOP框架AspectJ使用详解

作者头像
砸漏
发布2020-10-26 18:46:46
9590
发布2020-10-26 18:46:46
举报
文章被收录于专栏:恩蓝脚本恩蓝脚本恩蓝脚本

前言

之前了解过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());
}
}
  1. @Aspect:声明一个AOP容器
  2. @Pointcut:声明一个切入点
  3. @Around:将函数主体包裹起来,在函数主体前、后插入代码
  4. @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();,这个类型转化也是不会有问题。亲测有效,可以放心使用

以上就是本文的全部内容,希望对大家的学习有所帮助。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档