Android AOP library

Viewed 583

I've created simple AOP tracking project with com.archinamon.aspectj (https://github.com/Archinamon/android-gradle-aspectj). Everything works correctly and I'm able to log some android activities with aspects (in single module project).

The problem is that I need to create independent library to reuse it in multiple projects. I've created new android library module (with this tutorial https://developer.android.com/studio/projects/android-library) with such class:

package com.example.android_tracker
@Aspect
class LoggingAspect {

private val logger = Logger.getLogger("Logger")

@Pointcut("execution(void *.onClick(..))")
fun onClick() {
}

@Before("onClick() && args(view)")
fun beforeButtonClick(view: View) {
    if (view is TextView) {
        val text = view.text.toString();
        logger.log(
            Level.WARNING, "LOGGER: " +
                    "Logging aspect before onClick $text"
        )
    }
}

When I run appliaction following error occures:

java.lang.NoSuchMethodError: No static method aspectOf()Lcom/example/android_tracker/LoggingAspect; in class Lcom/example/android_tracker/LoggingAspect; or its super classes (declaration of 'com.example.android_tracker.LoggingAspect' appears in /data/app/com.example.aop_poc-lD_Wk32s3ddwX0esY9bGcw==/base.apk)

Configuration:

- project build gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.3.72"
repositories {
    google()
    jcenter()
    mavenCentral()
}
dependencies {
    classpath "com.android.tools.build:gradle:4.0.1"
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    classpath("com.archinamon:android-gradle-aspectj:4.2.1") //!!!
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}
}

-library module

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.archinamon.aspectj-provides' //!!!

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.1'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

- app module

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.archinamon.aspectj' //!!!


android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"

    defaultConfig {
        applicationId "com.example.aop_poc"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0'
    implementation 'androidx.navigation:navigation-ui-ktx:2.1.0'
    implementation project(':android-tracker')   //!!!
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

aspectj {
    includeAspectsFromJar ':android-tracker' //!!!
}

Did anyone have similar problem and know solution?

1 Answers

Disclaimer: I am an AspectJ + Maven guy, not a Gradle user. Also, I do not develop Android apps. So, even though being an AspectJ expert, I have zero experience with your scenario. So please be careful when verifying my answer.

There are two basic ways for working with aspect libraries in AspectJ:

  1. Use compile-time weaving in order to create woven byte code for your application. In order to do that, your application needs to be compiled with the AspectJ compiler and the aspect library on the aspect path. Maybe this is what you are trying to do, I do not speak Gradle and cannot tell you what you might have done wrong there. But please do make sure that the aspectjrt library is on the classpath when later running the app on your device. Without the AspectJ runtime you might get errors like the one you posted.

  2. Use load-time weaving, i.e. use -javaagent:/path/to/aspectjweaver.jar ... -cp /path/to/my-aspect-library.jar .... But AFAIK this is not an option for Android because Dalvik is not a normal JVM supporting classical Java agents.

So probably you want to investigate option 1 and fix your classpath. You might also have misconfigured the AspectJ plugin in one or both of your modules. I did not read the plugin description because I am really not interested in Gradle.

Related