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函数的代码不变,执行结果如下:
- APP开发实战148-使用AOP技术输出Log的具体实现
- APP开发实战147-使用AOP技术输出Log
- 使用 Spring 的 AOP 机制来输出 Log
- APP开发实战128-APP Log功能设计
- Android系统开发中LOG的输出与使用
- Android系统开发中LOG的输出与使用
- [转]Android系统开发中LOG的输出与使用
- AOP具体实现
- 不使用spring的aop功能实现日志输出
- 项目实战:几十行代码实现更加强大的Log,全方位输出类-方法-行数-信息
- APP开发实战129-APP Log功能注意事项和Log数据格式化
- APP开发实战58-面向切面编程(AOP)
- APP开发实战12-业务逻辑的实现
- 使用Android.util的 Log类可以实现Android输出Log这一操作。
- Cocos2dx 使用CCLOG实现log输出
- cocos2d-x使用CCLOG实现log输出
- 使用变长参数实现log输出
- APP开发实战80-Fragment的使用场景
- php分页显示数据库内容
- 最小生成树(城市之间的最短距离)
- 配置方法数超过 64K 的应用
- copy构造函数的调用时机1和2
- 222. Count Complete Tree Nodes
- APP开发实战148-使用AOP技术输出Log的具体实现
- 【WEB】前端系统配色方案(全览)
- Android底部/顶部滑动菜单,方便调用的工具类
- Java Web乱码 解决request中文乱码的问题
- gradle学习笔记之hook task
- 深入理解Java虚拟机3~垃圾收集器参数总结
- 使用Unity连接Bmob后端云
- Java数据结构和算法:HashMap,哈希表,哈希函数
- 产品与运营文章索引