Android(安卓)AOP之AspectJ入门
16lz
2021-01-26
本文主要介绍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‘ }
AspectJ Marven库
- 配置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.Mainproject.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识别@Aspectpublic 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
- 切换到子线程的实现
package com.example.hi.aspectjdemo.anotations;import ... @Aspectpublic 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() { @Override public void subscribe(ObservableEmitter emitter) throws Exception { try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } } }).subscribeOn(Schedulers.newThread()).subscribe(); }}
- 打印返回值的实现
package com.example.hi.aspectjdemo.anotations;import...@Aspectpublic 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; }}
结果:
线程切换.PNG
参数打印.png
如上图所示,我们实现了切面编程,实现了一个注解实现子、主线程切换和参数及执行结果的打印
几个坑
- 定义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> map=new HashMap<>(); public static void addNewDisposable(Activity activity,Disposable disposable){ Set disposables = map.get(activity); if(disposables==null){ disposables=new HashSet<>(); map.put(activity,disposables); } disposables.add(disposable); } public static void dispose(Activity activity){ Set disposables = map.get(activity); if(disposables==null){ return; } Iterator 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() { @Override public void subscribe(ObservableEmitter 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); } ...
新手入门,难免有疏漏错误,欢迎指正!
更多相关文章
- Android(安卓)DataBinding使用详解(一)
- android 【点击输入框调出输入法前的】输入框获取焦点和输入法的
- 使用 SQLiteDatabase 操作 SQLite 数据库
- Android(安卓)studio 中与本地 html 页面交互
- Android(安卓)开发者必知必会的权限管理知识
- 真机上使用Hierarchy Viewer
- Android(安卓)实现音乐播放器【源码+注释】— MediaPlayer
- Android(安卓)studio 命令gradlew assembleRelease打包时,出现 Un
- android关闭应用程序