如何使用
使用AspectJ做AOP可以做一些非侵入的AOP监控操作,方便简洁,功能强大,而且对目标工程没有侵入性,可以做切面的操作:监听方法耗时、输出日志、控制初入参数、进行运行时修改等等操作。
在Eclipse中已经有AJDT插件集成了AspectJ编译器的使用和关键字的声明。但是在Android Studio中没有这样的官方插件。因此,这里讲一下如何在Android Studio中使用AspectJ,来实现非侵入式的AOP监控。详细了解AspectJ的使用我在另一篇文章写了——Android基于AOP的非侵入式监控
AspectJ的使用核心就是它的编译器,它就做了一件事,将AspectJ的代码在编译期插入目标程序当中,运行时跟在其它地方没什么两样,因此要使用它最关键的就是使用它的编译器去编译代码ajc。ajc会构建目标程序与AspectJ代码的联系,在编译期将AspectJ代码插入被切出的PointCut中,已达到AOP的目的。
因此,无论在什么IDE上(如果使用命令行就可以直接使用ajc编译了),问题就是让IDE使用ajc作为编译器编译代码。
使用方法
1、插件:网上有人在github上提供了集成的插件gradle-android-aspectj-plugin,一开始我也是用的这个,但是在项目当中,无法兼容databinding,这个问题现在作者依然没有解决。
2、Gradle配置:通过在Gradle构建脚本中,定义任务来使得项目执行ajc编译,将AOP 的Module编织进入目标工程中,达到非侵入式AOP的目的。
下面就介绍一下第二种方法的具体的使用步骤。
Step
1、创建AS原工程
2、创建module(Android Library),然后添加AspectJ依赖至module中。
compile 'org.aspectj:aspectjrt:1.8.9'
3、编写build脚本,添加任务,使得IDE使用ajc作为编译器编译代码。
这里,分别对module、app的构建脚本添加一些任务,使得IDE使用ajc编译代码。
build.gradle(app):
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'
repositories {
mavenCentral()
}
dependencies {
compile project(':aspectjlibrary')
}
android {
compileSdkVersion 21
buildToolsVersion '22.0.1'
buildTypes {
debug {
minifyEnabled false // should disable proguard in debug builds
}
}
defaultConfig {
applicationId "com.example.lingyimly.try2"
minSdkVersion 15
targetSdkVersion 21
}
lintOptions {
abortOnError true
}
}
final def log = project.logger
final def variants = project.android.applicationVariants
//在构建工程时,执行编织
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
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;
}
}
}
}
build.gradle(module):
import com.android.build.gradle.LibraryPlugin
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
apply plugin: 'com.android.library'
buildscript {
repositories {
mavenCentral()
}
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 22
buildToolsVersion '23.0.1'
lintOptions {
abortOnError false
}
}
android.libraryVariants.all { variant ->
LibraryPlugin plugin = project.plugins.getPlugin(LibraryPlugin)
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", 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.error message.message, 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;
}
}
}
}
4、编写AspectJ切面程序代码
编写切面程序,做切面的操作:监听方法耗时、输出日志、控制初入参数、进行运行时修改
/**
* 截获类名最后含有Activity、Layout的类的所有非static方法(static方法另外加一个static修饰的execution或者call即可:execution( static * *..Activity+.*(..))
* 监听目标方法的执行时间
*/
@Aspect
public class TraceAspect {
private static Object currentObject = null;
//截获所有后缀为Activity或者Layout的类中所有方法的执行体(除了static,要监听static需要重新加一个static的execution 规则)
//target、this是用于截获运行时类型,便于做一些入参、出参的修改,或者做其他操作
private static final String POINTCUT_METHOD =
"(execution(* *..Activity+.*(..)) ||execution(* *..Layout+.*(..))) && target(Object) && this(Object)";
//截获所有后缀为Activity或者Layout的类中所有方法的调用(除了static,要监听static需要重新加一个static的execution 规则)
//target、this是用于截获运行时类型,便于做一些入参、出参的修改,或者做其他操作
private static final String POINTCUT_CALL = "(call(* *..Activity+.*(..)) || call(* *..Layout+.*(..))) && target(Object) && this(Object)";
@Pointcut(POINTCUT_METHOD)
public void methodAnnotated() {}
@Pointcut(POINTCUT_CALL)
public void methodCall(){}
/**
* 截获原方法,并替换
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("methodAnnotated()")
public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
if (currentObject == null){
currentObject = joinPoint.getTarget();
}
//初始化计时器
final StopWatch stopWatch = new StopWatch();
//开始监听
stopWatch.start();
//调用原方法的执行。
Object result = joinPoint.proceed();
//监听结束
stopWatch.stop();
//获取方法信息对象
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className;
//获取当前对象,通过反射获取类别详细信息
className = joinPoint.getThis().getClass().getName();
String methodName = methodSignature.getName();
String msg = buildLogMessage(methodName, stopWatch.getTotalTime(1));
if (currentObject != null && currentObject.equals(joinPoint.getTarget())){
DebugLog.log(new MethodMsg(className,msg,stopWatch.getTotalTime(1)));
}else if(currentObject != null && !currentObject.equals(joinPoint.getTarget())){
DebugLog.log(new MethodMsg(className, msg,stopWatch.getTotalTime(1)));
Log.e(className,msg);
currentObject = joinPoint.getTarget();
// DebugLog.outPut(new Path()); //日志存储
// DebugLog.ReadIn(new Path()); //日志读取
}
return result;
}
@After("methodCall()")
public void onCallAfter(JoinPoint joinPoint) throws Throwable{
Log.e("onCallAfter:", "class : "+joinPoint.getSignature().getDeclaringTypeName() + "method : " +((MethodSignature)joinPoint.getSignature()).getName());
}
/**
* 在截获的目标方法调用之前执行该Advise
* @param joinPoint
* @throws Throwable
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Before("methodCall()")
public void onCallBefore(JoinPoint joinPoint) throws Throwable{
Log.e("onCallBefore:", "class : "+joinPoint.getSignature().getDeclaringTypeName() + "method : " +((MethodSignature)joinPoint.getSignature()).getName());
Activity activity = null;
//获取目标对象,截获运行时类型
activity = ((Activity)joinPoint.getTarget());
//插入自己的实现,控制目标对象的执行
ChooseDialog dialog = new ChooseDialog(activity);
dialog.show();
//做其他的操作
buildLogMessage("test",20);
}
/**
* 创建一个日志信息
*
* @param methodName 方法名
* @param methodDuration 执行时间
* @return
*/
private static String buildLogMessage(String methodName, double methodDuration) {
StringBuilder message = new StringBuilder();
message.append(methodName);
message.append(" --> ");
message.append("[");
message.append(methodDuration);
if (StopWatch.Accuracy == 1){
message.append("ms");
}else {
message.append("mic");
}
message.append("] \n");
return message.toString();
}
}
5 运行结果
启动Activity,点击Button进入另一个Activity
07-20 11:12:30.991 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : setContentView
07-20 11:12:31.111 3536-3536/android10.org.viewgroupperformance E/onCallAfter:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : setContentView
07-20 11:12:31.111 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : mapGUI
07-20 11:12:31.131 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : findViewById
07-20 11:13:34.391 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : startActivity
07-20 11:13:34.441 3536-3536/android10.org.viewgroupperformance E/onCallAfter:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : startActivity
07-20 11:13:34.501 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.RelativeLayoutTestActivitymethod : setContentView
07-20 11:13:34.531 3536-3536/android10.org.viewgroupperformance E/onCallAfter:: class : org.android10.viewgroupperformance.activity.RelativeLayoutTestActivitymethod : setContentView
07-20 11:13:34.531 3536-3536/android10.org.viewgroupperformance E/org.android10.viewgroupperformance.activity.RelativeLayoutTestActivity: onCreate --> [32.708ms]