Android AOP之AspectJ入門

本文主要介紹AspectJ入門,實(shí)現(xiàn)Android簡單的AOP編程,完成線程切換和Log日志的輸出。
另外AspectJ還可以實(shí)現(xiàn)方法的執(zhí)行時(shí)間,日志的收集記錄,登陸校驗(yàn)等功能。入門之后這些都不難。

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'
}
    • 調(diào)用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,
                //覆蓋引導(dǎo)類的位置,
                "-bootclasspath",project.android.bootClasspath.join(File.separator)
        ]
        //調(diào)用執(zhí)行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 {
}
  • 打印方法的參數(shù)與返回值的注解LogPR
package com.example.hi.aspectjdemo.anotations;
import ...
/**
 * 打印方法中的參數(shù)與返回值
 */
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface LogPR {
}

注解的調(diào)用

  • 切換到主線程的方法的實(shí)現(xiàn)
package com.example.hi.aspectjdemo.anotations;
import ...
//類上添加注解@Aspect,以便AspectJ識(shí)別
@Aspect
public class MThreadImpl {
    //切點(diǎn)方法 注意Pointcut的寫法: 執(zhí)行被MThread注解的方法,方法的返回值為void,*表示方法名任意,(..)表示參數(shù)任意
    @Pointcut("execution(@com.example.hi.aspectjdemo.anotations.MThread void *(..))")
    public void mainThreadMethod() {//方法的名稱自定義
    }
    //Around為包裹方法,另外還有Before及After,參數(shù)為自己前面定義的方法名
    @Around("mainThreadMethod()")
    public void execute(final ProceedingJoinPoint joinPoint) {//方法的名稱可自定義,返回值與切點(diǎn)方法的返回值應(yīng)一致
        Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
                try {
                    joinPoint.proceed();//執(zhí)行方法
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
            }
        }).subscribeOn(AndroidSchedulers.mainThread()).subscribe();
    }
}
  • 切換到子線程的實(shí)現(xiàn)
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();
    }
}
  • 打印返回值的實(shí)現(xiàn)
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;
    }
}

注解的引用

寫一個(gè)Acitivity,在其中調(diào)用一個(gè)方法mSleep,要求切換到子線程,線程睡眠一段時(shí)間,睡眠結(jié)束后,執(zhí)行showToast切換到主線程,彈出Toast。調(diào)用另一個(gè)方法getResult,傳遞2個(gè)參數(shù),打印出2個(gè)參數(shù)和執(zhí)行的結(jié)果

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, "線程結(jié)束: "+e.getMessage());
        }
        showToast();
    }

    @MThread
    private void showToast() {
        Log.i(TAG, "showToast: " + Thread.currentThread().getName());
        Toast.makeText(this, "來自主線程的消息,子線程睡眠結(jié)束", Toast.LENGTH_SHORT).show();
    }

    @LogPR
    private int getResult(int i, int j) {
        Log.i(TAG, "getResult: " + Thread.currentThread().getName());
        return i + j;
    }
}

結(jié)果:


線程切換.PNG

參數(shù)打印.png

如上圖所示,我們實(shí)現(xiàn)了切面編程,實(shí)現(xiàn)了一個(gè)注解實(shí)現(xiàn)子、主線程切換和參數(shù)及執(zhí)行結(jié)果的打印

幾個(gè)坑

  • 定義Pointcut及執(zhí)行方法
    如果你真實(shí)的方法有返回值,在定義Pointcut的返回值時(shí)一定不要void,@Around注解的執(zhí)行方法也不能返回void,否則不能正確的生成需要的class文件,導(dǎo)致執(zhí)行結(jié)果與預(yù)期不符合。
    例如前面例子中 LogPRImplexecute 方法,如果返回void,那么就無法正確執(zhí)行了!
  • 關(guān)于內(nèi)存泄漏
    由于涉及到線程的切換,所以當(dāng)Activity結(jié)束時(shí),子線程可能會(huì)導(dǎo)致內(nèi)存泄漏,所以要想辦法拿到線程,當(dāng)Activity銷毀時(shí),不需要的線程也要結(jié)束。
  • 關(guān)于切換線程執(zhí)行方法的返回值
    目前還沒有找到優(yōu)雅的方法。

簡單處理Activity銷毀時(shí)使子線程結(jié)束

定義一個(gè)DisposableHandler,把*Impl中的Disposable存儲(chǔ)起來,當(dāng)Activity結(jié)束時(shí),調(diào)用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中調(diào)用addNewDisposable

    ...
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    @Around("subThreadMethod()")
    public void execute(final ProceedingJoinPoint joinPoint) {
        Object target = joinPoint.getTarget();//目標(biāo)類,如果時(shí)Activity,則獲取到原始方法所在的Activity實(shí)例
        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);
        }
    }
    ...

當(dāng)Activity銷毀時(shí)

    ...
    @Override
    protected void onDestroy() {
        super.onDestroy();
        DisposableHandler.dispose(this);
    }
    ...

新手入門,難免有疏漏錯(cuò)誤,歡迎指正!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容