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());
 }

}
  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();,这个类型转化也是不会有问题。亲测有效,可以放心使用

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

您可能感兴趣的文章:

  • Android中使用AspectJ详解
(0)

相关推荐

  • Android中使用AspectJ详解

    什么是AOP AOP是Aspect Oriented Programming的缩写,即『面向切面编程』.它和我们平时接触到的OOP都是编程的不同思想,OOP,即『面向对象编程』,它提倡的是将功能模块化,对象化,而AOP的思想,则不太一样,它提倡的是针对同一类问题的统一处理,当然,我们在实际编程过程中,不可能单纯的安装AOP或者OOP的思想来编程,很多时候,可能会混合多种编程思想,大家也不必要纠结该使用哪种思想,取百家之长,才是正道. 那么AOP这种编程思想有什么用呢,一般来说,主要用于不想侵入原

  • Android AOP框架AspectJ使用详解

    前言 之前了解过android的AOP框架,用法主要用来打日志:现在有一个需求需要函数在新线程中执行,并且函数主体执行完之后,在UI线程返回结果.想到手写的话,每次都要new Thread的操作,比较麻烦:因此就尝试用注解的方法解决这个问题. AspectJ的使用核心就是它的编译器,它就做了一件事,将AspectJ的代码在编译期插入目标程序当中,运行时跟在其它地方没什么两样,因此要使用它最关键的就是使用它的编译器去编译代码ajc.ajc会构建目标程序与AspectJ代码的联系,在编译期将Aspe

  • Android Volley框架使用方法详解

    本文主要从两个方面对Android Volley框架的使用方法进行讲解,具体内容如下 一.网络请求 1.get方式请求数据 // 1 创建一个请求队列 RequestQueue requestQueue = Volley.newRequestQueue(VolleyActivity.this); // 2 创建一个请求 String url = "http://api.m.mtime.cn/PageSubArea/TrailerList.api"; StringRequest stri

  • Android路由框架Router分析详解

    什么是路由?说简单点就是映射页面跳转关系的,当然它也包含跳转相关的一切功能. 路由框架的意义 Android系统已经给我们提供了api来做页面跳转,比如startActivity,为什么还需要路由框架呢?我们来简单分析下路由框架存在的意义: 在一些复杂的业务场景下(比如电商),灵活性比较强,很多功能都是运营人员动态配置的,比如下发一个活动页面,我们事先并不知道具体的目标页面,但如果事先做了约定,提前做好页面映射,便可以自由配置. 随着业务量的增长,客户端必然随之膨胀,开发人员的工作量越来越大,比

  • Android中XUtils3框架使用方法详解(一)

    xUtils简介 xUtils 包含了很多实用的android工具. xUtils 支持大文件上传,更全面的http请求协议支持(10种谓词),拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响... xUitls 最低兼容android 2.2 (api level 8) 今天给大家带来XUtils3的基本介绍,本文章的案例都是基于XUtils3的API语法进行的演示.相信大家对这个框架也都了解过, 下面简单介绍下XUtils3的一些基本知识. XUtils3一共有4大功能:注解模块,网络

  • Android 网络请求框架Volley实例详解

    Android 网络请求框架Volley实例详解 首先上效果图 Logcat日志信息on Reponse Volley特别适合数据量不大但是通信频繁的场景,像文件上传下载不适合! 首先第一步 用到的RequetQueue RequestQueue.Java RequestQueue请求队列首先得先说一下,ReuqestQueue是如何对请求进行管理的...RequestQueue是对所有的请求进行保存...然后通过自身的start()方法开启一个CacheDispatcher线程用于缓存调度,开

  • Android 中Manifest.xml文件详解

    Android 中Manifest.xml文件详解 每一个Android项目都包含一个清单(Manifest)文件--AndroidManifest.xml,它存储在项目层次中的最底层.清单可以定义应用程序及其组件的结构和元数据. 它包含了组成应用程序的每一个组件(活动.服务.内容提供器和广播接收器)的节点,并使用Intent过滤器和权限来确定这些组件之间以及这些组件和其他应用程序是如何交互的. 它还提供了各种属性来详细地说明应用程序的元数据(如它的图标或者主题)以及额外的可用来进行安全设置和单

  • Android onLoadFinished与onLoaderReset回调详解及实例

    Android onLoadFinished与onLoaderReset回调详解及实例 onLoadFinished 这个方法是在前面已创建的加载器已经完成其加载过程后被调用,这个方法保证会在应用到加载器上的数据被释放之前被调用.在此方法中,你必须删除所有对旧数据的使用(因为它将很快会被删除),但是不要自己去释放它们,因为它们的加载器会做这些事情. 加载器一旦了解到应用不再使用数据时,将马上释放这些数据.例如,如果数据是一个从CursorLoader来的游标,你不应调用游标的close(),如果

  • Android开发之Android.mk模板的实例详解

    Android开发之Android.mk模板的实例详解 关于Android NDK开发的文章已经比较多了,我的博客中也分享了很多NDK开发相关经验和技巧,今天简单写了一个 Android.mk 的示例模板,供初学者参考. 本模板主要给大家示例 Android NDK 开发中的如下几个问题: 1. 如何自动添加需要编译的源文件列表   2. 如何添加第三方静态库.动态库的依赖   3. 如何构造一个完整的NDK工程框架 假设我们的项目依赖 libmath.a, libjson.a, libffmp

  • Android加密之全盘加密详解

    前言 Android 的安全性问题一直备受关注,Google 在 Android 系统的安全方面也是一直没有停止过更新,努力做到更加安全的手机移动操作系统. 在 Android 的安全性方面,有很多模块: 1 内核安全性 2 应用安全性 3 应用签名 4 身份验证 5 Trusty TEE 6 SELinux 7 加密 等等 其中,加密又分全盘加密(Android 4.4 引入)和文件级加密(Android 7.0 引入),本文将论述加密中的全盘加密的基本知识.全盘加密在 Android 4.4

  • Android中的binder机制详解

    前言 Binder做为Android中核心机制,对于理解Android系统是必不可少的,关于binder的文章也有很多,但是每次看总感觉看的不是很懂,到底什么才是binder机制?为什么要使用binder机制?binder机制又是怎样运行的呢?这些问题只是了解binder机制是不够的,需要从Android的整体系统出发来分析,在我找了很多资料后,真正的弄懂了binder机制,相信看完这篇文章大家也可以弄懂binder机制. 1.Binder是什么? 要理解binder,先要知道IPC,Inter

随机推荐