性能优化,还得看AspectJ

public static final String TAG = “DemoAspect”;

@Before(“execution(* com.hujiang.library.demo.DemoActivity.test*(…))”)

public void testAspectBefore(JoinPoint point) {

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

}

}

运行时候打印:

testMethod-before com.hujiang.library.demo E/DemoAspect: testMethod-invoke

所以完成插入操作我们只需要

  1. 类上加入注释@Aspect

  2. 方法上加入注释@Before

  3. 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 Method

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 {

/**

  • 未登录情况下跳转至登录注册主界面

*/

}

使用很方便,毫无侵入性,后期也很好维护。类似的思想也可以实现:检查网络状况、检查权限状态、避免按钮多次点击、自动完成缓存等情况。

性能监控

AspectJ其实在Android中的应用主要还是性能监控、日志埋点等,下面以一个简单的例子表示:

我们监控布局加载耗时,判断布局是否嵌套过多或者过于复制导致Activity启动卡顿,首先我们知道Activity是通过setContentView方法加载布局的:

  1. 布局解析过程,IO过程

最后

愿你有一天,真爱自己,善待自己。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值