欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在Android項目中使用AspectJ的方法

 更新時間:2018年04月20日 10:21:34   作者:Cavalier-  
這篇文章主要介紹了在Android項目中使用AspectJ的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

什么是AOP

AOP是 Aspect Oriented Programming 的縮寫,即面向切面編程,和平常遇到的面向?qū)ο驩OP編程不一樣的是,OOP是將功能模塊化對象化,AOP是針對同一類的問題統(tǒng)一化處理。例如做日志埋點,性能監(jiān)控,動態(tài)權(quán)限控制等。

AspectJ

AspectJ實際上是對AOP編程的實踐,目前還有很多的AOP實現(xiàn),如ASMDex,但筆者選用的是AspectJ。

在Android項目中使用AspectJ

如果使用原生AspectJ在項目中配置會非常麻煩,在GitHub上有個開源的SDK gradle_plugin_android_aspectjx基于gradle配置即可。

接入說明

請自行查看開源項目中的接入配置過程

AspectJ 之 Join Points介紹

Join Points在AspectJ中是關(guān)鍵的概念。Join Points可以看做是程序運行時的一個執(zhí)行點,比如:一個函數(shù)的調(diào)用可以看做是個Join Points,相當于代碼切入點。但在AspectJ中,只有下面幾種執(zhí)行點是認為是Join Points:

Join Points 說明 實例
method call 函數(shù)調(diào)用 比如調(diào)用Log.e(),這是一個個Join Point
method execution 函數(shù)執(zhí)行 比如Log.e()的執(zhí)行內(nèi)部,是一處Join Points。注意這里是函數(shù)內(nèi)部
constructor call 構(gòu)造函數(shù)調(diào)用 和method call 類似
constructor execution 構(gòu)造函數(shù)執(zhí)行 和method execution 類似
field get 獲取某個變量 比如讀取DemoActivity.debug成員
field set 設(shè)置某個變量 比如設(shè)置DemoActivity.debug成員
pre-initialization Object在構(gòu)造函數(shù)中做的一些工作。 -
initialization Object在構(gòu)造函數(shù)中做的工作。 -
static initialization 類初始化 比如類的static{}
handler 異常處理 比如try catch 中,對應(yīng)catch內(nèi)的執(zhí)行
advice execution 這個是AspectJ 的內(nèi)容 -

Pointcuts 介紹

一個程序會有多個Join Points,即使同一個函數(shù),也還分為call 和 execution 類型的Join Points,但并不是所有的Join Points 都是我們關(guān)心的,Pointcuts 就是提供一種使得開發(fā)者能夠值選擇所需的JoinPoints的方法。

Advice

Advice就是我們插入的代碼可以以何種方式插入,有Before 還有 After、Around。
下面看個例子:

@Before(“execution(* android.app.Activity.on**(..)))”)
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable{
}

這里會分成好幾個部分,我們依次來看:

  1. @Before: Advice, 也就是具體的插入點
  2. execution:處理Join Point的類型,例如call、execution
  3. (* android.app.Activity.on**(..)): 這個是最重要的表達式,第一個*表示返回值,*表示返回值為任意類型,后面這個就是典型的包名路徑,其中可以包含 *來進行通配,幾個 *沒有區(qū)別。同時這里可以通過&&、||、!來進行條件組合。()代表這個方法的參數(shù),你可以指定類型,例如android.os.Bundle,或者 (..) 這樣來代表任意類型、任意個數(shù)的參數(shù)。
  4. public void onActivityMehodBefore: 實際切入的代碼。

Before 和 After 其實還是很好理解的,也就是在Pointcuts之前和之后,插入代碼,那么Android呢,從字面含義上來講,也就是在方法前后各插入代碼,他包含了 Before和 After 的全部功能,代碼如下:

@(“execution(* com.xys.aspectjxdemo.MainActivity.testAOP()))”)
public void onActivityMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
String key = proceedingJoinPoint.getSignature().toString();
Log.d(TAG,”onActivityMethodAroundFirst:”+key);
proceedingJoinPoint.proceed();
Log.d(TAG,”onActivityMethodAroundSecond:”+key);
}

以上代碼中,proceedingJoinPoint.proceed()代表執(zhí)行原始的方法,在這之前、之后,都可以進行各種邏輯處理。

自定義Pointcuts

自定義Pointcuts可以讓我們更加精準的切入一個或多個指定的切入點。

首先我們要定義一個注解類

@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface DebugTrace {
}

在需要插入代碼的地方加入這個注解,例如在MainActivity中加入:

public class MainActivity extends AppCompatActivity{
final String TAG = MainActivity.class.getSimpleName();

@Override
protedcted void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
logTest();
}

@DebugTrace
public void logTest(){
Log.e(TAG,”log test");
}
}

最后創(chuàng)建切入代碼

@Pointcut(“execution(@com.kun.aspectjtest.aspect.DebugTrace * *..*.*(..))”)
public void DebugTraceMethod(){}

@Before(“DebugTraceMethod()”)
public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable{
String key = joinPoint.getSignature().toString();
Log.e(TAG, “beforeDebugTraceMethod:”+key);
}

Call

在AspectJ的切入點表達式中,我們前面都是使用的execution,實際上還有一種類型—call,那么這兩種語法有什么區(qū)別呢?對call來說:

Call (Before)
Pointcut{
Pointcut Method
}
Call (After)

對Execution來說:

Pointcut{
execution (Before)
Pointcut Method
execution (After)
}

Withincode

這個語法通常來進行一些切入點條件的過濾,作更加精確的切入控制,如下:

public class MainActivity extends AppCompatActivity{
final String TAG = MainActivity.class.getSimpleName();

@Orveride
protected void onCreate(Bundle savedInstanceState){
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
aspectJ1();
aspectJ2();
aspectJ3();
}
public void aspectJTest(){
Log.e(TAG,”execute aspectJTest");
}

public void aspectJ1(){
aspectJTest();
}
public void aspectJ2(){
aspectJTest();

}
public void aspectJ3(){
aspectJTest();
}
}

aspectJ1(),aspectJ2(),aspectJ3()都調(diào)用了aspectJTest方法,但只想在aspectJ2調(diào)用aspectJTest時插入代碼,這個時候就需要使用到Pointcut和withcode組合的方式,來精確定位切入點。

@Pointcut(“(call(* *..aspectJTest()))&&withincode(* *..aspectJ2())”)
public void invokeAspectJTestInAspectJ2(){
}

@Before(“invokeAspectJTestInAspectJ2()”)
public void beforeInvokeaspectJTestInAspectJ2(JoinPoint joinPoint) throws Throwable{
Log.e(TAG,”method:”+getMethodName(joinPoint).getName());
}

private MethodSignature getMethodName(JoinPoint joinPoint){
if(joinPoint == null) return null;
return (MethodSignature) joinPoint.getSignature();
}

execution 語法

execution()是最常用的切點函數(shù),其語法如下所示:

例如下面這段語法:@Around(“execution(* *..MainActivity+.on*(..))")

整個表達式可以分為五個部分:
1.execution()是表達式主體
2.第一個*號代表返回類型,*號代表所有的類型。
3.包名 表示需要攔截的包名,這里使用*.代表匹配所有的包名。
4.第二個*號表示類名,后面跟.MainActivity是指具體的類名叫MainActivity。
5.*(..) 最后這個星號表示方法名,+.代表具體的函數(shù)名,*號通配符,包括括弧號里面表示方法的參數(shù),兩個dot代表任意參數(shù)。

遇到的錯誤

1.以下錯誤可以使用gradle2.2.3解決,由于目前還不適配gradle3.0導致的

Error:Execution failed for task ':app:transformClassesWithDexBuilderForDebug'.
> Unexpected scopes found in folder '/Users/ram/WorkSpace/AndroidWorkSpace/MyDemo/app/build/intermediates/transforms/AspectTransform/debug'. Required: PROJECT, SUB_PROJECTS, EXTERNAL_LIBRARIES. Found: EXTERNAL_LIBRARIES, PROJECT, PROJECT_LOCAL_DEPS, SUB_PROJECTS, SUB_PROJECTS_LOCAL_DEPS

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論