草庐IT

Android AOP你了解多少

搬砖的兔子 2023-03-28 原文

什么是AOP

AOP(Aspect Oriented Programming)意为面向切面编程,指通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

其广泛的应用在代码的后期修改与维护之中,它对原代码侵入性少,容易扩展辅助功能,可以使原执行逻辑与改变执行逻辑解耦。

举个例子.jpeg

现在有一个智能门,以前的开门的逻辑是:输入密码 -> 拎动把手 -> 开门,现在呢业主觉得密码有可能被盗,不够安全,希望加上指纹验证。这时候我们只需要将 验证指纹 这一步插入到 开门 之前就完成了,现在的逻辑是:输入密码 -> 拎动把手 -> 指纹验证 -> 开门。这种思维就是一种面向切面的思维。

思维导图.png

什么是AspectJ

要知道AOP只是一种编程思想,那么,在android中,我们该通过何种工具来实现这种思想呢,没错,就是AspectJ。要掌握AspectJ首先要明确下面的几大概念:

AspectJ.png

Advice(通知):定义需要被注入到.class字节码文件中的代码,通俗点儿来说就是告诉编织器哪里是你需要插入的代码。

  • @Pointcut :定义切点、标记方法以便于重用。
  • @Before :前置通知,其内容在切点之前执行。
  • @Around :环绕通知,其内容贯穿整个切点前后。
  • @After:后置通知,其内容在切点之后执行。
  • @AfterReturning:返回通知,其内容在切点返回结果后再执行。
  • @AfterThrowing:异常通知,其内容在切点抛出异常时执行。

JoinPoint(连接点):即允许你插入代码的地方。

Pointcut(切入点):是对连接点的筛选与定义的一种表达式。

  • 如下图所示,"execution(* android.view.View.OnClickListener.onClick(..))"是一个完整的切点表达式,它是由execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)组成的,其中<修饰符模式>和<异常模式>可以省略。
Pointcut.png

Aspect(切面):切面是通知和切入点的结合。

Weaving(织入):这个很好理解就是把我们定义好的切面注入到目标对象中去的过程。

基本用法

插件配置

//在根build.gradle下配置:
dependencies {
    ...
    classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8' 
}

//在app/build.gradle下配置
apply plugin: 'com.hujiang.android-aspectjx'

aspectjx {
    //关闭AspectJX功能
    enabled true
    //织入遍历符合条件的库
    //includeJarFilter 'universal-image-loader', 'AspectJX-Demo/library'
    //排除包含‘universal-image-loader’的库
    //excludeJarFilter 'universal-image-loader'
}

定义切面

创建一个类,并通过@Aspect定义为一个切面。

@Aspect
public class AspectTest {
    ......
}

创建通知、添加切点表达式

在该类中添加需要编织的方法,并通过通知和切点表达式来定义它。Ok,一个简单的防抖的OnClick判断就切入到你的程序中去了。相信其他业务你也能很好的利用AspectJ来处理了。

@Aspect
public class AspectTest {
    private static final String TAG = "AspectTest";
    @Around("execution(* android.view.View.OnClickListener.onClick(..))")
    public void onClickListener(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        Log.d(TAG, "onClick");
        if (!NoDoubleClickUtils.isDoubleClick()){
            proceedingJoinPoint.proceed();    //切回到切点并执行后续代码
        }
    }
}

补充:第三方库兼容

有时候我们在使用AspectJ的时候可能会遇到引入了一些其他的三方库的情况,而我们又需要对其内的连接点进行编织、切入。如上述点击事件防抖的例子,可能用到了Butterknife来注解@onClick(),这时你会发现上述切入表达式不起作用了,为什么呢,我们看一下Butterknife中的@OnClick定义就知道了。

@Target(METHOD)
@Retention(RUNTIME)
@ListenerClass(
    targetType = "android.view.View",
    setter = "setOnClickListener",
    type = "butterknife.internal.DebouncingOnClickListener",
    method = @ListenerMethod(
        name = "doClick",
        parameters = "android.view.View"
    )
)
public @interface OnClick {
  /** View IDs to which the method will be bound. */
  @IdRes int[] value() default { View.NO_ID };
}

我们可以发现。其通过Annotation Processor对@OnClick进行扫描,并将android.view.View.OnClickListener.onClick方法替换为butterknife.internal.DebouncingOnClickListener.doClick方法。因此为了兼容ButterKnife上述切入点表达式应该改为如下方式,及对butterknife的OnClick方法进行切入。

@Around("execution(* android.view.View.OnClickListener.onClick(..)) || execution(@butterknife.OnClick * *(..))")
public void onClickListener(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
    Log.d(TAG, "onClick");
    if (!NoDoubleClickUtils.isDoubleClick()){
        proceedingJoinPoint.proceed();
    }
}

有关Android AOP你了解多少的更多相关文章

  1. ruby - 可以通过多少种方法将方法添加到 ruby​​ 对象? - 2

    当谈到运行时自省(introspection)和动态代码生成时,我认为ruby​​没有任何竞争对手,可能除了一些lisp方言。前几天,我正在做一些代码练习来探索ruby​​的动态功能,我开始想知道如何向现有对象添加方法。以下是我能想到的3种方法:obj=Object.new#addamethoddirectlydefobj.new_method...end#addamethodindirectlywiththesingletonclassclass这只是冰山一角,因为我还没有探索instance_eval、module_eval和define_method的各种组合。是否有在线/离线资

  2. ruby - 使用 Ruby,计算 n x m 数组的每一列中有多少个 true 的简单方法是什么? - 2

    给定一个nxmbool数组:[[true,true,false],[false,true,true],[false,true,true]]有什么简单的方法可以返回“该列中有多少个true?”结果应该是[1,3,2] 最佳答案 使用转置得到一个数组,其中每个子数组代表一列,然后将每一列映射到其中的true数:arr.transpose.map{|subarr|subarr.count(true)}这是一个带有inject的版本,应该在1.8.6上运行,没有任何依赖:arr.transpose.map{|subarr|subarr.in

  3. ruby - 了解在 Ruby 中与 lambda 一起使用的 inject 行为 - 2

    我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject

  4. ruby-on-rails - 如何测试自己对 Ruby/ROR 的了解? - 2

    是否有self验证的问题列表。看着那个,我可以确定我知道。我应该复习一下。在学习的过程中,我列了一个这样的list,但它只包含我在某处听说过的项目。我需要一段时间才能找到新的东西。 最佳答案 以下是针对ruby​​和Rails的一些测试列表。证书名称:RubyonRails谁提供:oDeskIncorporation认证费用:免费网站:https://www.odesk.com/tests/985?pos=0证书名称:RubyonRails提供者:Techgig.com(TimesBusinessSolutionsLimited(T

  5. ruby-on-rails - 了解 "attribute_will_change!"方法 - 2

    我想覆盖store_accessor的getter。可以查到here.代码在这里:#Fileactiverecord/lib/active_record/store.rb,line74defstore_accessor(store_attribute,*keys)keys=keys.flatten_store_accessors_module.module_evaldokeys.eachdo|key|define_method("#{key}=")do|value|write_store_attribute(store_attribute,key,value)enddefine_met

  6. ruby - 我怎样才能更好地了解/了解更多关于 Ruby 的知识? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我最近开始学习Ruby,这是我的第一门编程语言。我对语法感到满意,并且我已经完成了许多只教授相同基础知识的教程。我已经写了一些小程序(包括我自己的数组排序方法,在有人告诉我谷歌“冒泡排序”之前我认为它非常聪明),但我觉得我需要尝试更大更难的东西来理解更多关于Ruby.关于如何执行此操作的任何想法?

  7. ruby-on-rails - 正确了解 Rails 框架的最佳方式是什么? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。我一直在Rails上做两个项目,它们运行良好,但在这个过程中重新发明了轮子,自来水(和热水)和止痛药,正如我随后了解到的那样,这些已经存在于框架中。那么基本上,正确了解框架中所有智能部分的最佳方法是什么,这将节省时间而不是自己构建已经实现的功能?从第1页开始阅读文档?是否有公开所有内容的特定示例应用程序?一个特定的开源项目?所有的rails交通?还是完全

  8. ruby - 了解 Ruby Enumerable#map(具有更复杂的 block ) - 2

    假设我有一个函数defodd_or_evennifn%2==0return:evenelsereturn:oddendend我有一个简单的可枚举数组simple=[1,2,3,4,5]然后我用我的函数在map中运行它,使用一个do-endblock:simple.mapdo|n|odd_or_even(n)end#=>[:odd,:even,:odd,:even,:odd]如果不首先定义函数,我怎么能做到这一点?例如,#doesnotworksimple.mapdo|n|ifn%2==0return:evenelsereturn:oddendend#Desiredresult:#=>[

  9. ruby - 了解 Ruby 中赋值和逻辑运算符的优先级 - 2

    在以下示例中,我无法理解Ruby运算符的优先级:x=1&&y=2由于&&的优先级高于=,我的理解是类似于+和*运算符:1+2*3+4解析为1+(2*3)+4它应该等于:x=(1&&y)=2但是,所有Ruby源代码(包括内部语法解析器Ripper)都将其解析为x=(1&&(y=2))为什么?编辑[08.01.2016]让我们关注一个子表达式:1&&y=2根据优先规则,我们应该尝试将其解析为:(1&&y)=2这没有意义,因为=需要特定的LHS(变量、常量、[]数组项等)。但是既然(1&&y)是一个正确的表达式,那么解析器应该如何处理呢?我试过咨询Ruby的parse.y,但它太像意大利面条

  10. ruby - 正则表达式 - 这个用于素数检测的正则表达式的复杂性是多少? - 2

    这行ruby​​代码检测素数(太棒了!)。("1"*n)!~/^1?$|^(11+?)\1+$/#wherenisapositiveinteger详细信息在这篇博文中解释http://www.noulakaz.net/weblog/2007/03/18/a-regular-expression-to-check-for-prime-numbers/我很好奇它在BIG-O表示法中的表现。有人帮忙吗? 最佳答案 根据经验数据,它似乎是O(n2)。我对前10000个质数中的每100个运行Ruby代码。以下是结果:蓝点是记录的时间,橙色线是

随机推荐