For example, if you want to log at the start and end of a method, is it realistic to write it for every method? Assuming you embed that process in a particular method to measure processing time for performance measurement, would you like to modify your code for that? Both are No, aren't they? Aspect-oriented programming provides a "relatively easy" way to solve them. By incorporating this, you can focus on each interest.
I searched for various articles, but I couldn't find an article that was written in Japanese, so [here](https://fernandocejas.com/2014/08/03/aspect-oriented-programming- Write an article based on in-android /). Basically, the code follows the reference page.
I will omit the question of what aspect-oriented programming is. Please refer to other articles and literature for academic content and terms.
From here, I will explain the procedure for making something that works concretely.
Before that, this article will take you to include the source code in your project. If you cut it out as another project and add it to the project as a library, please read as appropriate. I think the only thing you need to read is rewrite build.gradle.
I will explain the following two methods for that.
The former also uses AspectJ, but the plugin does the tedious settings for you. Therefore, it is easy to set up. The latter uses AspectJ as it is, so you need to write a build process in app / build.gradle to build.
First, make a mark to interrupt the process. The landmark is an annotation.
DebugTraceBefore.java
@Retention(RetentionPolicy.CLASS)
@Target({ ElementType.METHOD })
public @interface DebugTraceBefore {}
DebugTraceAround.java
@Retention(RetentionPolicy.CLASS)
@Target({ ElementType.METHOD })
public @interface DebugTraceAround {}
Please refer to another article for Retention and Target of annotations.
We have prepared two types of landmarks.
debugtracebefore
Before the method that set this annotation,
debugtracearound
Makes the process interrupt before and after the method that sets this annotation.
The name of the annotation is arbitrary.
Next, write what kind of processing is interrupted. This time I will write the process to output the log.
@aspect
Is a mark that the process of interrupting by aspect-oriented programming is written here.
@pointcut
Now, set a mark to interrupt the process.
Let's call the method with this annotation set as pointcut method </ b>.
@before
Or@after
、@around
Indicates where to interrupt the process, and specifies which pointcut method to interrupt.
The method name is arbitrary, but it's a good idea to give it a descriptive name.
Since DebugTraceBefore etc. are annotations, execution(...)among@Is attached.
If it is a method of the specified class, it can be specified as ```execution (void android.app.Activity.onCreate (..))` ``.
#### **`execution(...)You must specify the full package name for.`**
AspectDebugLog.java
@Aspect
public final class AspectDebugLog {
private static final String POINTCUT_BEFORE_METHOD =
"execution(@com.test.aspectorientationprogrammingsample.aspect.DebugTraceBefore * *(..))";
private static final String POINTCUT_AROUND_METHOD =
"execution(@com.test.aspectorientationprogrammingsample.aspect.DebugTraceAround * *(..))";
@Pointcut(POINTCUT_BEFORE_METHOD)
public void pointcutDebugTraceBefore() {}
@Pointcut(POINTCUT_AROUND_METHOD)
public void pointcutDebugTraceAround() {}
@Pointcut("execution(void android.app.Activity.onCreate(..))")
public void pointcutOnCreate() {}
@Before("pointcutOnCreate()")
public void weavePreOnCreate(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
Log.d("Aspect", "### weavePreOnCreate: " + className);
}
@After("pointcutOnCreate()")
public void weavePostOnCreate(JoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
Log.d("Aspect", "### weavePostOnCreate: " + className);
}
@Before("pointcutDebugTraceBefore()")
public void weaveDebugTraceBefore(JoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
Log.d("Aspect", "### weaveDebugTraceBefore: " + className + " " + methodName);
}
@Around("pointcutDebugTraceAround()")
public Object weaveDebugTraceAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
Log.d("Aspect", "### weaveDebugTraceAround - start: " + className + " " + methodName);
Object result = joinPoint.proceed();
Log.d("Aspect", "### weaveDebugTraceAround - end: " + className + " " + methodName);
return result;
}
}
Next, create a screen.
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button1).setOnClickListener(
new View.OnClickListener() {
@Override
@DebugTraceAround
public void onClick(View view) {
Log.d("Log", "### onClick: button1");
}
});
findViewById(R.id.button2).setOnClickListener(
new View.OnClickListener() {
@Override
@DebugTraceBefore
public void onClick(View view) {
Log.d("Log", "### onClick: button2");
}
});
}
// @Override
// public void onResume() {
// super.onResume();
// }
@Override
@DebugTraceBefore
public void onPause() {
super.onPause();
}
}
It is a screen with only two buttons.
onCreate(...)The annotation created this time is not set in.
```onpause() ```To output the log before```debugtracebefore```Is specified.
I set an annotation to output a log even when the button is clicked.
That's it for writing Java code.
From here, rewrite build.gradle.
## How to use the plugin
In the method of using the plug-in, I will introduce an example of using this.
[GradleAspectJ-Android](https://github.com/Archinamon/android-gradle-aspectj)
#### **`app/build.gradle`**
```gradle
//Add from here
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url "https://jitpack.io" }
}
dependencies {
classpath 'com.github.Archinamon:GradleAspectJ-Android:3.0.3'
}
}
//Add up to here
apply plugin: 'com.android.application'
apply plugin: 'com.archinamon.aspectj'//add to
android {
compileSdkVersion 27
buildToolsVersion "27.0.1"
defaultConfig {
applicationId "com.test.aspectorientationprogrammingsample"
minSdkVersion 21
targetSdkVersion 27
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
multiDexEnabled true
minifyEnabled true // true:Obfuscate(It is necessary to set classes, methods, etc. that are not obfuscated as appropriate.)、false:Do not obfuscate
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:27.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
}
There are some comment outs along the way, but it's okay to delete them.
app/build.gradle
//Add from here
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.1'
}
}
//Add up to here
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
buildToolsVersion "27.0.1"
defaultConfig {
applicationId "com.test.aspectorientationprogrammingsample"
minSdkVersion 21
targetSdkVersion 27
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
multiDexEnabled true
minifyEnabled true // true:Obfuscate(It is necessary to set classes, methods, etc. that are not obfuscated as appropriate.)、false:Do not obfuscate
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
//Add from here
applicationVariants.all { variant ->
// JavaCompile javaCompile = variant.javaCompile// use javaCompiler instead of javaCompile
// javaCompile.doLast {
// //
// }
variant.javaCompiler.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)]
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;
}
}
}
}
//Add up to here
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:27.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
compile 'org.aspectj:aspectjrt:1.8.1'//add to
}
When you do this, the log will be output as follows: (Since it becomes difficult to read, time etc. are deleted.)
D/Aspect: ### weavePreOnCreate: MainActivity
D/Aspect: ### weavePostOnCreate: MainActivity
D/Aspect: ### weaveDebugTraceBefore: MainActivity onPause
D/Aspect: ### weaveDebugTraceAround - start: onClick
D/Log: ### onClick: button1
D/Aspect: ### weaveDebugTraceAround - end: onClick
D/Aspect: ### weaveDebugTraceBefore: onClick
D/Log: ### onClick: button2
I also confirmed that it works in my environment, but how was it in your environment? Did it work?
One point to note is that execution (void android.app.Activity.onCreate (..))` `` can be changed to
execution (void android.app.Activity.onResume ())` `` No log is output.
Because MyActivity does not override onResume.
Logs of unimplemented methods are not output.
It's natural.
Now you can cut out the extra logic!
Appendix
We have confirmed the operation in the following environment.
environment | version |
---|---|
Mac OS | Sierra 10.12.6 |
Android Studio | 2.3.3 |
Java version | 8 |
Gradle version | 3.3 |
Android Plugin version | 2.3.3 |
Recommended Posts