欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android AOP框架AspectJ使用详解

程序员文章站 2023-11-17 19:36:28
前言 之前了解过android的aop框架,用法主要用来打日志;现在有一个需求需要函数在新线程中执行,并且函数主体执行完之后,在ui线程返回结果。想到手写的话,每次都要n...

前言

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

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