版權聲明:本文爲博主原創文章,未經博主容許不得轉載。html
轉載請代表出處:http://www.cnblogs.com/cavalier-/p/8888459.htmljava
AOP是 Aspect Oriented Programming 的縮寫,即面向切面編程,和日常遇到的面向對象OOP編程不同的是,OOP是將功能模塊化對象化,AOP是針對同一類的問題統一化處理。例如作日誌埋點,性能監控,動態權限控制等。android
AspectJ其實是對AOP編程的實踐,目前還有不少的AOP實現,如ASMDex,但筆者選用的是AspectJ。git
若是使用原生AspectJ在項目中配置會很是麻煩,在GitHub上有個開源的SDK gradle_plugin_android_aspectjx基於gradle配置便可。github
請自行查看開源項目中的接入配置過程編程
Join Points在AspectJ中是關鍵的概念。Join Points能夠看作是程序運行時的一個執行點,好比:一個函數的調用能夠看作是個Join Points,至關於代碼切入點。但在AspectJ中,只有下面幾種執行點是認爲是Join Points:app
Join Points | 說明 | 實例 |
---|---|---|
method call | 函數調用 | 好比調用Log.e(),這是一個個Join Point |
method execution | 函數執行 | 好比Log.e()的執行內部,是一處Join Points。注意這裏是函數內部 |
constructor call | 構造函數調用 | 和method call 相似 |
constructor execution | 構造函數執行 | 和method execution 相似 |
field get | 獲取某個變量 | 好比讀取DemoActivity.debug成員 |
field set | 設置某個變量 | 好比設置DemoActivity.debug成員 |
pre-initialization | Object在構造函數中作的一些工做。 | - |
initialization | Object在構造函數中作的工做。 | - |
static initialization | 類初始化 | 好比類的static{} |
handler | 異常處理 | 好比try catch 中,對應catch內的執行 |
advice execution | 這個是AspectJ 的內容 | - |
一個程序會有多個Join Points,即便同一個函數,也還分爲call 和 execution 類型的Join Points,但並非全部的Join Points 都是咱們關心的,Pointcuts 就是提供一種使得開發者可以值選擇所需的JoinPoints的方法。ide
Advice就是咱們插入的代碼能夠以何種方式插入,有Before 還有 After、Around。
下面看個例子:模塊化
@Before(「execution(* android.app.Activity.on**(..)))」) public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable{ }
這裏會分紅好幾個部分,咱們依次來看:函數
(* android.app.Activity.on**(..))
: 這個是最重要的表達式,第一個*
表示返回值,*
表示返回值爲任意類型,後面這個就是典型的包名路徑,其中能夠包含 *
來進行通配,幾個 *
沒有區別。同時這裏能夠經過&&、||、!
來進行條件組合。()表明這個方法的參數,你能夠指定類型,例如android.os.Bundle,或者 (..) 這樣來表明任意類型、任意個數的參數。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()表明執行原始的方法,在這以前、以後,均可以進行各類邏輯處理。
自定義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"); } }
最後建立切入代碼
@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); }
在AspectJ的切入點表達式中,咱們前面都是使用的execution,實際上還有一種類型—call,那麼這兩種語法有什麼區別呢?對call來講:
Call (Before) Pointcut{ Pointcut Method } Call (After)
對Execution來講:
Pointcut{ execution (Before) Pointcut Method execution (After) }
這個語法一般來進行一些切入點條件的過濾,做更加精確的切入控制,以下:
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()都調用了aspectJTest方法,但只想在aspectJ2調用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()是最經常使用的切點函數,其語法以下所示:
例以下面這段語法:
@Around(「execution(* *..MainActivity+.on*(..))")
整個表達式能夠分爲五個部分:
*
號表明返回類型,*號表明全部的類型。*(..)
最後這個星號表示方法名,+.表明具體的函數名,*
號通配符,包括括弧號裏面表示方法的參數,兩個dot表明任意參數。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