国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android切面編程入門講解

2019-10-21 21:43:01
字體:
來源:轉載
供稿:網友

切面編程聽起來可能有點陌生,不過現在越來越多的開發團隊正在使用上這種技術。

先說熟悉的面向對象編程 OOP,通常都是用各種對象/模塊來負責具體的功能,互相之間盡量不耦合。

切面編程AOP(aspect-priented programming)是為了解決OOP中耦合無法解除的問題而產生的。

打個比方現在項目中有負責網絡/數據存儲/UI幾個模塊,每個模塊都接入了另外一個Log模塊。
雖然Log不屬于前面三個的功能,但因為都接入了,所以他們在某種程度上就有了耦合,要修改Log模塊的實現的時候會影響到其他三個模塊的實現。

這篇文章用最簡單的例子來描述AOP是怎么解決這種問題的。(其實這是一篇AspectJ入門指南)

安裝AspectJ

Android上的ApsectJ開發由幾部分組成,AspectJ gradle插件,ApsectJ依賴,還有 AspectJ編譯器。
首先安裝AspectJ編譯器很簡單,就跟安裝JAVA環境一樣,

下載鏈接:http://www.eclipse.org/downloads/download.php?file=/tools/aspectj/aspectj-1.9.0.jar

目前最新的已經更新到1.9.1了。如果你電腦已經有JAVA環境的話直接運行這個jar包就行,
在安裝完畢后需要配置環境變量到 aspectj的bin目錄下,這里不贅述

export PATH="$PATH:~/Library/Android/sdk/platform-tools"export PATH="$PATH:/usr/local/opt/gradle/gradle-4.1/bin"export PATH="$PATH:~/Library/Android/sdk/ndk-bundle"export PATH="$PATH:~/Library/flutter/bin"export PATH="$PATH:~/Library/kotlinc/bin"export PATH="$PATH:~/Library/AspectJ/bin" <- AspectJ的PATH

配置完后運行 ajc -v 應該可以看到對應輸出

AspectJ Compiler 1.9.0 (1.9.0 - Built: Monday Apr 2, 2018 at 18:52:10 GMT)

配置Android Gradle增加AspectJ依賴

構建帶AspectJ支持的Android App的流程是先按正常流程編譯出 .class 文件后,再用 ajc 編譯器在 .class文件中插入我們需要的代碼。

首先需要把 AspectJ 依賴加到 gradle根目錄中,

buildscript {  repositories {    google()    jcenter()  }  dependencies {    classpath 'com.android.tools.build:gradle:3.1.2'    classpath 'org.aspectj:aspectjtools:1.8.9' //Aspect    classpath 'org.aspectj:aspectjweaver:1.8.9' //Aspect  }}

然后在項目app目錄的build.gradle需要添加以下內容,

apply plugin: 'com.android.application'//+增加內容import org.aspectj.bridge.MessageHandlerimport org.aspectj.tools.ajc.Mainbuildscript {  repositories {    mavenCentral()  }  dependencies {    classpath 'org.aspectj:aspectjtools:1.8.9'    classpath 'org.aspectj:aspectjweaver:1.8.9'  }}repositories {  mavenCentral()}final def log = project.loggerfinal def variants = project.android.applicationVariantsvariants.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)]    MessageHandler handler = new MessageHandler(true);    new Main().run(args, handler);  }}//-增加內容

這段gradle腳本是在java編譯完成后追加一個 acj 的編譯流程,

MessageHandler 是 AspectJ Tools中的對象,用來接收參數然后進行 acj 編譯的。

最后再把 dependencies依賴加上對AspectJ的支持就可以了,

implementation 'org.aspectj:aspectjrt:1.9.0'

創建AspectJ代碼

下面這部分代碼看起來會一臉懵逼,不過目前先不用管具體的語法含義,

先跑起來環境,然后再結合理論慢慢在修改代碼中感受就能快速的上手AOP了。

以一個HelloWorld為例子,我們的MainActivity中啥事情不干,只有基本的生命周期方法,

public class MainActivity extends AppCompatActivity {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);  }  @Override  protected void onStart() {    super.onStart();  }  @Override  protected void onPause() {    super.onPause();  }  @Override  protected void onStop() {    super.onStop();  }  @Override  protected void onDestroy() {    super.onDestroy();  }}

現在我們要寫一個AspectJ類,這個類看起來會跟一般的Java類有點不同,可以理解為它只是用注解作為媒介,讓ACJ編譯器知道要去注入哪些方法。

這個類要做的事情是告訴ACJ編譯器,要在MainActivity中的每個方法前面打印一行log,輸出當前執行的是哪個方法,

@Aspectpublic class AspectTest {  private static final String TAG = "AspectTest";  @Pointcut("execution(* phoenix.com.helloaspectj.MainActivity.**(..))")  public void executeAspectJ() {  }  @Before("executeAspectJ()")  public void beforeAspectJ(JoinPoint joinPoint) throws Throwable {    Log.d(TAG, "beforeAspectJ: injected -> " + joinPoint.toShortString());  }}

第一次接觸AspectJ的看到這段代碼有點摸不著頭腦,解釋一下幾個注解的意思,

  • @Aspect: 告訴ACJ編譯器這是個AspectJ類
  • @PointCut: PointCut是AspectJ中的一個概念,跟它一起的另一個概念是 JoinPoint,這兩個概念一起描述要注入的切面
  • @Before: 表示要注入的位置,常用的有 Before/After/Around,分別表示在執行前,執行后,和取代原方法

這里@PointCut注解后的參數表示的意思是對 MainActivity中的所有方法進行注入,參數用的是正則匹配語法。
下面看看這段代碼執行的結果

07-26 16:04:56.611 22823-22823/? D/AspectTest: beforeAspectJ: injected -> execution(MainActivity.onCreate(..))
07-26 16:04:56.661 22823-22823/? D/AspectTest: beforeAspectJ: injected -> execution(MainActivity.onStart())

看到雖然我們沒有在MainActivity中寫入log打印語句,但是通過AspectJ實現了,在MainActivity兩個生命周期執行前插入了我們自己的log。

使用場景

AspectJ只是AOP的其中一種手段,類似的還有用 asm 去修改字節碼。AOP之所以會有越來越多的人去了解,抽象上來說它可以非常好的去耦合。

高級點的可以用AOP來實現無痕埋點,數據收集,甚至修改SDK中動不了的代碼。

上面的整個DEMO代碼可以從GitHub上獲取,后臺回復"切面"就可以獲取下載鏈接。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 荔浦县| 武清区| 福鼎市| 内江市| 洞头县| 永福县| 广安市| 安徽省| 安乡县| 松溪县| 闵行区| 贡觉县| 佛山市| 青海省| 江口县| 雷波县| 黄石市| 宁化县| 张家港市| 丹江口市| 垦利县| 石家庄市| 玛曲县| 常熟市| 神农架林区| 衡山县| 巴林左旗| 冕宁县| 饶平县| 定结县| 三江| 阿尔山市| 凌海市| 白河县| 驻马店市| 喀喇| 青冈县| 长寿区| 武城县| 武汉市| 孟津县|