AOP in Android: Problems from Buried Points


AoP

Before reading, let's take a look at some questions about burial sites and see if this article is useful for you.

  • The entry and exit of each page need buried statistics.
  • Many button clicks require statistical reporting.
  • The performance data of some functions need statistics.

Well, if you encounter these problems, then tell you that AOP can solve your problems.

The earliest contact with AOP was in the process of talking with Mr. Zheng, who mentioned it. Before that, I met some buried problems and some of them were not perfect. So I consulted Mr. Zheng for some ideas to solve them. Thank you for your guidance.

There are a lot of people who are going for solutions. Just give me the fish. As for how to fish, I'll talk about it later. From a methodological point of view, this is certainly wrong, but from a practical point of view, it is the simplest and most efficient way. So this time I'm going directly to fish, and then I'll talk about how to fish.

For a slightly more specific example, let's first look at the general management methods.

public class TestActivity extends AppCompatActivity {
    private static final String TAG = TestActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
    }

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

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

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

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

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

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

    private void debugLog(String loginfo){
        LogUtils.i(this.getClass(), loginfo);
    }
}

Here, if we need to count Activity's life cycle (as an APP development, we have logged more or less like this), we will typically log several key life cycle functions of Activity, and then see what the whole activity's life cycle is like. If it's good to just count one activity, if it's more than one activity? Does every activity have to be written once?

Of course not. One of the programmers'missions is that they can never do it by themselves. So we let AOP help us solve it.

Specific AOP-related code is as follows:

@Aspect   //The @AspectJ annotation must be used so that class DemoAspect is equivalent to aspect DemoAspect
public class DemoAspect {
    static final String TAG = "DemoAspect";

    /*
    @Pointcut: pointcut It also becomes a comment, which is for a function, such as logForActivity() here.
    It actually represents the name of the pointcut. If it's pointcut with parameters, put the parameter type and name in
    logForActivity, which represents the name of pointcut, then uses the parameter name in the @Pointcut annotation.
    It's basically the same as before, but it's a bit odd to write. Later we will introduce examples with parameters.
    */
    @Pointcut("execution(* com.brothergang.demo.aop.TestActivity.onCreate(..)) ||"
            + "execution(* com.brothergang.demo.aop.TestActivity.onStart(..)) ||"
            + "execution(* com.brothergang.demo.aop.TestActivity.onResume(..)) ||"
            + "execution(* com.brothergang.demo.aop.TestActivity.onDestroy(..)) ||"
            + "execution(* com.brothergang.demo.aop.TestActivity.onPause(..))"
    )
    public void logForActivity() {
    }

    ;  //Note that this function must be implemented, otherwise the Java compiler will report errors

    /*
    @Before: This is Before advice, for after, after -returning, and after-throwing. For the annotation format is
    @After,@AfterReturning,@AfterThrowing. Before Following is the pointcut name, and then the code block is implemented by a function.
            For example, log here.
    */
    @Before("logForActivity()")
    public void log(JoinPoint joinPoint) {
        //For AspectJ using Annotation, JoinPoint can't get much more directly in the code, but needs to be passed
        //The parameters are passed in.
        Log.e(TAG, "AOP Buried Points:" + joinPoint.toShortString());
    }

    @Pointcut("execution(* android.view.View.OnClickListener.onClick(..))"
    )
    public void clickEvent() {
    }

    @Before("clickEvent()")
    public void logClickEvent(JoinPoint joinPoint) {
        //For AspectJ using Annotation, JoinPoint can't get much more directly in the code, but needs to be passed
        //The parameters are passed in.
        Log.e(TAG, "AOP Buried Points:" + joinPoint.toShortString());
    }
}

Of course, this alone does not work. First of all, dependencies need to be increased in build.gradle in the project root directory:

classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1'

Then add AspectJ dependencies in the build.gradle of the main project or library:

compile 'org.aspectj:aspectjrt:1.8.9'

At the same time, add AspectJX plug-in:

apply plugin: 'com.jakewharton.hugo'

Then Sync Now, all right, you can run. Let's see for ourselves. You can see that the white part is the buried point in the code, and the red part is the buried point through AOP.


Operation results

Well, after the practice is finished, we will have a deep understanding of the concept.

Here's the source code. If you feel good, give me a Star.

Personal home page: http://brothergang.me

Keywords: Gradle less Java Android

Added by Gorillas on Thu, 11 Jul 2019 22:30:56 +0300