性能优化,还得看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过程

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

代码真的是重质不重量,质量高的代码,是当前代码界提倡的,当然写出高质量的代码肯定需要一个相当高的专业素养,这需要在日常的代码书写中逐渐去吸收掌握,谁不是每天都在学习呀,目的还不是为了一个,为实现某个功能写出高质量的代码。

所以,长征路还长,大家还是好好地做个务实的程序员吧。

最后,小编这里有一系列Android提升学习资料,有兴趣的小伙伴们可以来看下哦~

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

613802674)]

[外链图片转存中…(img-7tlwqTuW-1713613802675)]

[外链图片转存中…(img-ucBJ1rkt-1713613802676)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

代码真的是重质不重量,质量高的代码,是当前代码界提倡的,当然写出高质量的代码肯定需要一个相当高的专业素养,这需要在日常的代码书写中逐渐去吸收掌握,谁不是每天都在学习呀,目的还不是为了一个,为实现某个功能写出高质量的代码。

所以,长征路还长,大家还是好好地做个务实的程序员吧。

最后,小编这里有一系列Android提升学习资料,有兴趣的小伙伴们可以来看下哦~

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值