Real Malpractice, Real Reality Face Programming AOP for Android

Keywords: Android Gradle Programming Java

concept

The idea of programming in Java is OOP(Object-Oriented Progreming) object-oriented programming, which divides problems or functionality into modules, where each module interacts through a designed interface, resulting in a single principle of code responsibility, but sometimes we have many different types of modules that need the same functionality, at which time we also use OOP would be a hassle.So AOP was born, not only in Java but also in Android.

Take a chestnut: I currently want to know that every method of all activities in our project is executed and reflects its execution efficiency. If we write code as OOP, we need to call the method of getting time before and after each method in each Activity, and then calculate the execution time, print it from the Log log, or print it through interfaces, abstract classes, etc.Boss won't agree at this time because you've made so many changes. In case of a mistake, my App is not unstable. Of course, in the end you really can't do it and this function must exist. Then you can do it slowly by yourself...

AOP(Aspect-Oriented Progreming) Aspect-oriented programming, when you need certain modules to have the same functionality, then you can use AOP programming mode

First of all - environment building

1. Plug-ins: There are many ways to plug-in, and there are two types that I've seen so far

  • First
    gradle_plugin_android_aspectjx

  • Second
    First, you need to increase dependencies in build.gradle in the project root directory:
    classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1'
    Then add AspectJ dependency to build.gradle in the main project or library:
    compile 'org.aspectj:aspectjrt:1.8.9'
    Also add the AspectJX plug-in:
    apply plugin: 'com.jakewharton.hugo'

(The first one is a legend to me and the second one is used) But I don't know if they have any shortcomings. I only know that the first one is not compatible with databinding in the project, and I'm not sure if it still exists......

2.Gradle Configuration: By configuring it in a Gradle build script, here's how to do the second

  • First, install AspectJ first. I don't know if I want to install it or not, because I started running my own project unsuccessfully, followed by installation or unsuccessful. Finally, I found something wrong with the file I wrote, so I changed it successfully. Hope you can tell me when you finished the operation. Thank you, theoretically he is a jar file, so he is in AndroidStud.I just want to add dependencies to io, so I don't need to install them. Let's go first download Here you can download the corresponding version according to your own preferences, recommend that you learn to use the latest, stable company projects, specific Installation process

  • Create a new project in AndroidStudio and configure the build.gradle file in the app. My configuration is as follows:

apply plugin: 'com.android.application'
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.10'
        classpath 'org.aspectj:aspectjweaver:1.8.10'
    }
}

repositories {
    mavenCentral()
}

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.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.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;
            }
        }
    }
}

android {
    compileSdkVersion 25
    buildToolsVersion "26.0.0"
    defaultConfig {
        applicationId "com.leezp.xingyun.aspect_demo"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    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:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-beta4'
    testCompile 'junit:junit:4.12'
    compile 'org.aspectj:aspectjrt:1.8.10'
}

Then - code implementation

In the code, we need to build an extra class, which is a monitoring class. When the code is compiled, it will implant the method that the monitoring class needs to execute into the detected method. When the detected method is executed, the method that needs to be executed will also be executed along with it.

package com.leezp.xingyun.aspect_demo;

import android.app.Activity;
import android.util.Log;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * Created by xingyun on 2017/8/18.
 *
 * Listen for the **Visible** function in all activities and type its time out through the Log log
 *
 * The @Aspect flag below indicates that this class is a monitoring class
 */

@Aspect
public class TraceAspect {

    public static final String TAG = "TraceAspect";

    //Here is the string for the entry point, which is the same way to have it monitor (*..Activity+. * (.))
    // We won't talk about this matching rule this time, but we'll explain it when we get to the second edition
//    private static final String POINTCUT_METHOD = "execution(* *..Activity+.*(..))";

    //Here's the starting point, so we can do a few small things based on the method name of the starting point
//    @Pointcut(POINTCUT_METHOD)
    @Pointcut("execution(* *..Activity+.*(..))")
    public void methodLogger() {}

//    @Before("methodLogger()")
//    public void onMethodBefore(JoinPoint joinPoint) {
//        //You can do a few things before calling the entry point method, just do it here
//        //For example, get the class name of this method, pop up a prompt before calling the method, modify some data, etc.
//        //I'll just print out the class name of the method here
//        Log.e(TAG, "onMethodBefore: Class name:"+joinPoint.toString());
//        //Get the object of the method's class and manipulate it
//        Log.e(TAG, "onMethodBefore: Is it an activity?"+ (joinPoint.getTarget() instanceof Activity));
//        //You can do more and explore slowly yourself
//    }

    @After("methodLogger()")
    public void onMethodAfter(JoinPoint joinPoint) {
        //What you can do after calling the tangent method is done here
        //For example, get the class name of this method, pop up a prompt before calling the method, modify some data, etc.
        //I'll just print out the class name of the method here
        Log.e(TAG, "onMethodBefore: Class name:"+joinPoint.toString());
        //Get the object of the method's class and manipulate it
        Log.e(TAG, "onMethodBefore: Is it? Activity? "+(joinPoint.getTarget() instanceof Activity));
        //You can do more and explore slowly by yourself
    }

    @Around("methodLogger()")
    public Object onMethodAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //When a method executes, manipulate it
        //Method Execution Start, Timing
        long startTime = System.nanoTime();
        //Method Execution
        Object proceed = joinPoint.proceed();
        //Timing after method execution
        long endTime = System.nanoTime();

        //The proceed object of this Object is the return value of the method we are listening for
        //We can change this return value by changing some of the properties of this value

        Log.e(TAG, "onMethodAround: Efficiency of method execution:"+(endTime-startTime));
        return proceed;
    }

}

In this Friendship reminder: After, before, around cannot coexist when the three methods'incoming attributes are the same. At present, after and around can coexist because the incoming attribute of after is JoinPoint, and the incoming attribute of around is ProceedingJoinPoint. In fact, ProceedingJoinPoint is a subclass of JoinPoint, which means that the attributes of both methods can be the same!

Other code:

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private Button mChange;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mChange = (Button) findViewById(R.id.activity_main_change);
        mChange.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClass(MainActivity.this, TestActivity.class);
                startActivity(intent);
            }
        });
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.leezp.xingyun.aspect_demo.MainActivity">

    <TextView
        android:id="@+id/activity_main_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="30dp"
        android:text="Hello World!"
        android:textSize="20sp"
        android:textColor="#FF0000"/>

    <Button
        android:id="@+id/activity_main_change"
        android:layout_marginTop="50dp"
        android:layout_below="@id/activity_main_info"
        android:layout_alignLeft="@id/activity_main_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Switch to the second page" />

</RelativeLayout>

TestActivity.java

public class TestActivity extends Activity {

    private Button mBack;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        mBack = (Button) findViewById(R.id.activity_test_back);
        mBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onRestart() {
        super.onRestart();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

activity_test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/activity_test_back"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Return" />

</LinearLayout>

Display results:

Finally, I would like to recommend some articles for further study (also for myself to come back to see such problems in the future):

Posted by cutups on Sat, 01 Jun 2019 10:16:30 -0700