写点什么

性能优化,还得看 AspectJ,android 高级开发实战

用户头像
Android架构
关注
发布于: 2021 年 11 月 06 日
  1. 方法上加入注释 @Before

  2. Before 里写入要插入的相关信息


是不是很简单,下面就一一详细讲解。

[](

)Advice


就是说我们要插入的代码以何种方式插入,就是方法上的注释。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vjyhrNO4-1569575936472)(https://upload-images.jianshu.io/upload_images/15679108-cf3986276b65e6a4?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]


Before 和 After 很好理解,上面的例子已经展示的很清楚了。


AfterReturning


适用于需要获取到返回值的情况比如:


private int AfterReturningTest() {


Log.e(DemoAspect.TAG, "AfterReturning-invoke");


return 10;


}


@AfterReturning(value = "execution(* com.hujiang.library.demo.DemoActivity.AfterReturning*(..))", returning = "num")


public void testAspectAfterReturning(int num) {


Log.e(TAG, "AfterReturning-num:" + num);


}


这样就可以在切面方法里获取到返回值了,值得注意的是方法参数必须和注解中的值一致。


【returning = “num”】===【int num】


AfterThrowing


适用于收集和监控异常信息。


private void AfterThrowingTest() {


View v = null;


v.setVisibility(View.VISIBLE);


}


@AfterThrowing(value = "execution(* com.hujiang.library.demo.DemoActivity.AfterThrowing*(..))", throwing = "exception")


public void testAspectAfterReturning(Exception exception) {


Log.e(TAG, "AfterThrowing-exception:" + exception.getMessage());


}


同样,参数和注解里的值必须一致。这里崩溃同样会发生,不会因为切面操作而直接 catch 住,只是在抛出异常之前会打印出异常信息而已。


Around


在方法执行前后都可调用,比较灵活。


private void AroundTest() {


Log.e(DemoAspect.TAG, "AroundTest-invoke");


}


@Around("execution(* com.hujiang.library.demo.DemoActivity.AroundTest(..))")


public void testAspectAround(ProceedingJoinPoint point) throws Throwable {


Log.e(TAG, point.getSignature().getName() + "-before ");


point.proceed();


Log.e(TAG, point.getSignature().getName() + "-after ");


}


通过执行 ProceedingJoinPoint 的 proceed 方法调用原方法,灵活控制。如果你想的话也可以不调用,直接拦截了。


Pointcut


告诉代码注入工具,在何处注入一段特定代码的表达式。也就是例子中的这句话:


@Before("execution(* com.hujiang.library.demo.DemoActivity.test*(..))")


我们分成几个部分依次来看:


@Before:Advice,也就是具体的插入点,我们已经介绍过


execution:处理 Join Point 的类型,例如 call、execution、withincode


其中 call、execution 类似,都是插入代码的意思,区别就是 execution 是在被切入的方法中,call 是在调用被切入的方法前或者后。


//对于 Call 来说:


Call(Before)


Pointcut{


Pointcut Method


}


Call(After)


//对于 Execution 来说:


Pointcut{


execution(Before)


Pointcut M


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ethod


execution(After)


}


withcode 这个语法通常来进行一些切入点条件的过滤,作更加精确的切入控制,比如:


@Override


protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);


setContentView(R.layout.activity_main);


test1();


test2();


}


public void test() {


Log.e("qiuyunfei", "test");


}


public void test1() {


test();


}


public void test2() {


test();


}


假如我们想要切 test 方法,但是只希望在 test2 中调用 test 才执行切面方法,就需要用到 withincode。


// 在 test()方法内


@Pointcut("withincode(* com.hujiang.library.aspect.MainActivity.test2(..))")


public void invoke2() {


}


// 调用 test()方法的时候


@Pointcut("call(* com.hujiang.library.aspect.MainActivity.test(..))")


public void invoke() {


}


// 同时满足前面的条件,即在 test2()方法内调用 test()方法的时候才切入


@Pointcut("invoke() && invoke2()")


public void invokeOnlyIn2() {


}


@Before("invokeOnlyIn2()")


public void beforeInvokeOnlyIn2(JoinPoint joinPoint) {


String key = joinPoint.getSignature().toString();


Log.d(TAG, "beforeInvokeOnlyIn2: " + key);


}


MethodPattern:这个是最重要的表达式,大致为:@注解和访问权限,返回值的类型和包名.函数名(参数)


@注解和访问权限(public/private/protect,以及 static/final):属于可选项。如果不设置它们,则默认都会选择。以访问权限为例,如果没有设置访问权限作为条件,那么 public,private,protect 及 static、final 的函数都会进行搜索。


返回值类型:就是普通的函数的返回值类型。如果不限定类型的话,就用*通配符表示。


包名.函数名:用于查找匹配的函数。可以使用通配符,包括和…以及+号。其中号用于匹配除.号之外的任意字符,而…则表示任意子 package,+号表示子类。


  • com.hujiang.library.demo.DemoActivity.test*(..)


第一部分:『』表示返回值,『』表示返回值为任意类型。


第二部分:就是典型的包名路径,其中可以包含『』来进行通配,几个『』没区别。同时,这里可以通过『&&、||、!』来进行条件组合。类似【test*】的写法,表示以 test 开头为方法名的任意方法。


第三部分:()代表这个方法的参数,你可以指定类型,例如 android.os.Bundle,或者(…)这样来代表任意类型、任意个数的参数,也可以混合写法(android.os.Bundle,…)这样来代表第一个参数为 bundle,后面随意。


自定义 Pointcuts:有时候我们需要指定哪些方法需要进行 AOP 操作,目标明确,也可以通过注解的方式来完成。首先声明注解:


@Retention(RetentionPolicy.CLASS)


@Target({ElementType.METHOD, ElementType.TYPE})


public @interface AspectAnnotation {


}


然后在切面类中定义:


//定义一个使用该注解的 Pointcut


@Pointcut("execution(@com.hujiang.library.aspect.AspectAnnotation * *(..))")


public void AspectAnnotation(){


}


@Before("AspectAnnotation()")


public void testAspectAnnotation(JoinPoint point){


Log.e(TAG, point.getSignature().getName() + "-Before ");


}


//在 Activity 中使用


@Override


protected void onCreate(@Nullable Bundle savedInstanceState) {


super.onCreate(savedInstanceState);


AnnotationTest();


}


@AspectAnnotation


private void AnnotationTest() {


Log.e(DemoAspect.TAG, "AnnotationTest-invoke");


}


使用很简单,前面也介绍过 MethodPattern,注释 @在这里就不能省略了。


[](


)/? ?AspectJ 实战? ?/


============================================================================


实现登录检查的操作


很多 app 都有这个需求,在操作前提醒用户注册登录,跳转到注册或者登录界面,如果用 AspectJ 实现就显得非常简洁且无侵入性。


private static final String TAG = "AspectCommonTool";


@Pointcut("execution(@xxx.aspectj.annotation.NeedLogin * *(..))")


public void needLoginMethod() {


}


/**


  • 在 @NeedLogin 方法中插入

  • 若在非 Activity 中使用 @NeedLogin,参数必须传入 Context 作为跳转起始页


*/


@Around("needLoginMethod()")


public void onNeedLoginAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {


Context mContext = null;


//proceedingJoinPoint.getThis()可以获取到调用该方法的对象


if (proceedingJoinPoint.getThis() instanceof Context) {


mContext = (Context) proceedingJoinPoint.getThis();


} else {


//proceedingJoinPoint.getArgs()可以获取到方法的所有参数


for (Object context : proceedingJoinPoint.getArgs()) {


if (context instanceof Context) {


mContext = (Context) context;


break;


}


}


}


if (mContext == null) {


return;


}


if (LoginUtils.isLogin()) {


/**


  • 如果用户登录则执行原方法


*/


proceedingJoinPoint.proceed();


} else {


/**

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
性能优化,还得看AspectJ,android高级开发实战