Switching AspectJ Plugins in Android
March 18, 2023 Update:
I no longer recommend using AOP in Android for various reasons. Please take a look at my article "Why I Don't Recommend Aspect-Oriented Programming in Android in 2023" before deciding to leverage AOP in your application so you can at least weigh the pros and cons!
This article will be talking about Aspect-Oriented Programming (AOP), particularly in the context of Android Applications. If you aren't familiar with this paradigm, I'd recommend reading my previous article on the subject: Aspect-Oriented Programming in Android
Artistic interpretation of AOP Code being woven
Trouble in AOParadise
Recently, when updating my Aspect-Oriented Programming Android sample project, I ran into issues for every Android Gradle Plugin (AGP) release version. Frequently with AGP updates, my previous aspect plugin of choice, android-gradle-aspectj, would break and I would be unable to update AGP until the plugin itself supported the newer AGP version.
This isn't ideal for various reasons and was made worse since some recent fixes have re-introduced older issues. Of course, since this is an open-source project, I can't exactly blame the maintainers since it's free software and no one is getting paid. I could potentially fix the issues myself and raise PRs every time the plugin broke with an AGP change (assuming I could understand the internals properly in the first place), but I wanted to find something that felt a little more stable.
At their core, most (if not all) AOP Plugins for Android are using AspectJ as their base, so I figured that as long as I switched to a plugin leveraging the same aspectjrt library, I would be able to swap out the plugins with relatively little effort. This is because the aspects that you write when using the android-gradle-aspectj plugin, for example, are referencing classes within the aspectjrt.jar rather than anything defined in the plugin itself.
Searching GitHub or Google for some AspectJ Android plugins/libraries, I found that the most popular options seemed to be the following (sorry about all the similar names!):
- gradle_plugin_android_aspectjx
This plugin seems like it would still have the fragile nature I described above since at the time of writing, the project's README specifies that it supports AGP 3.6.1. Further, contributing to the project or understanding the source code itself would likely be a little daunting for me personally as the docs are in Chinese, so I'd need to Google Translate basically everything as I went along. - android-gradle-aspectj
This is the plugin that I initially was having troubles with in the first place and was looking to replace. - gradle-android-aspectj-plugin
This project has a decent amount of stars on GitHub but is archived and no longer maintained. - GradleAndroidAspectJPlugin
No longer maintained.
Since the larger projects for weaving AspectJ in Android seemed unsuitable to me, I figured I'd have to start looking at smaller, newer projects.
Enter gradle-aspectj-pipeline-plugin
While looking for another project, I stumbled upon this article by Eric Schlenz on the Ibotta team: "Ibotta's Solution for AOP Weaving on Android".
I was curious what approach an actual company was taking and intrigued to find out that they had decided to create their own open-source AOP project for Android. They had found that the existing projects had limitations and decided to write their own to solve the issue. Their project is on GitHub and named gradle-aspectj-pipeline-plugin.
Since their article mentions that they use the "Android bytecode manipulation pipeline" and believe that they are the only Android AOP project doing so, I felt like it was worth a shot incorporating into my own sample project to see if it would be a suitable replacement. Since I wasn't using any of the more advanced options provided in the previous plugin, I tried to just switch over to their plugin.
In the top-level build.gradle, I had to add the Gradle plugin repository to my repositories block and replace the old plugin with the new one:
//Replace with latest plugin version classpath "com.ibotta:plugin:1.4.1"
//Replace with latest plugin version classpath("com.ibotta:plugin:1.4.1")
In the app-level build.gradle, I just needed to replace which plugin was applied and add a dependency on the aspectjrt dependency since the plugin doesn't add it automatically:
//Replace with latest dependency version implementation "org.aspectj:aspectjrt:1.9.7"
//Replace with latest dependency version implementation("org.aspectj:aspectjrt:1.9.7")
All-in-all, for a simple project the switch was painless. After running the app, I was able to verify that my existing aspect code was still weaved. I was able to see which locations were injected using the build/aop.log
file (as opposed to build.ajc-transform.log
in the previous plugin) . Most importantly, the aspects were still functioning as expected in the compiled application.
Stability?
The fact that the plugin switched with such a small amount of effort was great, but it didn't guarantee that the project would still remain functional across AGP updates. However, I did a quick test and it seemed that the same plugin version (1.0.7) did work on all of the AGP plugin versions between 3.0.1 and the current 4.1.1. Since AGP 3.0.1 was released in November 2017, this was a really good sign to me personally and made me feel confident switching, even if the gradle-aspectj-pipeline-plugin is relatively new or has a low number of contributors currently.
Just to be sure, I raised this issue to directly ask the developers about the items I was hesitant about. They mentioned that they did think that AGP updates could break things but figured it was unlikely since the bytecde mechanism they had mentioned has been introduced in April 2017 and the contract hasn't changed since then. Since this made sense to me, I decided to switch the sample project to this new plugin and update my previous AOP article.
Limitations & Considerations
Although this plugin supports Kotlin, Java, and Kotlin+Java codebases, it currently does not appear to support weaving in external aspects from outside the project source code and doesn't appear to be able to weave code inside of libraries (such as appcompat). If you are leveraging either of those features from alternate plugins, I don't think you would be able to make any switch at this time. If you have a more simple setup where your aspects all live inside your own project modules, I believe this plugin could work for you.
Another potential issue is that this project doesn't have much activity around it currently. This is likely due to a combination of factors. AOP isn't exactly super popular for Android in the first place so new libraries are unlikely to get a lot of attention. Further, codebases that are already working won't look to switch unless they are forced to, since generally switching libraries requires a decent amount of work.
This isn't a huge issue now, since the library is working for me directly, but it may be an issue for anyone implementing as there will be less eyes on the project. That being said, the project isn't abandoned and the maintainers are open to community contributions and suggestions.
Wrapping Up
Thanks for following along with me as I explained why I moved from android-gradle-aspectj to gradle-aspectj-pipeline-plugin for weaving AOP code in my Android builds. If you have any comments or questions, feel free to leave them below. I also would definitely recommend checking out the Ibotta Medium article and their plugin if you are interested in switching (or even doing a first implementation of) AOP in Android!
February 04, 2023 Update
On January 30, the Ibotta plugin that I had recommended in this article for adding AOP to Android applications has announced that the plugin is no longer supported due to upcoming changes in AGP 8.0 that will remove the bytecode transform API that had been leveraged by the plugin to weave aspects.
Because this and the previous plugin I had leveraged no longer appear to be supported, I do not currently have a recommendation for how to add AOP to an Android project.
My personal opinion these days is that in an enterprise codebase, leveraging AOP may be more trouble than it is worth. Although logging and other cross-cutting concerns can be handled in a single place with AOP, the knowledge of how AOP works is often silo-ed and becomes fragile as no one knows how it works (and are often unaware the code exists in the first place). Further, there is no native support for AOP in Android Studio, using aspects with Kotlin is hack-y, and there doesn't appear to be a good way to use AOP with Compose.
March 18, 2023 Update
I no longer recommend using AOP in Android for various reasons. Please take a look at my article "Why I Don't Recommend Aspect-Oriented Programming in Android in 2023" before deciding to leverage AOP in your application so you can at least weigh the pros and cons!
This article is also available on Medium. You can find it here.
There are currently no comments on this article, be the first to add one below
Add a Comment
Note that I may remove comments for any reason, so try to be civil. If you are looking for a response to your comment, either leave your email address or check back on this page periodically.