APP开发实战148-使用AOP技术输出Log的具体实现

来源:互联网 发布:故宫淘宝营销策略分析 编辑:程序博客网 时间:2024/06/05 22:40

31.4.2AOP技术的使用

(参考:

http://fernandocejas.com/2014/08/03/aspect-oriented-programming-in-android/)

 

在工程里添加一个Module,包含AspectJ相关功能代码,如下所示Module名为liba_aspectj:

此Module的build.gradle文件内容如下:

importcom.android.build.gradle.LibraryPluginimport org.aspectj.bridge.IMessageimport org.aspectj.bridge.MessageHandlerimport org.aspectj.tools.ajc.Mainapply plugin: 'com.android.library'buildscript {    repositories {        jcenter()    }    dependencies {        classpath'com.android.tools.build:gradle:2.1.0'        classpath'org.aspectj:aspectjtools:1.8.9'        classpath'org.aspectj:aspectjweaver:1.8.9'    }}repositories {    mavenCentral()}dependencies {    compile 'org.aspectj:aspectjrt:1.8.9'    compile'com.android.support:appcompat-v7:22.2.1'}android {    compileSdkVersion 23    buildToolsVersion '23.0.2'    lintOptions {        abortOnError false    }}android.libraryVariants.all { variant ->    LibraryPlugin plugin =project.plugins.getPlugin(LibraryPlugin)    JavaCompile javaCompile =variant.javaCompile    javaCompile.doLast {        String[] args =["-showWeaveInfo",                         "-1.8",//当前使用的JDK版本                        "-inpath", javaCompile.destinationDir.toString(),                        "-aspectpath", javaCompile.classpath.asPath,                         "-d",javaCompile.destinationDir.toString(),                         "-classpath",javaCompile.classpath.asPath,                        "-bootclasspath", plugin.project.android.bootClasspath.join(                File.pathSeparator)]        MessageHandler handler = newMessageHandler(true);        new Main().run(args, handler)        def log = project.logger        for (IMessage message :handler.getMessages(null, true)) {            switch (message.getKind()) {                case IMessage.ABORT:                case IMessage.ERROR:                case IMessage.FAIL:                    log.errormessage.message, message.thrown                    break;                case IMessage.WARNING:                case IMessage.INFO:                    log.infomessage.message, message.thrown                    break;                case IMessage.DEBUG:                    log.debugmessage.message, message.thrown                    break;            }        }    }}

 
如要在application模块的代码里使用AspectJ库,需要在build.gradle文件里添加如下代码:

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
    }
}

apply plugin: 'com.android.application'

dependencies {
    compile project(':liba_aspectj')
}

final def log = project.logger
final def variants = project.android.applicationVariants

variants.all { variant ->
    if (!variant.buildType.isDebuggable()){
        log.debug("Skippingnon-debuggable build type '${variant.buildType.name}'.")
        return;
    }

    JavaCompile javaCompile =variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.8",
                         "-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.warnmessage.message, message.thrown
                    break;
                case IMessage.INFO:
                    log.info message.message,message.thrown
                    break;
                case IMessage.DEBUG:
                    log.debugmessage.message, message.thrown
                    break;
            }
        }
    }
}

如要在library模块的代码里使用AspectJ库,需要在build.gradle文件里添加如下代码:

import com.android.build.gradle.LibraryPlugin
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
    }
}

apply plugin: 'com.android.library'

dependencies {
      compile project(':liba_aspectj')
}

 

android.libraryVariants.all { variant ->
    LibraryPlugin plugin =project.plugins.getPlugin(LibraryPlugin)
    JavaCompile javaCompile =variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.8",
                         "-inpath",javaCompile.destinationDir.toString(),
                         "-aspectpath",javaCompile.classpath.asPath,
                         "-d",javaCompile.destinationDir.toString(),
                         "-classpath",javaCompile.classpath.asPath,
                         "-bootclasspath",plugin.project.android.bootClasspath.join(
                File.pathSeparator)]

        MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler)

        def log = project.logger
        for (IMessage message :handler.getMessages(null, true)) {
            switch (message.getKind()) {
                case IMessage.ABORT:
                case IMessage.ERROR:
                case IMessage.FAIL:
                    log.errormessage.message, message.thrown
                    break;
                case IMessage.WARNING:
                case IMessage.INFO:
                    log.infomessage.message, message.thrown
                    break;
                case IMessage.DEBUG:
                    log.debugmessage.message, message.thrown
                    break;
            }
        }
    }
}

如要注入代码,需要先定义Pointcut, 常用的表达式格式如下:

execution(modifiers-pattern?ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

modifiers-pattern—修饰符
ret-type-pattern —方法的返回值类型
declaring-type-pattern—方法所属的包名或类名
name-pattern—方法名
param-pattern —方法参数
throws-pattern—方法抛出的异常类型

注:带?表示参数是可选项

使用的最频繁的返回类型模式是*,表示匹配任意的返回类型。

方法参数的具体表示:

()匹配了一个不接受任何参数的方法

(..)匹配了一个接受任意数量参数的方法(零或者更多)

(*)匹配了一个接受一个任何类型的参数的方法

(*,String)匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型。

以下是一些Pointcut表达式的例子。

任意公共方法的执行:

execution(public* *(..))

因为返回类型和方法名必须有,所以是两个**

 

任何一个名字以“on”开始的方法的执行:

execution(*on*(..))

 

xxx接口定义的任意方法的执行:

execution(*com.ruwant.eam.xxx.*(..))

 

在xxx包中定义的任意方法的执行:

Execution(*com.ruwant.xxx.*.*(..))

 

在xxx包或其子包中定义的任意方法的执行:

execution(*com.ruwant.eam..*.*(..))

定义Pointcut,除了execution指令外,还有一个常用的call指令,格式与execution指令一样,表示任意方法的调用。

 

如工程中包含如下包和对应的类:

想要输出com.ruwant.eam包里所有onCreate方法的执行时间,定义的Pointcut指令如下:

privatestatic final String POINT_METHOD = "execution(*com.ruwant.eam.activity.*..onCreate(..))";

 

之前提到,注入代码的方式有before、after和around三种,表示在目标方法执行之前、执行后和替代目标方法执行的代码。

1around方式的使用

统计方法的执行时间采用around方式比较方便。

具体的实现函数如下:

@Pointcut(POINT_METHOD)
public void methodAnnotated(){}

 

@Around("methodAnnotated()")
public Object aroundWeaverPoint(ProceedingJoinPoint joinPoint) throws Throwable{
    //获取方法信息对象
    MethodSignature methodSignature =(MethodSignature) joinPoint.getSignature();
    String className;
    //获取当前对象,通过反射获取类别详细信息
    className =joinPoint.getThis().getClass().getName();

    Log.e(TAG,className);

    //初始化计时器
    final StopWatch stopWatch = newStopWatch();
    //开始监听
    stopWatch.start();
    //调用原方法的执行。
    Object result = joinPoint.proceed();
    //监听结束
    stopWatch.stop();

    String methodName =methodSignature.getName();
    String msg =  buildLogMessage(methodName,stopWatch.getTotalTime());

    Log.e(TAG,msg);
    return result;
 }

 

执行结果如下:

有时需要跟踪程序代码是怎样运行的,各方法按怎样的顺序被调用的,可定义如下的Point:

privatestatic final String POINT_METHOD = "execution(*com.ruwant.eam.activity.*.*(..))";

采用around方式,可输出com.ruwant.eam.activity包里所有方法的执行顺序,执行结果如下:

    (这在阅读他人代码时极为有用,不用通过到处添加log语句或加断点,跟踪方法的执行顺序)

2 before和after方式的使用

在MainActivity.java中有如下代码:

@Override
protected void onCreate(BundlesavedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    initVariables();

    initViews();

    loadData();
}

 

private void initVariables() {


    Log.e(TAG,"exitinitVariables");
}

 

privatevoid initViews() {
    Log.e(TAG,"entryinitViews");
    …
}

 

定义的Pointcut和具体实现函数如下:

privatestatic final String POINT_METHOD =

"execution(*com.ruwant.eam.activity.MainActivity.initViews(..))";

privatestatic final String POINT_CALLMETHOD =

"call(*com.ruwant.eam.activity.MainActivity.initViews(..))";
private static final String POINTCUT_METHOD_AFTER =

"execution(**..MainActivity+.initVariables(..))";

@Pointcut(POINT_METHOD)
public void methodAnnotated(){}

@Pointcut(POINT_CALLMETHOD)
public void methodCallAnnotated(){}

@Pointcut(POINTCUT_METHOD_AFTER)
public void methodAnootatedWith(){}

 

@Before("methodCallAnnotated()")
public void beforeCall(JoinPoint joinPoint){

    Log.e(TAG, "beforeCall" +joinPoint.toShortString());
}

@After("methodAnootatedWith()")
public void afterCall(JoinPoint joinPoint) throws Throwable{
    Log.e(TAG, "afterCall" +joinPoint.toShortString());
}

 

aroundWeaverPoint函数的代码不变,执行结果如下:

 

0 0
原创粉丝点击