Movatterモバイル変換


[0]ホーム

URL:


Skip to content

@AndroidAopMatchClassMethod

Brief description

This aspect is used to match a class and its corresponding method. This aspect focuses on the execution of the method (Method Execution). Please note the difference between it and@AndroidAopReplaceClass.

@AndroidAopMatchClassMethod(targetClassName=targetclassname(includingpackagename),methodName=methodnamearray,type=matchtype,optional,defaultEXTENDSexcludeClasses=arrayofclassestoexcludeininheritancerelationship(validonlywhentypeisnotSELF),optionalexcludeWeaving=excludeweavingrangeincludeWeaving=includeweavingrange)
  • When targetClassName is an inner class, do not use the$ character, but use.

  • There are four types of type (defaultEXTENDS is not set):

    • SELF means that the match isitself of the class set by targetClassName
    • EXTENDS means that the match isall classes inherited from the class set by targetClassName
    • DIRECT_EXTENDS means that the match isdirectly inherited from the class set by targetClassName
    • LEAF_EXTENDS means that the match isTerminal inheritance (no subclasses) The class set by targetClassName
    graph LRC(Class C) ---> |Class C inherits from Class B| B{Class B};B --->|Class B inherits from Class A| A[Class A];B --->|DIRECT_EXTENDS / EXTENDS| A;C ---->|LEAF_EXTENDS / EXTENDS| A;D(Class D) --->|Class D inherits from Class A| A;D --->|DIRECT_EXTENDS/ LEAF_EXTENDS / EXTENDS| A;

    Simply put,LEAF_EXTENDS andDIRECT_EXTENDS are two extremes. The former focuses on the last node in the inheritance relationship, while the latter focuses on the first node in the inheritance relationship. Also note thatEXTENDS This type of match has a wide range, and all inherited intermediate classes may also add aspect codes

  • excludeClasses

    • If targetClassName is a class name, it means excluding some classes in the inheritance relationship. You can set multiple, and type is not SELF to be effective
    • If targetClassName is a package name, it means excluding some matched classes. You can set multiple, and type is SELF to be effective
  • overrideMethod defaults to false

    • Set to true, if there is no matching method in the subclass (non-interface, can be an abstract class), the parent class method is overwritten

      • targetClassName cannot contain *
      • methodName cannot define [ "*" ]
      • The overridden method cannot be private or final
    • Set to false, if there is no matching method in the subclass, no processing is done

  • excludeWeaving and includeWeaving are similar to exclude and include in thegetting started

Note

In addition, not all classes can be hooked in

  • type Type isSELF WhentargetClassName is set, the class must be the code in the installation package.For example: if this class (such as Toast) is inandroid.jar, it will not work. If you have such a requirement, you should use@AndroidAopReplaceClass
  • type When the type is notSELF, this aspect needs to have a matching method to work. If the subclass does not override the matching method, the subclass will not be matched. Use overrideMethod to ignore this restriction. But please note that the subclass must also be the code in the installation package
  • When you modify the configuration of this aspect, in most cases you should clean the project and continue development

Create an aspect processing class

The aspect processing class needs to implement the MatchClassMethod interface and handle the aspect logic in invoke

interfaceMatchClassMethod{funinvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?}

Note

If the point function is suspendClick here to view

Matching rules

You can see that in the following examples, some of the method names are set with only the method name, while others also have the return value type and parameter type. The following is an introduction

Fuzzy matching

    1. When targetClassName ends with.* and has other characters, andtype = MatchType.SELF, it matches all classes under the package, including subpackagesas shown in Example 9 below
    1. When methodName has only one method name*, it matches all methods in the classas shown in Example 8 below
    1. When methodName only writes the method name but does not write the return type and parameter type, it is also a fuzzy match, which will add all methods with the same name in the target class to the cut point

Note

For matching package names, I strongly recommend not doing this, as it intrudes too much code and significantly reduces the packaging speed. It is recommended to use it only for debugging and logging, and the original method should be released in full

Precise matching

Write the return type and parameter type on the methodName method name, so that you can accurately find a method to add a cut point (it will automatically degenerate to fuzzy matching when the precise matching is abnormal)

Matching formula:Return value type method name (parameter type, parameter type...)

  • Return value type can be omitted
  • Method name must be written
  • Parameter type can be omitted. If written, wrap it with(). Multiple parameter types are separated by,. If there is no parameter, just write()
  • Separate the return value type and method name with a space
  • If the return value type and parameter type are not written, it means no verification
  • Return value type and parameter type must be expressed in Java types. Except for the 8 basic types, other reference types arepackage name.class name
  • If the function is modified withsuspend, then the return value type is written regardless of the typesuspend, parameter types should still be written according to the above points
  • For generic information (such as collection List), the generic information must be erased

  • Different from targetClassName, if the method parameter and return value type are inner classes, they need to be replaced with$ instead of.

Note

AOP Code Generation Assistant, which can help you generate code with one click

Below is a comparison table of Kotlin and Java with different types. If it is Kotlin code, please check the corresponding code

(If you find any incomplete information, please give me feedback)

Kotlin typeJava type
Intint
Shortshort
Bytebyte
Charchar
Longlong
Floatfloat
Doubledouble
Booleanboolean
Int?java.lang.Integer
Short?java.lang.Short
Byte?java.lang.Byte
Char?java.lang.Character
Long?java.lang.Long
Float?java.lang.Float
Double?java.lang.Double
Boolean?java.lang.Boolean
Stringjava.lang.String
Unit(Or do not write)void
Unit?kotlin.Unit
Nothingjava.lang.Void
Anyjava.lang.Object

Other data types not listed above are reference types, and are written aspackage name.class name

Note

  1. vararg str : String in Kotlin is equivalent toString... in Java. In this matching, no matter what kind of code is used, it is represented byString[] (String is used as an example here, and other types are the same)
  2. For types with generics, do not write generics, for example,java.lang.List<String> methodName(java.lang.List<String>) should be directly written asjava.lang.List methodName(java.lang.List)

Examples of various scenarios

Example 1

Want to monitor all startActivity jumps inherited from the AppCompatActivity class

@AndroidAopMatchClassMethod(targetClassName="androidx.appcompat.app.AppCompatActivity",methodName={"startActivity"},type=MatchType.EXTENDS)publicclassMatchActivityMethodimplementsMatchClassMethod{@Nullable@OverridepublicObjectinvoke(@NonNullProceedJoinPointjoinPoint,@NonNullStringmethodName){// Write your logic herereturnjoinPoint.proceed();}}

⚠Note: For matching subclass methods, if the subclass does not override the matching method, it is invalid. Use overrideMethod to ignore this limitation

Example 2

If you want to hook all android.view.View.OnClickListener onClick, to put it simply, is to globally monitor all click events set onClickListener, the code is as follows:

@AndroidAopMatchClassMethod(targetClassName="android.view.View.OnClickListener",methodName=["onClick"],type=MatchType.EXTENDS//type must be EXTENDS because you want to hook all classes that inherit OnClickListener)classMatchOnClick:MatchClassMethod{// @SingleClick(5000) //Combined with @SingleClick, add multi-click protection to all clicks, 6 or notoverridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{Log.e("MatchOnClick","=====invoke=====$methodName")returnjoinPoint.proceed()}}

Here is a reminder for those who use lambda click monitoring;

ProceedJoinPoint The target is not android.view.View.OnClickListener - For Java, the target is the object of the class that sets the lambda expression - For Kotlin, the target is null

The methodName of the invoke callback is not onClick, but the method name automatically generated at compile time, similar to onCreate\(lambda\)14, which contains the lambda keyword

For the view of onClick(view:View) - If it is Kotlin code, ProceedJoinPoint.args[1] - If it is Java code, ProceedJoinPoint.args[0]

I will not go into details about this, you will know it after using it yourself;

To summarize: In fact, for all lambda's ProceedJoinPoint.args

  • If it is Kotlin, the first parameter is the object of the class that sets the lambda expression, and the subsequent parameters are all the parameters of the hook method
  • If it is Java, starting from the first parameter, it is all the parameters of the hook method

Example 3

The target class has multiple methods with the same name, and you only want to match one method (the exact matching rule is mentioned above)

packagecom.flyjingfish.test_lib;publicclassTestMatch{publicvoidtest(intvalue1){}publicStringtest(intvalue1,Stringvalue2){returnvalue1+value2;}}
For example, there is a class TestMatch with two methods named test. You only want to match the method test(int value1,String value2). Then write it as follows:

packagecom.flyjingfish.test_lib.mycut;@AndroidAopMatchClassMethod(targetClassName="com.flyjingfish.test_lib.TestMatch",methodName=["java.lang.String test(int,java.lang.String)"],type=MatchType.SELF)classMatchTestMatchMethod:MatchClassMethod{overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{Log.e("MatchTestMatchMethod","======"+methodName+",getParameterTypes="+joinPoint.getTargetMethod().getParameterTypes().length);// Write your logic here// If you don't want to execute the original method logic, 👇 don't call the following sentencereturnjoinPoint.proceed()}}

Example 4

When there are many levels of inheritance relationships, you don't want to add aspects to each level

@AndroidAopMatchClassMethod(targetClassName="android.view.View.OnClickListener",methodName=["onClick"],type=MatchType.EXTENDS// type must be EXTENDS because you want to hook all classes that inherit OnClickListener)classMatchOnClick:MatchClassMethod{// @SingleClick(5000) //Join @SingleClick to add anti-multiple clicks to all clicks, 6 or 6overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{Log.e("MatchOnClick","=====invoke=====$methodName")returnjoinPoint.proceed()}}
publicabstractclassMyOnClickListenerimplementsView.OnClickListener{@OverridepublicvoidonClick(Viewv){...//This is the necessary logic code}}
binding.btnSingleClick.setOnClickListener(object:MyOnClickListener(){overridefunonClick(v:View?){super.onClick(v)//Especially this sentence calls the parent class onClick and wants to retain the logic of executing the parent class methodonSingleClick()}})

Writing this way will cause the MyOnClickListener onClick above to also be added to the aspect, which is equivalent to a click that calls back twice the invoke of the aspect processing class, which may not be what we want, so we can change it like this

@AndroidAopMatchClassMethod(targetClassName="android.view.View.OnClickListener",methodName=["onClick"],type=MatchType.EXTENDS,excludeClasses=["com.flyjingfish.androidaop.test.MyOnClickListener"]//Adding this can exclude some classes)classMatchOnClick:MatchClassMethod{overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{Log.e("MatchOnClick","=====invoke=====$methodName")returnjoinPoint.proceed()}}
Or set type to LEAF_EXTENDS directly filters out the intermediate classes (this reminds me of an advertisement: no middlemen make a profit from the price difference)

Example 5

What if the entry point is a companion object?

Suppose there is such a code

packagecom.flyjingfish.androidaopclassThirdActivity:BaseActivity(){companionobject{funstart(){...}}}
The first letter of the companion object modifier companion is capitalized, as shown below
@AndroidAopMatchClassMethod(targetClassName="com.flyjingfish.androidaop.ThirdActivity.Companion",methodName=["start"],type=MatchType.SELF)classMatchCompanionStart:MatchClassMethod{overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{Log.e("MatchCompanionStart","======$methodName")returnjoinPoint.proceed()}}

Example 6

The entry point is Kotlin Member variables of the code, want to monitor the assignment and retrieval operations

packagecom.flyjingfish.androidaop.testclassTestBean{varname:String="test"}

In the code, we will have such operations

testBean.name="1111"//Assignment operationvalname=testBean.name//Get value operation

You can write like this

@AndroidAopMatchClassMethod(targetClassName="com.flyjingfish.androidaop.test.TestBean",methodName=["setName","getName"],type=MatchType.SELF)classMatchTestBean:MatchClassMethod{overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{Log.e("MatchTestBean","======$methodName");ToastUtils.makeText(ToastUtils.app,"MatchTestBean======$methodName")returnjoinPoint.proceed()}}

Example 7

If the cut point method issuspend What about modified functions?

packagecom.flyjingfish.androidaopclassMainActivity:BaseActivity2(){suspendfungetData(num:Int):Int{returnwithContext(Dispatchers.IO){getDelayResult()}}}

The exact match is written as follows. Regardless of the return value type of the matching function, writesuspend. For details, see theExact Matching Part

@AndroidAopMatchClassMethod(targetClassName="com.flyjingfish.androidaop.MainActivity",methodName=["suspend getData(int)"],type=MatchType.SELF)classMatchSuspend:MatchClassMethod{overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{Log.e("MatchSuspend","======$methodName")returnjoinPoint.proceed()}}

Example 8

Want to match all methods of a class

@AndroidAopMatchClassMethod(targetClassName="com.flyjingfish.androidaop.SecondActivity",methodName=["*"],type=MatchType.SELF)classMatchAllMethod:MatchClassMethod{overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{Log.e("MatchMainAllMethod","AllMethod======$methodName");returnjoinPoint.proceed()}}
Write only one method name and fill in * to match all methods

Example 9

Want to match all methods of all classes in a package

@AndroidAopMatchClassMethod(targetClassName="com.flyjingfish.androidaop.*",methodName=["*"],type=MatchType.SELF)classMatchAll:MatchClassMethod{overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{Log.e("MatchAll","---->${joinPoint.targetClass}--${joinPoint.targetMethod.name}--${joinPoint.targetMethod.parameterTypes.toList()}");returnjoinPoint.proceed()}}
1.* replacesclass name Or replacepart of the package name + class name, this example represents all classes under thecom.flyjingfish.androidaop package and its subpackages
2. Of course, the methodName part can still be filled with multiple fuzzy matching or even exact matching method names

Example 10

Want to match top-level functions or top-level extension functions

  • Top-level functions

Suppose the following function is located in a kotlin file namedContextEx

packagecom.androidaop.ktxfuntoast(text:String){}
@AndroidAopMatchClassMethod(targetClassName="com.androidaop.ktx.ContextExKt",type=MatchType.SELF,methodName=["void toast(java.lang.String)"])classMatchContextKt:MatchClassMethod{overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{returnjoinPoint.proceed()}}

As you can see, the signature of this top-level function is nothing special, except that Kt is added to the class name

  • Top-level extension functions

Still suppose the following function is located in a kotlin file namedContextEx in the kotlin file

packagecom.androidaop.ktxfunContext.hasPermission(permission:String):Boolean{returnContextCompat.checkSelfPermission(this,permission)==PackageManager.PERMISSION_GRANTED}
@AndroidAopMatchClassMethod(targetClassName="com.androidaop.ktx.ContextExKt",type=MatchType.SELF,methodName=["boolean hasPermission(android.content.Context,java.lang.String)"])classMatchContextKt:MatchClassMethod{overridefuninvoke(joinPoint:ProceedJoinPoint,methodName:String):Any?{returnjoinPoint.proceed()}}

This top-level extension function not only adds Kt to the class name, but also adds Kt to the class name. , and the first parameter of the function signature is your extension type, and the rest are the same


[8]ページ先頭

©2009-2026 Movatter.jp