最近,在纽约证券交易所上市一年后,中国电动汽车初创公司威来似乎遇到了麻烦。第二季度财报显示,由于旗舰电动车ES8销量大幅下滑,公司2019年第二季度亏损4.79亿美元,仅实现收入2.2亿美元。
这篇文章来自verymrq的提交,分享了AspectJ的相关知识,AspectJ是性能优化的产物。相信会对大家有帮助!同时也感谢作者精彩的文章。
verymrq的博客地址:
https://me.csdn.net/verymrq
/ AOP /
面向方面编程。
如果OOP把问题分成单个模块,AOP统一管理某一类涉及多个模块的问题。比如登录、传输、大文件上传三个模块。现在,有必要添加性能测试功能来计算这三个模块各花费多少时间。
OOP思想是设计一个性能检测模块,提供这三个模块调用的接口。这样,每个模块都应该调用性能测试模块的接口。如果改变接口,需要在每次调用这三个模块的地方进行修改。
AOP的思想是在特定的切入点挂接这些独立的模块,在不影响原有模块独立性的情况下,给模块增加共同的逻辑。
所以,这就是我上面说的:统一管理某一类涉及多个模块的问题。
安卓AOP三剑客:APT,AspectJ,Javassist。
APT应用:Dagger,butterKnife,组件化方案等等AspectJ:主要用于性能监控,日志埋点等Javassist:热更新(可以在编译后,打包Dex之前干事情,可以突破一下限制)/ AspectJ /
AspectJ是今天的主角,主要用在不想侵犯原代码的场景中。比如SDK需要在不入侵的情况下向主机中插入一些代码,进行日志记录、性能监控、动态权限控制,甚至代码调试。
访问说明
首先,您需要在项目根目录的build.gradle中添加依赖项:
构建{
仓库{
共享
}
依赖项{
class path ' com . Android . tools . build:gradle:2 . 3 . 0-beta 2 '
类路径' com . Hu Jiang . aspectjx:gradle-Android-plugin-aspectjx:2 . 0 . 6 '
}
}
然后将AspectJ依赖项添加到主项目或库的build.gradle中:
compile ' org . AspectJ:AspectJ rt:1 . 8 . 9 '
同时,添加AspectJX模块进行build.gradle:
应用插件:' android-aspectjx '
这样就配置好了Android Studio中AspectJ的环境。如果在编译过程中遇到“无法确定缺失类型xxxxx的超类”等错误,请参考项目自述文件中excludeJarFilter的使用。
aspectjx {
//包含您想要编制的libs
包括图像过滤器'通用图像加载器',' AspectJX-演示/库'
//不包括您不想编制的lib
排除图像过滤器'通用图像加载器'
}
基本用途
直接看代码,在活动:
@覆盖
protected void oncreate(@ Nullable Bundle savedInstanceState){
super . OnCreate(SaveDinstancestate);
testMethod
}
privatevoidtestMethod{
Log.e(DemoAspect。标签,“TestMethod-invoke”);
}
创建新的DemoAspect类:
@方面
publicclassDemoAspect{
publicationstatifinString TAG = " DeMoaspect ";
@ Before(" execution(* com . Hu Jiang . library . demo . demo activity . test *(..))")
publicvoidtestAspectBefore(连接点){
Log.e(TAG,point . GetSignature . GetName+"-before ");
}
}
运行时打印:
test method-before com . hujiang . library . demo E/demo aspect:test method-invoke
为了完成插入操作,我们只需要
类上加入注释@Aspect方法上加入注释@BeforeBefore里写入要插入的相关信息很简单吗?下面详细解释一下。
建议
也就是说,要插入的代码是如何插入的,就是方法上的注释。
前后都很好理解,上面的例子已经说的很清楚了。
返回后
适用于需要获取返回值的情况,如:
privateintAfterReturningTest {
Log.e(DemoAspect。标签,“after returning-invoke”);
返回10;
}
@ after returning(value = " execution(* com . hujiang . library . demo . demo activity . after returning *(..))",返回= "num ")
public void testaspectafterreturning(int num){
Log.e(TAG," after returning-num:"+num);
}
这样就可以在cut方法中得到返回值。值得注意的是,方法参数必须与注释中的值一致。
【returning = "num"】===【intnum】
事后
适用于收集和监控异常信息。
privatevoidAfterThrowingTest {
视图v = null
设置可见性(视图。VISIble);
}
@ AFTERROWwing(value = " execution(* com . Hu Jiang . library . demo . demo activity . AFTERROWwing *(..))”,抛出=“异常”)
publicationvoutestaspectafterreturning(异常异常){
Log.e(TAG," after hrowing-exception:"+exception . getmessage);
}
同样,参数和注释中的值必须一致。崩溃也会在这里发生,不会因为切片操作而直接被捕获,只会在抛出异常之前打印异常信息。
围绕
可以在方法执行前后调用,更加灵活。
privatevoidAroundTest{
Log.e(DemoAspect。标签,“around test-invoke”);
}
@ about(" execution(* com . hujiang . library . demo . demo activity . Around test(..))")
public void testaspectround(proceedingjoint point)throws throwable {
Log.e(TAG,point . GetSignature . GetName+"-before ");
继续;
Log.e(TAG,point . GetSignature . GetName+"-after ");
}
原方法通过执行ProceedingJoinPoint的Proceeding方法调用,控件灵活。如果你愿意,可以不调用就截取。
切入点
告诉代码注入工具在哪里注入特定代码段的表达式。也就是例子中的这句话:
@ Before(" execution(* com . Hu Jiang . library . demo . demo activity . test *(..))")
我们分成几个部分依次来看:
@ before: advice,也就是具体的插入点,我们已经介绍过了
执行:处理连接点的类型,如调用、执行和代码内
其中,调用和执行是类似的,也就是插入代码。不同的是执行是在切入方法中,调用是在调用切入方法之前或者之后。
//通话:
呼叫(之前)
切入点{
切入点方法
}
呼叫(之后)
//执行:
切入点{
执行(之前)
切入点方法
执行(之后)
}
Withcode通常用于过滤一些切入条件,以实现更精确的切入控制,例如:
@覆盖
受保护的无效创建(捆绑保存的实例){
super . OnCreate(SaveDinstancestate);
setContentView(r . layout . activity _ main);
test1
test2
}
publicvoidtest{
Log.e("丘云飞"," test ");
}
publicvoidtest1{
测试;
}
publicvoidtest2{
测试;
}
如果我们想剪切测试方法,但只想在test2中调用test来执行剪切方法,我们需要使用withincode。
//在测试方法中
@ Pointcut(" within code(* com . Hu Jiang . library . aspect . main activity . test2(..))")
publicvoidinvoke2{
}
//调用测试方法时
@ Pointcut(" call(* com . Hu Jiang . library . aspect . main activity . test(..))")
publicvoidinvoke{
}
//同时满足前面的条件,即在切入之前在test2方法中调用测试方法
@切入点(“调用& amp& ampinvoke2”)
publicationvoinvokeaonly 2 {
}
@ Before(" invoke only 2 ")
publicationvotybeforeivokeonly 2(连接点连接点){
string key = join point . GetSignature . tostring;
Log.d(TAG," BeforeInvokOnly 2:"+key);
}
MethodPattern:这是最重要的表达式,大概:@ comment和access right,返回值类型和包名。函数名(参数)
@注释和访问权限(public/private/protect和static/final):它们是可选的。如果没有设置,默认情况下都是选中的。以访问权限为例。如果未将访问权限设置为条件,将搜索公共、私有、保护、静态和最终功能。
返回值类型:是普通函数的返回值类型。如果类型不受限制,则用*通配符表示。
包名。函数名:用于查找匹配的函数。可以使用通配符,包括and …和+。其中该符号用于匹配除。,而...表示任何包,符号+表示子类。
* com . Hu Jiang . library . demo . demo activity . test *(..)
第一部分:“”表示返回值,“”表示返回值为任意类型。
第二部分:是典型的包名路径,可以包含""进行一般分配,几个" "没有区别。在「时间」,你可以使用「&:& amp;、||、! "结合各种条件。类似于[test*]的写法,是指任何方法名称以test开头的方法。
第三部分:表示这个方法的参数,可以指定类型,比如android.os.Bundle,或者(…)表示任意类型任意个数的参数,也可以混合编写(android.os.Bundle,…)表示第一个参数为Bundle,以下可选。
自定义切入点:有时候需要指定哪些方法需要AOP操作,目标明确,也可以通过标注来完成。先声明评论:
@Retention(保留策略。班级)
@Target({ElementType。方法,元素类型。类型})
public @ InterfaceSpectannotation {
}
然后在切片类中定义它:
//使用这个注释定义一个切入点
@ Pointcut(" execution(@ com . Hu Jiang . library . aspect . aspectannotation * *(..))")
publicationvotyaspectannotation {
}
@Before("AspectAnnotation ")
publicvoidtestAspectAnnotation(连接点){
Log.e(TAG,point . getsignature . getname+"-Before ");
}
//用于活动
@覆盖
protected void oncreate(@ Nullable Bundle savedInstanceState){
super . OnCreate(SaveDinstancestate);
注释测试;
}
@ AspectannoStation
privatevoidAnnotationTest{
Log.e(DemoAspect。TAG," AnnotationTest-invoke ");
}
很好用,MethodPattern之前也有介绍,所以这里不能省略@这个注释。
/AspectJ实战/
实施登录检查操作
很多应用都有这个要求,操作前提醒用户注册登录,跳转到注册或登录界面,如果用AspectJ实现的话,非常简洁无创。
privateStaticFinaliString TAG = " AspectCommonTool ";
@ Pointcut(" execution(@ XXX . AspectJ . annotation . NeedLogon * *(..))")
publicvoidneedLoginMethod{
}
/**
*插入@ NeedLogin方法
*如果在非活动中使用@ NeedLogin,则必须将参数作为跳转开始页传递到上下文中
*/
@周围(“需求方法”)
public void connected loginaround(proceedingjointpoint proceedingjointpoint)throws可浏览{
mContext = null
///proceedingjointpoint . getthis可以获取调用此方法的对象
if(proceedingjointpoint . getthiinstancefcontext){
mccontext =(Context)proceedingjointpoint . getthis;
} else{
///proceedingjointpoint . getargs可以获取方法的所有参数
for(对象上下文:proceedingjointpoint . getargs){
if(上下文实例上下文){
mContext =(Context)Context;
打破;
}
}
}
if(McContext = = null){
返回;
}
if(LoginUtils.isLogin) {
/**
*如果用户登录,执行原始方法
*/
proceedingJoinPoint.proceed .继续;
} else{
/**
*不登录跳转到登录注册主界面
*/
}
使用方便,无创,后期维护容易。可以实现类似的思路:查看网络状态、查看权限状态、避免多次点击按钮、自动完成缓存等。
性能监控
其实AspectJ在Android中的应用主要集中在性能监控、日志嵌入等方面。下面是一个简单的例子:
我们监控布局加载时间,判断布局是否嵌套过多或复制过多,导致活动开始堵塞。首先,我们知道活动通过设置内容视图方法加载布局:
布局解析过程,IO过程创建View为反射过程这两个步骤都是耗时的操作,所以我们需要监控setContentView。
@ about(" execution(* Android . app . Activity . SetContentView(..))")
publicationgetcontentviewtime(proceedingjoint point)throws throwable {
string name = point . GetSignature . ToshortString;
long time = system . CurrentiMemillis;
继续;
Log.e(TAG,name+" cost:"+(System . CurrentiMemillis-time));
}
//活动
publicClassDemoactivityExtendsAppCompactivity {
@覆盖
protected void oncreate(@ Nullable Bundle savedInstanceState){
super . OnCreate(SaveDinstancestate);
setContentView(r . layout . activity _ main);
}
}
按如下方式打印日志:
demo aspect:AppCompactivity . SetContentView(..)成本:76
在日常开发中,我们可以将时间上传到服务器,收集用户信息,找到Caton Activity,进行相应的优化。
当然,这是一个非常简单的实现,您也可以在实际开发中监控您想要监控的各种位置。
1.《returning 性能优化,还得看AspectJ》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《returning 性能优化,还得看AspectJ》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/shehui/1085691.html