主目錄見:Android高級進階知識(這是總目錄索引)
?AOP(Aspect-oriented programming)面向切面編程,如果你關注到這個概念的話,那你應該大概懂得他的作用了。最早接觸這個概念是在Spring框架中,如今android也越來越多地地方可以使用他了,其實他不是一門語言,他是一種編程思想。今天我們不講基礎的概念,如果你還不了解,這里推薦幾篇文章:
1.Android中的AOP編程;
2.Aspect Oriented Programming in Android這是前一篇的英文版本;
3.深入理解Android之AOP ;
4.同時這里推薦一個項目hugo (一個日志打印框架)。
一.目標
AOP的實現框架有好多,我們今天選擇一個AOP庫AspectJ來實現我們的功能,我們今天還是以我們的框架LRouter來講解。今天有如下目標:
1.了解AOP的思想概念;
2.懂得簡單地AOP庫AspectJ的使用。
二.使用講解
首先講解之前我來說一下為什么當初會用到這個,因為當初寫這個框架有個需求是:
因為框架是多組件跨進程的模塊分割的,然后我們想知道各個模塊之間的通訊頻率,還有哪個模塊被訪問的次數最多呢?這時候我們不能再對原有的框架做一個大的改動,像ARouter用的是CountDownLatch機制來做的攔截。但是我們這里明顯不適用,我們只能選擇一個侵入性比較小的辦法,那就是這里的AOP了,我們在跨進程請求的部分做了一個攔截,攔截了請求參數,請求的方法等等信息來做一個記錄。
1.gradle文件的配置
我們的AspectJ庫必須使用 AspectJ 的編譯器(ajc,一個java編譯器的擴展)對所有受 aspect 影響的類進行織入。所以我們需要一些配置,在hugo中這個配置是寫成gradle插件了。我們這里就直接添加到LRouter項目中的lrouter-api項目的gradle中:
具體配置內容在前面文章和項目里面有。
2.Aspect(切面)的選擇
我們知道,我們跨進程或者跨模塊的訪問是在lrouter-api模塊的LocalRouter類中的navigation()方法進行訪問的,所以我們可以攔截這個方法的訪問,我們選擇跟hugo同樣的做法,在這個方法上面添加一個注解@Navigation:
@Navigation
public ListenerFutureTask navigation(Context context, LRouterRequest request) throws Exception {
........
}
然后我們就可以來定義Pointcut切入點了。
//篩選出用Navigation注解的所有方法
private static final String POINTCUT_METHOD = "execution(@com.lenovohit.lrouter_api.intercept.ioc.Navigation * *(..))";
//篩選出所有用Navigation注解的所有構造函數
private static final String POINTCUT_CONSTRUCTOR = "execution(@com.lenovohit.lrouter_api.intercept.ioc.Navigation *.new(..))";
@Pointcut(POINTCUT_METHOD)
public void methodAnnotationWithNavigation(){}
@Pointcut(POINTCUT_CONSTRUCTOR)
public void constructorAnnotaionWithNavigation(){}
可以看到我們選擇了添加了Navigation注解的所有方法和構造函數作為切入點。后面我們就可以選擇合適的Advice了,我們這里選擇Around這個類型。
@Around("methodAnnotationWithNavigation() || constructorAnnotaionWithNavigation()")
public Object requestAndExecute(ProceedingJoinPoint joinPoint) throws Throwable{
//執行方法前
enterRequestMethod(joinPoint);
//方法執行
long startNanos = System.nanoTime();
Object result = joinPoint.proceed();
long stopNanos = System.nanoTime();
long lengthMillis = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos);
//執行方法后
exitRequestMethod(joinPoint,lengthMillis);
return result;
}
我們看到這里我們定義了Around類型的Advice,然后我們在方法里在攔截的方法前執行了enterRequestMethod(),還有在方法執行后調用了exitRequestMethod()方法,我們來看下:
public void enterRequestMethod(JoinPoint joinPoint){
CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
String methodName = codeSignature.getName();
String[] parameterNames = codeSignature.getParameterNames();
Object[] parameterValues = joinPoint.getArgs();
if (null != mAopInterceptors) {
for (AopInterceptor aopInterceptor : mAopInterceptors){
aopInterceptor.enterRequestIntercept(methodName, parameterNames, parameterValues);
}
}
}
我們看到這個方法里面通過JoinPoint獲取到了方法名,方法參數,參數值等信息,然后調用我們自己的方法進行處理。我們這里是用一個抽象方法來實現,意思就是說給用戶自己來處理:
public abstract class AopInterceptor {
/**
* 進入請求方法前
* */
public abstract void enterRequestIntercept(String methodName,String[] paramNames,Object[] paramValues);
/**
* 執行完請求方法后
* */
public abstract void exitRequestIntercept(String methodName,String[] paramNames,Object[] paramValues,long lengthMillis);
}
到這里我們的Aop使用例子也就結束了,步驟還是非常簡單的。這個思想還能干很多牛逼的事情,希望大家想不到解決方案的時候,這可以當做一個備選。
總結:今天的例子是非常簡單的,因為這個庫用起來本來就比較容易,但是思想還是非常棒的,如果大家有興趣希望進一步了解,看看我上面推薦的幾篇文章。