本文主要介绍AspectJ入门,实现Android简单的AOP编程,完成线程切换和Log日志的输出。
另外AspectJ还可以实现方法的执行时间,日志的收集记录,登陆校验等功能。入门之后这些都不难。
Gradle配置
- 配置Project Gradle
- 引入
aspectjtools
在Project Gradle中配置classpath
- 引入
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0-alpha17'
classpath 'org.aspectj:aspectjtools:1.9.1‘
}
- 配置Moudle gradle
- 添加依赖
dependencies {
//AspectJ
implementation 'org.aspectj:aspectjrt:1.9.1'
//rxandroid,用于切换线程
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
}
- 调用AspectJ编译器
import org.aspectj.tools.ajc.Main
project.android.applicationVariants.all {
JavaCompile compile = it.javaCompile
compile.doLast {
String[] args = [
//java版本
"-1.8",
//aspect处理的源文件
"-inpath", compile.destinationDir.toString(),
//aspect的输出目录
"-d", compile.destinationDir.toString(),
//aspect编译器的classpath
"-aspectpath", compile.classpath.asPath,
//java程序类的查找路径
"-classpath",compile.classpath.asPath,
//覆盖引导类的位置,
"-bootclasspath",project.android.bootClasspath.join(File.separator)
]
//调用执行aspectJ编译器
new Main().runMain(args,false)
}
}
代码
定义注解
- 切换到主线程的注解
MThread
package com.example.hi.aspectjdemo.anotations;
import ...
/**
* 切换到主线程
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface MThread {
}
- 切换到子线程的注解
SThread
package com.example.hi.aspectjdemo.anotations;
import ...
/**
* 切换到子线程
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface SThread {
}
- 打印方法的参数与返回值的注解
LogPR
package com.example.hi.aspectjdemo.anotations;
import ...
/**
* 打印方法中的参数与返回值
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface LogPR {
}
注解的调用
- 切换到主线程的方法的实现
package com.example.hi.aspectjdemo.anotations;
import ...
//类上添加注解@Aspect,以便AspectJ识别
@Aspect
public class MThreadImpl {
//切点方法 注意Pointcut的写法: 执行被MThread注解的方法,方法的返回值为void,*表示方法名任意,(..)表示参数任意
@Pointcut("execution(@com.example.hi.aspectjdemo.anotations.MThread void *(..))")
public void mainThreadMethod() {//方法的名称自定义
}
//Around为包裹方法,另外还有Before及After,参数为自己前面定义的方法名
@Around("mainThreadMethod()")
public void execute(final ProceedingJoinPoint joinPoint) {//方法的名称可自定义,返回值与切点方法的返回值应一致
Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
try {
joinPoint.proceed();//执行方法
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}).subscribeOn(AndroidSchedulers.mainThread()).subscribe();
}
}
- 切换到子线程的实现
package com.example.hi.aspectjdemo.anotations;
import ...
@Aspect
public class SThreadImpl {
@Pointcut("execution(@com.example.hi.aspectjdemo.anotations.SThread void *(..))")
public void subThreadMethod(){
}
@Around("subThreadMethod()")
public void execute(final ProceedingJoinPoint joinPoint){
Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}).subscribeOn(Schedulers.newThread()).subscribe();
}
}
- 打印返回值的实现
package com.example.hi.aspectjdemo.anotations;
import...
@Aspect
public class LogPRImpl {
@Pointcut("execution(@com.example.hi.aspectjdemo.anotations.LogPR * *(..))")
public void logMethod() {
}
@Around("logMethod()")
public Object execute(ProceedingJoinPoint joinPoint) {
Object result=null;
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
Object target = joinPoint.getTarget();
String arg = "";
for (int i = 0; i < args.length; i++) {
if (i == 0)
arg = args[i].toString();
else
arg = arg + "," + args[i].toString();
}
Log.i(methodName, "args: "+arg);
try {
result= joinPoint.proceed();
if (result != null)
Log.i("result:", "result=" + result.toString());
else
Log.i("result","没有返回值");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return result;
}
}
注解的引用
写一个Acitivity,在其中调用一个方法mSleep
,要求切换到子线程,线程睡眠一段时间,睡眠结束后,执行showToast
切换到主线程,弹出Toast。调用另一个方法getResult,传递2个参数,打印出2个参数和执行的结果
package com.example.hi.aspectjdemo;
import ...
public class AspectJDemoActivity extends AppCompatActivity {
private static final String TAG = "AspectJDemoActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSleep();
getResult(1,2);
}
@SThread
private void mSleep() {
Log.i(TAG, "mSleep: " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
Log.i(TAG, "mSleep--> finish: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
Log.i(TAG, "线程结束: "+e.getMessage());
}
showToast();
}
@MThread
private void showToast() {
Log.i(TAG, "showToast: " + Thread.currentThread().getName());
Toast.makeText(this, "来自主线程的消息,子线程睡眠结束", Toast.LENGTH_SHORT).show();
}
@LogPR
private int getResult(int i, int j) {
Log.i(TAG, "getResult: " + Thread.currentThread().getName());
return i + j;
}
}
结果:
如上图所示,我们实现了切面编程,实现了一个注解实现子、主线程切换和参数及执行结果的打印
几个坑
- 定义Pointcut及执行方法
如果你真实的方法有返回值,在定义Pointcut的返回值时一定不要void,@Around注解的执行方法也不能返回void,否则不能正确的生成需要的class文件,导致执行结果与预期不符合。
例如前面例子中LogPRImpl
的execute
方法,如果返回void,那么就无法正确执行了! - 关于内存泄漏
由于涉及到线程的切换,所以当Activity结束时,子线程可能会导致内存泄漏,所以要想办法拿到线程,当Activity销毁时,不需要的线程也要结束。 - 关于切换线程执行方法的返回值
目前还没有找到优雅的方法。
简单处理Activity销毁时使子线程结束
定义一个DisposableHandler
,把*Impl
中的Disposable
存储起来,当Activity
结束时,调用Disposable.dispose()
;
package com.example.hi.aspectjdemo;
import ...
public class DisposableHandler {
static HashMap<Activity,Set<Disposable>> map=new HashMap<>();
public static void addNewDisposable(Activity activity,Disposable disposable){
Set<Disposable> disposables = map.get(activity);
if(disposables==null){
disposables=new HashSet<>();
map.put(activity,disposables);
}
disposables.add(disposable);
}
public static void dispose(Activity activity){
Set<Disposable> disposables = map.get(activity);
if(disposables==null){
return;
}
Iterator<Disposable> iterator = disposables.iterator();
while (iterator.hasNext()){
Disposable next = iterator.next();
if(!next.isDisposed()){
next.dispose();
}
}
}
}
在Impl中调用addNewDisposable
...
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Around("subThreadMethod()")
public void execute(final ProceedingJoinPoint joinPoint) {
Object target = joinPoint.getTarget();//目标类,如果时Activity,则获取到原始方法所在的Activity实例
Disposable disposable = Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}).subscribeOn(Schedulers.newThread()).subscribe();
if (target instanceof Activity) {
//保存Disposable
DisposableHandler.addNewDisposable((Activity) target, disposable);
}
}
...
当Activity销毁时
...
@Override
protected void onDestroy() {
super.onDestroy();
DisposableHandler.dispose(this);
}
...
新手入门,难免有疏漏错误,欢迎指正!