前言
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
还可以做动态权限处理,埋点统计等等之类的。