Android AOP面向切面設計程式設計

鋸齒流沙發表於2018-01-03

AOP介紹

AOP為Aspect Oriented Programming的縮寫,即面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點。

作用

利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的 耦合度降低,提高程式的可重用性,同時提高了開發的效率。

AOP基礎

要了解AOP,首先需要AOP的基礎,也就是其關鍵點:切點、切面、連線點等等

通知:注入到class檔案中的程式碼。

連線點:程式中可能作為程式碼注入目標的特定的點,例如一個方法呼叫或者方法入口。

切入點:告訴程式碼注入工具,在何處注入一段特定程式碼的表示式

切面:Pointcut 和 Advice 的組合看做切面。

織入: 注入程式碼(advices)到目標位置(joint points)的過程。

AOP在Android的應用

在Android中已經有一些庫和工具幫助我們簡單的使用AOP程式設計的了,如ASMDEX、AspectJ、DexMaker、Javassist for Android等等,這些庫各有自己的優缺點,詳情可以參考《Android AOP 總結》 這篇文章,該文章總結得比較詳細。

既然使用了AOP,必有它的用處,具體在Android中哪裡可以使用AOP程式設計呢? 一般的我們可以使用AOP實現一下這些功能,並且可以做好封裝,方便我們開發人員使用:

1)日誌

2)效能監控

3)資料校驗

4)統計

5)許可權驗證

當然除了以上的使用之外,可以更多的地方可能需要用到AOP程式設計。

瞭解了這麼多的AOP知識,接下來帶大家使用AspectJ,在Android中實現AOP程式設計。

AspectJ

為什麼使用AspectJ呢?主要是AspectJ功能比較強大,易於使用,註解也比較多和支援編譯時注入方式。既然選擇了,那麼接下來就看下如何使用AspectJ。

1)下載jar包,使用AspectJ必須到官網下載它的jar,地址:

http://www.eclipse.org/aspectj/downloads.php

2)將jar放到專案的libs下,同時Add As Library

AspectJ

AspectJ

3)在app的build.gradle的配置,需要加入一下程式碼

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}

final def log = project.logger
final def variants = project.android.applicationVariants

variants.all { variant ->
    if (!variant.buildType.isDebuggable()) {
        log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
        return;
    }

    JavaCompile javaCompile = variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.8",
                         "-inpath", javaCompile.destinationDir.toString(),
                         "-aspectpath", javaCompile.classpath.asPath,
                         "-d", javaCompile.destinationDir.toString(),
                         "-classpath", javaCompile.classpath.asPath,
                         "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
        log.debug "ajc args: " + Arrays.toString(args)

        MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler);
        for (IMessage message : handler.getMessages(null, true)) {
            switch (message.getKind()) {
                case IMessage.ABORT:
                case IMessage.ERROR:
                case IMessage.FAIL:
                    log.error message.message, message.thrown
                    break;
                case IMessage.WARNING:
                    log.warn message.message, message.thrown
                    break;
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break;
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break;
            }
        }
    }
}
複製程式碼

4)建立註解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface AOPTrace {
    String getValue();
}
複製程式碼

ElementType.METHOD表明用在方法上,RetentionPolicy.CLASS表示這個註解週期宣告在 class 檔案上。

5)建立Aspect類

@Aspect
public class AOPAspect {


    /**
     * 切點
     */
    @Pointcut("execution(@com.main.aop.AOPTrace  * *(..))")
    public void annoAOP() {

    }

    /**
     * 切面
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("annoAOP()")
    public Object dealPoint(ProceedingJoinPoint point) throws Throwable {
        //方法執行前
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        AOPTrace behaviorTrace = methodSignature.getMethod().getAnnotation(AOPTrace.class);
        String value = behaviorTrace.getValue();
        Log.i("test",""+value);
        //方法執行時
        Object object = null;
        try {
            object = point.proceed();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return object;
    }
    
}
複製程式碼

@Pointcut宣告註解的方法,也就是切點,object = point.proceed();執行註解的方法,需要把object返回即可。

6)使用

public class AopActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aop);
        this.findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                test();
            }
        });
    }

    /**
     * aop
     */
    @AOPTrace(getValue = "aop")
    private void test()
    {
        Log.i("test","你好,我是aop");
    }
}
複製程式碼

使用的時候,直接在使用方法哪裡,新增剛剛我們建立的註解即可

@AOPTrace(getValue = "aop")

7)執行結果

AspectJ

結果表示已經成功實現AOP程式設計了,使用這種方式可以很方便實現包括日誌輸入,許可權驗證和統計等功能。

參考文章:《【翻譯】Android中的AOP程式設計》,地址:

https://www.jianshu.com/p/0fa8073fd144

參考AspectJ的寫法

https://fernandocejas.com/2014/08/03/aspect-oriented-programming-in-android/

相關文章