赞
踩
android 开发,我们通常使用的是面向对象编程,这个写起来比较方便。但一些特殊的功能,比如说埋点统计些信息,或者打印某些方法的消耗时间,如果我们在要统计的地方直接写代码,看着不优雅,并且也把功能耦合在一起了。AOP 叫做切面编程,它更像一把刀切入到某个功能里面,不用直接耦合代码。比如如打印耗时日志,使用切面编程则可以把要统计的一些方法的代码统一放在一个地方,通过注解来引用,这样就比较完美的做到了代码分离。
AspectJ 是 AOP 的一个实现类库,我们可以直接使用它。先说说gradle配置方式,我们可以在 Android Studio 中建个 module 库,再库的 gradle 配置文件中,引入 aspectj 的单独脚本配置,我把它抽了出来,如下
- build.gradle
- ******************
-
- apply plugin: 'com.android.library'
- apply from: 'asplib.gradle'
- android {
- compileSdkVersion 28
- buildToolsVersion '28.0.3'
- }
-
- ******************
-
- asplib.gradle
- *********************
-
- import org.aspectj.bridge.MessageHandler
- import org.aspectj.tools.ajc.Main
-
- buildscript {
- repositories {
- mavenCentral()
- google()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:3.4.1'
- classpath 'org.aspectj:aspectjtools:1.8.1'
- }
- }
-
- dependencies {
- implementation 'org.aspectj:aspectjrt:1.8.1'
- }
-
-
- project.android.libraryVariants.all { variant ->
- JavaCompile javaCompile = variant.javaCompileProvider.get()
- javaCompile.doLast {
- String[] args = ["-showWeaveInfo",
- "-1.5",
- "-inpath", javaCompile.destinationDir.toString(),
- "-aspectpath", javaCompile.classpath.asPath,
- "-d", javaCompile.destinationDir.toString(),
- "-classpath", javaCompile.classpath.asPath]
-
- MessageHandler handler = new MessageHandler(true)
- new Main().run(args, handler)
- }
- }
-
- *******************************
-
在 module 中,定义一个类 TrAspect ,用 @Aspect 来修饰它,比如我们来打印下 Activity 的声明周期日志,
- @Aspect
- public class TrAspect {
-
- private static final String POINTCUT_ONMETHOD = "execution(* android.app.Activity.on**(..))";
-
- @Before(POINTCUT_ONMETHOD)
- public void beforeOnMethod(JoinPoint joinPoint) {
- MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
- String className = methodSignature.getDeclaringType().getSimpleName();
- String methodName = methodSignature.getName();
- Log.e("TrAspect", "before " + className + " " + methodName );
-
- }
-
- @After(POINTCUT_ONMETHOD)
- public void onMethLog(JoinPoint joinPoint){
- MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
- String className = methodSignature.getDeclaringType().getSimpleName();
- String methodName = methodSignature.getName();
- Log.e("TrAspect", "after " + className + " " + methodName );
- }
-
- }
在主工程 app 中,创建 MainActivity,重写 onCreate(Bundle savedInstanceState) 方法
- public class MainActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
-
- }
然后我们编译下,发现无效,什么原因呢?原来 app 主工程中 build.gradle 也需要配置(我也不清楚原因),抽取出来,build.gradle 中配置 apply from:'asp.gradle'
- asp.gradle
-
- ********************************
-
- import org.aspectj.bridge.IMessage
- import org.aspectj.bridge.MessageHandler
- import org.aspectj.tools.ajc.Main
-
-
- buildscript {
- repositories {
- mavenCentral()
- google()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:3.4.1'
- classpath 'org.aspectj:aspectjtools:1.8.1'
- }
- }
-
- dependencies {
- implementation project(':gintonic')
- implementation 'org.aspectj:aspectjrt:1.8.1'
- }
-
-
- 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.javaCompileProvider.get()
- 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)]
-
- 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
- }
- }
- }
- }
-
- ***************************
编译后,打印日志
- 2020-04-29 19:08:28.406 11794-11794/ E/TrAspect: before MainActivity onCreate
- 2020-04-29 19:08:28.495 11794-11794/ E/TrAspect: after MainActivity onCreate
细看 TrAspect 中使用的注解,@Before 和 @After 这两个,意思是在要切入的方法之前和之后执行,"execution(* android.app.Activity.on**(..))" 是我们配置的条件,这个里面是说 Activity 子类中所有以 on 开头的方法,当然是我们重写的方法,这就是个条件匹配,一旦匹配到了,马上执行方法。如果想把上面二合一,怎么办?使用 @Around,它具有 @Before 和 @After 的功能,见代码
- @Around(POINTCUT_ONMETHOD)
- public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
- MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
- String className = methodSignature.getDeclaringType().getSimpleName();
- String methodName = methodSignature.getName();
-
- Log.e("TrAspect", "before " + className + " " + methodName );
- Object result = joinPoint.proceed();
-
- Log.e("TrAspect", "after " + className + " " + methodName );
-
- return result;
- }
joinPoint.proceed(); 的意思就是执行 onCreate() 方法,打印值日,和上面的一样。
同理,埋点也可以这么做,我们可以通过判断方法名字,把日志写入缓存或文本中,然后在主工程中通过读取缓存输入,上报到服务端。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。