parent
1173843e39
commit
7428e29dfc
@ -0,0 +1,143 @@ |
|||||||
|
--- |
||||||
|
AOP 之 AspectJ |
||||||
|
--- |
||||||
|
|
||||||
|
#### 目录 |
||||||
|
|
||||||
|
1. 思维导图 |
||||||
|
2. AOP |
||||||
|
- 概念 |
||||||
|
- 术语 |
||||||
|
- 实现方式 |
||||||
|
3. AspectJ |
||||||
|
- 引入 |
||||||
|
- 注解定义 |
||||||
|
- 切点表达式 |
||||||
|
- 示例 |
||||||
|
- 全局点击事件拦截 |
||||||
|
- 方法统计耗时 |
||||||
|
4. 参考 |
||||||
|
|
||||||
|
#### 思维导图 |
||||||
|
|
||||||
|
#### AOP |
||||||
|
|
||||||
|
##### 概念 |
||||||
|
|
||||||
|
AOP 即面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。 |
||||||
|
|
||||||
|
在手动埋点的时候,需要在每个需要埋点的地方都添加代码,导致页面臃肿。还有就是在需要统计方法执行的时间的时候,不可能在每个方法执行前后都添加一段代码,所以就可以用到 AOP 的思想,它可以对一组相同的事物做统一处理。 |
||||||
|
|
||||||
|
##### 术语 |
||||||
|
|
||||||
|
1. Advice 通知、增强处理 |
||||||
|
|
||||||
|
即想要添加的功能,比如自动化埋点、统计方法耗时等等。 |
||||||
|
|
||||||
|
2. JoinPoint 连接点 |
||||||
|
|
||||||
|
允许你通知( Advice )的地方,方法的前前后后都是连接点。 |
||||||
|
|
||||||
|
3. PointCut 切入点 |
||||||
|
|
||||||
|
从连接点上,选择切点。 |
||||||
|
|
||||||
|
4. Aspect 切面 |
||||||
|
|
||||||
|
切面是通知和切入点的结合。现在发现了吧,没连接点什么事,连接点只是为了让你更好的理解切点从哪来。 |
||||||
|
|
||||||
|
5. Weaving 织入 |
||||||
|
|
||||||
|
把切面应用到目标对象来创建新的代理对象的过程。 |
||||||
|
|
||||||
|
##### 实现方式 |
||||||
|
|
||||||
|
AOP 和 OOP 都只是一种编程思想,实现 AOP 的方式有以下几种: |
||||||
|
|
||||||
|
1. AspectJ |
||||||
|
|
||||||
|
一个 Java 语言的面向切面编程的无缝扩展。 |
||||||
|
|
||||||
|
2. Javassist for Android |
||||||
|
|
||||||
|
用于字节码操作的知名 Java 类库 Javassist 的 Android 平台移植版。 |
||||||
|
|
||||||
|
3. DexMaker |
||||||
|
|
||||||
|
Dalvik 虚拟机上,在编译器或者运行时生成代码的 Java API。 |
||||||
|
|
||||||
|
4. ASMDEX |
||||||
|
|
||||||
|
一个类似 ASM 的字节码操作库,运行在 Android 平台,操作 Dex 字节码。 |
||||||
|
|
||||||
|
#### AspectJ |
||||||
|
|
||||||
|
##### 引入 |
||||||
|
|
||||||
|
引入直接看文末参考文章。 |
||||||
|
|
||||||
|
##### 注解定义 |
||||||
|
|
||||||
|
- @Aspect |
||||||
|
|
||||||
|
声明切面,标记类。 |
||||||
|
|
||||||
|
- @Pointcut(切点表达式) |
||||||
|
|
||||||
|
定义切点,标记方法。 |
||||||
|
|
||||||
|
- @Before(切点表达式) |
||||||
|
|
||||||
|
前置通知,切点之前执行。 |
||||||
|
|
||||||
|
- @Around(切点表达式) |
||||||
|
|
||||||
|
环绕通知,切点前后执行。 |
||||||
|
|
||||||
|
- @After(切点表达式) |
||||||
|
|
||||||
|
后置通知,切点之后执行。 |
||||||
|
|
||||||
|
- @AfterReturning(切点表达式) |
||||||
|
|
||||||
|
返回通知,切点方法返回结果之后执行。 |
||||||
|
|
||||||
|
- @AfterThrowing(切点表达式) |
||||||
|
|
||||||
|
异常通知,切点抛出异常时执行。 |
||||||
|
|
||||||
|
以上定义的切点都必须在标记了 @Aspect 的类中使用。 |
||||||
|
|
||||||
|
##### 切点表达式 |
||||||
|
|
||||||
|
切点表达式的结构如下: |
||||||
|
|
||||||
|
```java |
||||||
|
execution(<@注解类型模式>? <修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?) |
||||||
|
``` |
||||||
|
|
||||||
|
其中注解类型模式、修饰符模式、异常模式都是可选的。 |
||||||
|
|
||||||
|
##### 示例 |
||||||
|
|
||||||
|
全局事件拦截: |
||||||
|
|
||||||
|
```java |
||||||
|
@Before("execution(* android.view.View.OnClickListener.onClick(android.view.View))") |
||||||
|
public void onViewClickBefore(JoinPoint joinPoint) { |
||||||
|
Log.i(TAG, "onViewClickBefore: 点击事件之前执行"); |
||||||
|
} |
||||||
|
|
||||||
|
@After("execution(* android.view.View.OnClickListener.onClick(android.view.View))") |
||||||
|
public void onViewClickAfter(JoinPoint joinPoint) { |
||||||
|
Log.i(TAG, "onViewClickAfter: 点击事件之后执行"); |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
但是我们发现其实切点表达式是一样的,那就就可以抽成一个 Pointcut 来复用,即: |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 参考 |
||||||
|
|
||||||
|
[Android 面向切面编程(AOP)](https://www.jianshu.com/p/aa1112dbebc7) |
@ -0,0 +1,90 @@ |
|||||||
|
```java |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 我想统计两个类中方法执行耗时,BootActivity 中的统计没什么问题, |
||||||
|
* 但是 CommonRequest 类中的统计直接导致应用崩溃 |
||||||
|
* 类转换异常?很萌 |
||||||
|
* AspectJ 才学,希望大佬能帮帮我,不胜感激 |
||||||
|
*/ |
||||||
|
|
||||||
|
com.xxx.BootActivity |
||||||
|
public class BootActivity extends Activity{ |
||||||
|
onCreate(){ |
||||||
|
CommonRequest.method1(this); |
||||||
|
CommonRequest.method2(this); |
||||||
|
//... |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
com.yyy.CommonRequest |
||||||
|
public class CommonRequest{ |
||||||
|
public static void mothod1(Context context){ |
||||||
|
//网络请求 |
||||||
|
new HttpRequest(context).callback(new CallBack()){ |
||||||
|
// |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//切面类 |
||||||
|
@Aspect |
||||||
|
public class MethodTimeTracker { |
||||||
|
|
||||||
|
//这个切点执行正常 |
||||||
|
@Around("execution( * com.xxx.BootActivity.* (..))") |
||||||
|
public void bootTracker(ProceedingJoinPoint joinPoint) throws Throwable { |
||||||
|
long beginTime = SystemClock.currentThreadTimeMillis(); |
||||||
|
joinPoint.proceed(); |
||||||
|
long endTime = SystemClock.currentThreadTimeMillis(); |
||||||
|
long dx = endTime - beginTime; |
||||||
|
Log.i("2333", joinPoint.getSignature().getDeclaringType().getName() + "#" + joinPoint.getSignature().getName() |
||||||
|
+ " " + dx + " ms"); |
||||||
|
} |
||||||
|
|
||||||
|
//这个切点直接导致应用崩溃 |
||||||
|
@Around("execution( * com.yyy.CommonRequest.* (..))") |
||||||
|
public void requestTracker(ProceedingJoinPoint joinPoint) throws Throwable { |
||||||
|
long beginTime = SystemClock.currentThreadTimeMillis(); |
||||||
|
joinPoint.proceed(); |
||||||
|
long endTime = SystemClock.currentThreadTimeMillis(); |
||||||
|
long dx = endTime - beginTime; |
||||||
|
Log.i("2333", joinPoint.getSignature().getDeclaringType().getName() + "#" + joinPoint.getSignature().getName() |
||||||
|
+ " " + dx + " ms"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Log 日志: |
||||||
|
java.lang.RuntimeException: Unable to start activity ComponentInfo{.BootActivity}: java.lang.ClassCastException: BootActivity cannot be cast to cn.shuhe.dmnetwork.network.CjjHttpRequest |
||||||
|
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913) |
||||||
|
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) |
||||||
|
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) |
||||||
|
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) |
||||||
|
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) |
||||||
|
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) |
||||||
|
at android.os.Handler.dispatchMessage(Handler.java:106) |
||||||
|
at android.os.Looper.loop(Looper.java:193) |
||||||
|
at android.app.ActivityThread.main(ActivityThread.java:6669) |
||||||
|
at java.lang.reflect.Method.invoke(Native Method) |
||||||
|
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) |
||||||
|
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) |
||||||
|
Caused by: java.lang.ClassCastException: BootActivity cannot be cast to CjjHttpRequest |
||||||
|
at cn.shuhe.projectfoundation.CommonRequest$AjcClosure15.run(CommonRequest.java:1) |
||||||
|
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149) |
||||||
|
at com.dataseed.cjjanalytics.aspect.MethodTimeTracker.requestTracker(MethodTimeTracker.java:29) |
||||||
|
at cn.shuhe.projectfoundation.CommonRequest.checkDeviceInfo(CommonRequest.java:223) |
||||||
|
at com.dataseed.huanbei.ui.BootActivity.startUp(BootActivity.java:296) |
||||||
|
at com.dataseed.huanbei.ui.BootActivity.onCreate_aroundBody2(BootActivity.java:129) |
||||||
|
at com.dataseed.huanbei.ui.BootActivity$AjcClosure3.run(BootActivity.java:1) |
||||||
|
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149) |
||||||
|
at com.dataseed.cjjanalytics.aspect.CjjTraceAspect.weaveActivityOnCreateJoinPoint(CjjTraceAspect.java:225) |
||||||
|
at com.dataseed.huanbei.ui.BootActivity.onCreate(BootActivity.java:126) |
||||||
|
at android.app.Activity.performCreate(Activity.java:7136) |
||||||
|
at android.app.Activity.performCreate(Activity.java:7127) |
||||||
|
|
||||||
|
|
||||||
|
类转换异常是怎么肥事? |
||||||
|
|
||||||
|
|
||||||
|
``` |
||||||
|
|
Loading…
Reference in new issue