前言
AOP
,它不是一門新語言,是一種面向切面的思想。它主要的作用是把一些具有相同屬性或者相同功能的代碼抽離出來形成一個切面,從而實現面向切面編程!而AspectJ
就是基於Java
語言實現AOP
這種思想的一個框架。
Java之安裝AspectJ
AspectJ官網下載Jar
包,然後在下載目錄執行下面的命令安裝
java -jar aspectj-1.x.x.jar
安裝完後如下圖
- bin對應的是編譯命令,常用ajc.bat
- doc 放的是一些文檔
- lib裏面放的是一些
AspectJ
的一些庫。
知道大概得結構後,再來看看Android
的使用方法。
Android之集成AspectJ
project之build.gradle
dependencies {
classpath group:'org.aspectj',name:'aspectjtools',version:'1.9.1'
}
app之build.gradle
import org.aspectj.tools.ajc.Main
//配置aspectJ
android.applicationVariants.all{
//編譯Java代碼的任務
JavaCompile javaCompile = it.javaCompile
javaCompile.doLast {
println "在編譯之後執行"
//執行 aspectJ 修改字節碼的操作
String[] args = [
"-1.7",
"-inpath",javaCompile.destinationDir.toString(),
"-d",javaCompile.destinationDir.toString(),
"-aspectpath",javaCompile.classpath.asPath,
"-classpath",javaCompile.classpath.asPath,
"-bootclasspath",project.android.bootClasspath.join(File.pathSeparator)
]
new Main().runMain(args,false)
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation group:'aspectj',name:'aspectjrt',version:'1.5.4'
}
一個添加事務的例子
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="添加事務"
android:onClick="hello"/>
public void hello(View view){
Log.d(TAG,"hello aspectJ");
}
這是一個按鈕點擊事件,下面通過AOP對它進行添加事務
第一步
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Transaction {
}
第二步
@Aspect
public class TransactionAspect {
private static final String TAG = "TransactionAspect";
private static final String TRANSACTION_METHOD =
"execution(@com.goach.myaspectj.annotation.Transaction * *(..))";
@Pointcut(TRANSACTION_METHOD)
public void transactionMethod(){}
@Around("transactionMethod()")
public void addTransaction(ProceedingJoinPoint joinPoint){
Log.d(TAG,"開始事務....");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Log.d(TAG,"結束事務....");
}
}
第三步
@Transaction//加上註解
public void hello(View view){
Log.d(TAG,"hello aspectJ");
}
最後執行結果
這裏只要添加註解,沒動hello
裏面的一行代碼就添加了事務。這就是一個簡單的AOP編程
在上面引出了個@Aspect,Join Points,@Pointcut, @Around
這樣的概念,
- @Aspect 修飾一個類,這樣就會把這個類當一個Bean
來處理
- Join Points
程序運行執行點,比如上面的execution
就是其中的一種類型,還有Call
類型等
- Pointcut
選出我們需要的Join Points
,如
@Pointcut("execution(@com.goach.myaspectj.annotation.Transaction * *(..))")
execution
指的是類型,com.goach.myaspectj.annotation.Transaction
指的是包名+對應的註解名,第一個* 指的是返回值爲任意類型,第二個*指的是方法名爲任意類型或者是構造器的話使用,new
代替,(..)指的是任意類型的參數。
Around
advice
的類型之一,還有before
和after
,而Around
會替代原來的JPoint
,如果需要執行原來的JPoint
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
還有一些其他用法:
- 獲取方法的註解
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
Permission permission = method.getAnnotation(Permission.class);
- 獲取當前得上下文
(Context) joinPoint.getTarget()
- 結合RXJava做線程切換
implementation "io.reactivex.rxjava2:rxandroid:2.0.2"
同上面的定義方法,先定義一個異步註解
@Retention(RetentionPolicy.CLASS)
public @interface Async {
}
和一個同步註解
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Main {
}
再實現異步切面
@Aspect
public class AsyncAspect {
/**
* Around : doAync 替換,原本被 Async 聲明的方法
*/
@Around("execution(@com.goach.permissions.annotation.Async void *(..))")
public void doAsync(final ProceedingJoinPoint joinPoint){
//切換線程
Completable.create(new CompletableOnSubscribe() {
@Override
public void subscribe(CompletableEmitter emitter) throws Exception {
//子線程
//執行原來的方法
try{
joinPoint.proceed();
}catch (Throwable throwable){
throwable.printStackTrace();
}
}
}).subscribeOn(Schedulers.io()).subscribe();
}
}
主線程的切面
@Aspect
public class MainAspect {
@Around("execution(@com.goach.permissions.annotation.Main void * (..))")
public void doMain(final ProceedingJoinPoint joinPoint){
//保證在主線程
if(Looper.myLooper()==Looper.getMainLooper()){
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return;
}
//如果不在 切換到主線程
Completable.create(new CompletableOnSubscribe() {
@Override
public void subscribe(CompletableEmitter emitter) throws Exception {
try{
joinPoint.proceed();
}catch (Throwable throwable){
throwable.printStackTrace();
}
}
}).subscribeOn(AndroidSchedulers.mainThread()).subscribe();
}
}
最後測試使用
@Async
public void readFile(View view){
Log.e("Main","讀取文件:"+Thread.currentThread().toString());
try {
Thread.sleep(3000);
}catch (InterruptedException e){
e.printStackTrace();
}
showRestult();
}
@Async
public void writeFile(View view){
Log.e("Main","寫入文件:"+Thread.currentThread().toString());
showRestult();
}
@Main
public void showRestult(){
Toast.makeText(this,"操作成功", Toast.LENGTH_SHORT).show();
}
這樣就是一個線程切換的例子了。其實AspectJ
還可以做動態權限處理,埋點統計等等之類的。