(4.6.23.2)Android之面向切面编程:Aspect集成示例之DebugTrace 与 SecurityCheckAnnotation

来源:互联网 发布:淘宝开店培训班 编辑:程序博客网 时间:2024/05/21 17:51

  • 一集成方法一
  • 二集成方法二
  • 三DebugTrace
    • 1 定义注解DebugTrace
    • 1 计时工具类
    • 3 切面编程
  • 四SecurityCheckAnnotation
    • 1 定义注解
    • 2 定义切面
  • 参考文献

一、集成方法一

  • 需要在项目根目录的build.gradle中增加依赖:
buildscript {    repositories {        jcenter()    }    dependencies {        classpath 'com.android.tools.build:gradle:2.3.0-beta2'        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:1.0.10'        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }}
  • 再主项目或者库的build.gradle中增加AspectJ的依赖
compile 'org.aspectj:aspectjrt:1.8.9'同时在build.gradle中加入AspectJX模块apply plugin: 'android-aspectjx'

这样就把整个Android Studio中的AspectJ的环境配置完毕了,如果在编译的时候,遇到一些『can’t determine superclass of missing type xxxxx』这样的错误,请参考项目README中关于excludeJarFilter的使用。

aspectjx {    //includes the libs that you want to weave    includeJarFilter 'universal-image-loader', 'AspectJX-Demo/library'    //excludes the libs that you don't want to weave    excludeJarFilter 'universal-image-loader'}

二、集成方法二

  • AspectJ比较强大,除了支持对source文件(即aj文件、或@AspectJ注解的Java文件,或普通java文件)直接进行编译外,
  • 还能对Java字节码(即对class文件)进行处理。有感兴趣的同学可以对aspectj-test小例子的class文件进行反编译,你会发现AspectJ无非是在被选中的JPoint的地方加一些hook函数。当然Before就是在调用JPoint之前加,After就是在JPoint返回之前加。
  • 更高级的做法是当class文件被加载到虚拟机后,由虚拟机根据AOP的规则进行hook。

在Android里边,我们用得是第二种方法,即对class文件进行处理:

//AndroidAopDemo.build.gradle  //此处是编译一个App,所以使用的applicationVariants变量,否则使用libraryVariants变量  //这是由Android插件引入的。所以,需要import com.android.build.gradle.AppPlugin;  android.applicationVariants.all { variant ->     /*      这段代码之意是:      当app编译个每个variant之后,在javaCompile任务的最后添加一个action。此action      调用ajc函数,对上一步生成的class文件进行aspectj处理。    */      AppPluginplugin = project.plugins.getPlugin(AppPlugin)     JavaCompile javaCompile = variant.javaCompile     javaCompile.doLast{         String bootclasspath =plugin.project.android.bootClasspath.join(File.pathSeparator)         //ajc是一个函数,位于utils.gradle中          ajc(bootclasspath,javaCompile)      }  }  

ajc函数其实和我们手动试玩aspectj-test目标一样,只是我们没有直接调用ajc命令,而是利用AspectJ提供的API做了和ajc命令一样的事情。

import org.aspectj.bridge.IMessage  import org.aspectj.bridge.MessageHandler  import org.aspectj.tools.ajc.Main  def ajc(String androidbootClassFiles,JavaCompile javaCompile){     String[] args = ["-showWeaveInfo",                       "-1.8", //1.8是为了兼容java 8。请根据自己java的版本合理设置它                       "-inpath",javaCompile.destinationDir.toString(),                       "-aspectpath",javaCompile.classpath.asPath,                       "-d",javaCompile.destinationDir.toString(),                       "-classpath",javaCompile.classpath.asPath,                      "-bootclasspath", androidbootClassFiles]      MessageHandlerhandler = new MessageHandler(true);      new Main().run(args,handler)      deflog = project.logger      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           throw message.thrown           break;         case IMessage.WARNING:         case IMessage.INFO:           log.info message.message, message.thrown           break;         case IMessage.DEBUG:           log.debug message.message, message.thrown           break;        }      }    }  

主要利用了https://eclipse.org/aspectj/doc/released/devguide/ajc-ref.html中TheAspectJ compiler API一节的内容。由于代码已经在csdn git上,大家下载过来直接用即可。

三、DebugTrace

有该注解的函数,打印执行时间

3.1 定义注解DebugTrace

/** * Indicates that the annotated method is being traced (debug mode only) and * will use {@link android.util.Log} to print debug data: * - Method name * - Total execution time * - Value (optional string parameter) */@Retention(RetentionPolicy.CLASS)@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD })public @interface DebugTrace {}

4.1 计时工具类

public class TimeWatcher {    private long startTime;    private long endTime;    private long elapsedTime;    public TimeWatcher() {        //empty    }    private void reset() {        startTime = 0;        endTime = 0;        elapsedTime = 0;    }    public void start() {        reset();        startTime = System.nanoTime();    }    public void stop() {        if (startTime != 0) {            endTime = System.nanoTime();            elapsedTime = endTime - startTime;        } else {            reset();        }    }    public long getTotalTimeMillis() {        return (elapsedTime != 0) ? TimeUnit.NANOSECONDS.toMillis(endTime - startTime) : 0;    }    public long getStartTime() {        return startTime;    }    public long getEndTime() {        return endTime;    }}

3.3 切面编程

/** * 类描述: * Aspect representing the cross cutting-concern: Method and Constructor Tracing. * Created by yhf on 2017/3/28. */@Aspectpublic class DebugTraceAspect {    public static final String TAG = "MainActivity-DTAspect";    //有这个注解的 返回值任意的  所有类的所有函数且参数不限    private static final String POINTCUT_METHOD =            "execution(@com.sangfor.aop.annotation.DebugTrace * *(..))";    //有这个注解的 返回值任意的  所有类的构造函数且参数不限    private static final String POINTCUT_CONSTRUCTOR =            "execution(@com.sangfor.aop.annotation.DebugTrace *.new(..))";    @Pointcut(POINTCUT_METHOD)    public void methodAnnotatedWithDebugTrace() {}    @Pointcut(POINTCUT_CONSTRUCTOR)    public void constructorAnnotatedDebugTrace() {}    @Around("methodAnnotatedWithDebugTrace() || constructorAnnotatedDebugTrace()")    public Object debugTraceJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();        String className = methodSignature.getDeclaringType().getSimpleName();        String methodName = methodSignature.getName();        final TimeWatcher stopWatch = new TimeWatcher();        stopWatch.start();        Object result = joinPoint.proceed();        stopWatch.stop();        LogCore.aspectI(TAG,                new TimeAspectData("DebugTraceAspect","debugTraceJoinPoint",joinPoint,stopWatch).toString());        return result; //修改这里,相当于hook了返回值    }}

几个在此提到的重点:

  • 我们声明了两个作为 pointcuts 的 public 方法,筛选出所有通过 “org.android10.gintonic.annotation.DebugTrace” 注解的方法和构造函数。
  • 我们使用 “@Around” 注解定义了 “weaveJointPoint(ProceedingJoinPoint joinPoint)” 方法,使我们的代码注入在使用 “@DebugTrace” 注解的地方生效。
  • “Object result = joinPoint.proceed();” 这行代码是被注解的方法执行的地方。因此,在此之前,我们启动我们的计时类计时,在这之后,停止计时。
  • 最后,我们构造日志信息,用 Android Log 输出。
    @DebugTrace    private int mapGUI() {        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        testAOP();        return 1;    }

四、SecurityCheckAnnotation

检查权限

4.1 定义注解

//第一个@Target表示这个注解只能给函数使用//第二个@Retention表示注解内容需要包含的Class字节码里,属于运行时需要的。@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface SecurityCheckAnnotation {//@interface用于定义一个注解。    public String declaredPermission();  //declarePermssion是一个函数,其实代表了注解里的参数}

4.2 定义切面

@Aspectpublic class SecurityCheckAnnotationAspect {    public static final String TAG = "MainActivity-SCAAspect";    /* 来看这个Pointcut,首先,它在选择Jpoint的时候,把@SecurityCheckAnnotation使用上了,这表明所有那些public的,并且携带有这个注解的API都是目标JPoint 接着,由于我们希望在函数中获取注解的信息,所有这里的poincut函数有一个参数,参数类型是SecurityCheckAnnotation,参数名为ann 这个参数我们需要在后面的advice里用上,所以pointcut还使用了@annotation(ann)这种方法来告诉AspectJ,这个ann是一个注解 */    @Pointcut("execution(@com.sangfor.aop.annotation.SecurityCheckAnnotation * *(..)) && @annotation(securityCheckAnnotation)")    public void checkPermssion(SecurityCheckAnnotation securityCheckAnnotation){};    /*    接下来是advice,advice的真正功能由check函数来实现,这个check函数第二个参数就是我们想要    的注解。在实际运行过程中,AspectJ会把这个信息从JPoint中提出出来并传递给check函数。    */    @Before("checkPermssion(securityCheckAnnotation)")    public void checkPermssionByAspect(JoinPoint joinPoint, SecurityCheckAnnotation securityCheckAnnotation){        //从注解信息中获取声明的权限。        String neededPermission = securityCheckAnnotation.declaredPermission();        Log.d(TAG, "checkPermssionByAspect--" + joinPoint.toShortString());        Log.d(TAG, "checkPermssionByAspect -- \tneeded permission is " + neededPermission);        return;    }}

参考文献

  • 深入理解Android之AOP
  • 看AspectJ在Android中的强势插入
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 如果有个恶毒又有心机的后妈怎么办 qq炫舞的好忘了怎么办 百度网盘别人发我一个链接我怎么办 感觉地下和墙壁里有蚂蚁怎么办? 射像头监控摄像头家用卡满了怎么办 微乐贵阳捉鸡麻将不显示头像怎么办 苹果6p用久了卡怎么办 小猪民宿平台限制三天上线怎么办 交易猫买家申请了仲裁我的钱怎么办 雷达石英手表表里面有小灰尘怎么办 电脑宽带用户名和密码忘记了怎么办 腾讯会员不让别人在异地登录怎么办 中毒后电脑文件后缀是lnk怎么办 wps逗号隔的空不一样大怎么办 电脑盘里的文件删不了怎么办 在淘宝买的qq账号被找回了怎么办 微信公众号的密码忘了怎么办 公众号安全助手密码忘了怎么办 微博账号存在发布违规信息怎么办 余额宝转出到银行卡被冻结怎么办 银行账户被冻结被转出钱怎么办 从余额宝转出的资金被冻结怎么办 微信账号卖了但是实名认证了怎么办 uc下载文档里的文档全没了怎么办 二手乐视没有账号和密码怎么办 华为账号密码忘了手机卡丢了怎么办 联想平板微信更新后不可兼容怎么办 小米手机刷完机账号密码忘了怎么办 红米手机的小米账号密码忘了怎么办 小米手机账号密码手机号忘了怎么办 小米手机忘了账号和密码怎么办 自己的小米账号密码忘了怎么办 小米手机丢了不记得小米账号怎么办 小米手环账号密码忘了怎么办 阴阳师一个区的账号找不到了怎么办 阴阳师手机账号代练登录了后怎么办 我的微信账号被盗更改密码了怎么办 vivo手机密保密码忘了怎么办 华为手机保密柜忘记密保问题怎么办 支付宝账号突然说没有了怎么办 快手号密码可能被盗登不上该怎么办