自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android高手進(jìn)階:性能調(diào)優(yōu)hugo中全面分析AOP切面編程使用詳解

移動(dòng)開(kāi)發(fā) Android
AOP 能夠?qū)崿F(xiàn)將日志紀(jì)錄,性能統(tǒng)計(jì),埋點(diǎn)統(tǒng)計(jì),安全控制,異常處理等代碼從具體的業(yè)務(wù)邏輯代碼中抽取出來(lái),放到統(tǒng)一的地方進(jìn)行處理。

[[411233]]

前言

Android 性能調(diào)優(yōu)中,通常存在需要對(duì)方法的執(zhí)行時(shí)間進(jìn)行統(tǒng)計(jì)的需求,這樣就可以看出哪些方法耗時(shí)多,是系統(tǒng)的瓶頸。最容易想到的方案是在每個(gè)方法的開(kāi)頭處獲取系統(tǒng)時(shí)間,在方法的結(jié)尾處再次獲取系統(tǒng)時(shí)間,前后兩個(gè)時(shí)間戳的差值就是這個(gè)方法執(zhí)行所消耗的總時(shí)間;

Hugo項(xiàng)目是一個(gè)調(diào)試函數(shù)調(diào)用耗時(shí)的工具,通過(guò)對(duì)方法或者類(lèi)添加@DebugLog注解,在運(yùn)行時(shí)會(huì)將函數(shù)的耗時(shí)打印在控制臺(tái)中,通常用于排查函數(shù)耗時(shí),或者用于卡頓檢測(cè);

hugo 這個(gè)框架麻雀雖小但五臟俱全,它使用了很多 Android 開(kāi)發(fā)中流行的技術(shù),例如注解,AOP,AspectJ,Gradle 插件;

一、hugo插件詳解

1、hugo使用

hugo 以 gradle 插件的形式供開(kāi)發(fā)者集成和使用,分為兩步:

  • 在項(xiàng)目全局添加對(duì) hugo 插件的依賴(lài)
  • 在需要使用 hugo 的 module 中應(yīng)用 hugo 插件
  1. buildscript { 
  2.   repositories { 
  3.     mavenCentral() 
  4.   } 
  5.   dependencies { 
  6.     classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1' // 添加 Hugo 的 Gradle 插件依賴(lài) 
  7.   } 
  1. apply plugin: 'com.jakewharton.hugo' // 應(yīng)用 Hugo 插件 

2、hugo源碼分析

①aspectjrt.jar:aspectJ 運(yùn)行時(shí)的依賴(lài)庫(kù),想要使用 aspectJ 的功能都需要引入這個(gè)庫(kù);

hugo-annotations:hugo 的注解庫(kù),定義了 DebugLog 這個(gè)注解;

  1. @Target({TYPE, METHOD, CONSTRUCTOR}) @Retention(CLASS) 
  2. public @interface DebugLog { 

②hugo-runtime:hugo 的運(yùn)行時(shí)庫(kù),是實(shí)現(xiàn) hugo 日志功能的核心庫(kù);

③hugo-plugin:hugo 的插件庫(kù),主要實(shí)現(xiàn)了aop的插件;

  1. class HugoPlugin implements Plugin<Project> { 
  2.   @Override void apply(Project project) { 
  3.     def hasApp = project.plugins.withType(AppPlugin) 
  4.     def hasLib = project.plugins.withType(LibraryPlugin) 
  5.     if (!hasApp && !hasLib) { 
  6.       throw new IllegalStateException("'android' or 'android-library' plugin required."
  7.     } 
  8.     final def log = project.logger 
  9.     final def variants 
  10.     if (hasApp) { 
  11.       variants = project.android.applicationVariants 
  12.     } else { 
  13.       variants = project.android.libraryVariants 
  14.     } 
  15.     project.dependencies { 
  16.       debugCompile 'com.jakewharton.hugo:hugo-runtime:1.2.2-SNAPSHOT' 
  17.       // TODO this should come transitively 
  18.       debugCompile 'org.aspectj:aspectjrt:1.8.6' 
  19.       compile 'com.jakewharton.hugo:hugo-annotations:1.2.2-SNAPSHOT' 
  20.     } 
  21.     project.extensions.create('hugo', HugoExtension) 
  22.     variants.all { variant -> 
  23.       if (!variant.buildType.isDebuggable()) { 
  24.         log.debug("Skipping non-debuggable build type '${variant.buildType.name}'."
  25.         return
  26.       } else if (!project.hugo.enabled) { 
  27.         log.debug("Hugo is not disabled."
  28.         return
  29.       } 
  30.       JavaCompile javaCompile = variant.javaCompile 
  31.       javaCompile.doLast { 
  32.         String[] args = [ 
  33.             "-showWeaveInfo"
  34.             "-1.5"
  35.             "-inpath", javaCompile.destinationDir.toString(), 
  36.             "-aspectpath", javaCompile.classpath.asPath, 
  37.             "-d", javaCompile.destinationDir.toString(), 
  38.             "-classpath", javaCompile.classpath.asPath, 
  39.             "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator) 
  40.         ] 
  41.         log.debug "ajc args: " + Arrays.toString(args) 
  42.         MessageHandler handler = new MessageHandler(true); 
  43.         new Main().run(args, handler); 
  44.         for (IMessage message : handler.getMessages(nulltrue)) { 
  45.           switch (message.getKind()) { 
  46.             case IMessage.ABORT: 
  47.             case IMessage.ERROR: 
  48.             case IMessage.FAIL: 
  49.               log.error message.message, message.thrown 
  50.               break; 
  51.             case IMessage.WARNING: 
  52.               log.warn message.message, message.thrown 
  53.               break; 
  54.             case IMessage.INFO: 
  55.               log.info message.message, message.thrown 
  56.               break; 
  57.             case IMessage.DEBUG: 
  58.               log.debug message.message, message.thrown 
  59.               break; 
  60.           } 
  61.         } 
  62.       } 
  63.     } 
  64.   } 

3、代碼實(shí)操

需要進(jìn)行日志記錄的類(lèi)名或者方法名處使用 @DebugLog 注解標(biāo)記即可;

  1. @Override 
  2. protected void onCreate(Bundle savedInstanceState) { 
  3.     super.onCreate(savedInstanceState); 
  4.     setContentView(R.layout.activity_main); 
  5. @DebugLog 
  6. private void test(String... tests) { 
  7.     for (String arg : tests) { 
  8.         Log.i("Args", arg); 
  9.     } 

二、AOP詳解

 1、什么是aop

AOP,全稱(chēng)為 Aspect Oriented Programming,即面向切面編程;AOP 是軟件開(kāi)發(fā)中的一個(gè)編程范式,通過(guò)預(yù)編譯方式或者運(yùn)行期動(dòng)態(tài)代理等實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù),它是 OOP(面向?qū)ο缶幊?的延續(xù),利用 AOP 開(kāi)發(fā)者可以實(shí)現(xiàn)對(duì)業(yè)務(wù)邏輯中的不同部分進(jìn)行隔離,從而進(jìn)一步降低耦合,提高程序的可復(fù)用性,進(jìn)而提高開(kāi)發(fā)的效率;

aop涉及到的關(guān)鍵知識(shí)點(diǎn):

  • 橫切關(guān)注點(diǎn)(Cross-cutting concerns):在面向?qū)ο缶幊讨校?jīng)常需要在不同的模塊代碼中添加一些類(lèi)似的代碼,例如在函數(shù)入口處打印日志,在 View 的點(diǎn)擊處添加點(diǎn)擊事件的埋點(diǎn)統(tǒng)計(jì),在 AOP 中把軟件系統(tǒng)分成兩個(gè)部分:核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn),核心關(guān)注點(diǎn)就是業(yè)務(wù)邏輯處理的主要流程,而橫切關(guān)注點(diǎn)就是上面所說(shuō)的經(jīng)常發(fā)生在核心關(guān)注點(diǎn)的多個(gè)地方,且基本相似的日志紀(jì)錄,埋點(diǎn)統(tǒng)計(jì)等等;
  • 連接點(diǎn)(Joint point):在核心關(guān)注點(diǎn)中可能會(huì)存在橫切關(guān)注點(diǎn)的地方,例如方法調(diào)用的入口,View 的點(diǎn)擊處理等地方,在 AOP 中習(xí)慣稱(chēng)為連接點(diǎn);
  • 增強(qiáng)(Advice):特定連接點(diǎn)處所執(zhí)行的動(dòng)作,也就是 AOP 織入的代碼,目的是對(duì)原有代碼進(jìn)行功能的增強(qiáng);
  • before:在目標(biāo)方法執(zhí)行之前的動(dòng)作;
  • around:在目標(biāo)方法之前前后的動(dòng)作;
  • after:在目標(biāo)方法執(zhí)行之后的動(dòng)作;
  • 切入點(diǎn)(Pointcut):連接點(diǎn)的集合,這些連接點(diǎn)可以確定什么時(shí)機(jī)會(huì)觸發(fā)一個(gè)通知;
  • 切面(Aspect):切入點(diǎn)和通知可以組合成一個(gè)切面;
  • 織入(Weaving):將通知注入到連接點(diǎn)的過(guò)程;

AOP 中代碼的織入根據(jù)類(lèi)型的不同,主要可以分為三類(lèi):

  • 編譯時(shí)織入:在 Java 類(lèi)文件編譯的時(shí)候進(jìn)行織入,這需要通過(guò)特定的編譯器來(lái)實(shí)現(xiàn),例如使用 AspectJ 的織入編譯器;
  • 類(lèi)加載時(shí)織入:通過(guò)自定義類(lèi)加載器 ClassLoader 的方式在目標(biāo)類(lèi)被加載到虛擬機(jī)之前進(jìn)行類(lèi)的字節(jié)代碼的增強(qiáng);
  • 運(yùn)行時(shí)織入:切面在運(yùn)行的某個(gè)時(shí)刻被動(dòng)態(tài)織入,基本原理是使用 Java 的動(dòng)態(tài)代理技術(shù);

2、Android中aop實(shí)現(xiàn):用aspectj實(shí)現(xiàn)aop

2.1、什么是AspectJ

①AspectJ實(shí)際上是對(duì)AOP編程思想的一個(gè)實(shí)踐,AOP雖然是一種思想,但就好像OOP中的Java一樣,一些先行者也開(kāi)發(fā)了一套語(yǔ)言來(lái)支持AOP;

基礎(chǔ)知識(shí)點(diǎn):

  • Aspect 切面:切面是切入點(diǎn)和通知的集合;
  • PointCut 切入點(diǎn):切入點(diǎn)是指那些通過(guò)使用一些特定的表達(dá)式過(guò)濾出來(lái)的想要切入Advice的連接點(diǎn);
  • Advice 通知:通知是向切點(diǎn)中注入的代碼實(shí)現(xiàn)方法;
  • Joint Point 連接點(diǎn):所有的目標(biāo)方法都是連接點(diǎn);
  • Weaving 編織:主要是在編譯期使用AJC將切面的代碼注入到目標(biāo)中, 并生成出代碼混合過(guò)的.class的過(guò)程;

涉及到的注解:

  • @Aspect:聲明切面,標(biāo)記類(lèi)
  • @Pointcut(切點(diǎn)表達(dá)式):定義切點(diǎn),標(biāo)記方法
  • @Before(切點(diǎn)表達(dá)式):前置通知,切點(diǎn)之前執(zhí)行
  • @Around(切點(diǎn)表達(dá)式):環(huán)繞通知,切點(diǎn)前后執(zhí)行
  • @After(切點(diǎn)表達(dá)式):后置通知,切點(diǎn)之后執(zhí)行
  • @AfterReturning(切點(diǎn)表達(dá)式):返回通知,切點(diǎn)方法返回結(jié)果之后執(zhí)行
  • @AfterThrowing(切點(diǎn)表達(dá)式):異常通知,切點(diǎn)拋出異常時(shí)執(zhí)行

2.2、實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)狀態(tài)檢測(cè)的AOP

①aspectj配置

項(xiàng)目的gradle中配置build.gradle(project)

  1. buildscript { 
  2.   repositories { 
  3.     google() 
  4.     mavenCentral() 
  5.     jcenter() 
  6.   } 
  7.   dependencies { 
  8.     classpath 'com.android.tools.build:gradle:4.1.1' 
  9.     classpath 'org.aspectj:aspectjtools:1.8.6' 
  10.   } 
  11. ...... 
  12. ...... 

主app中build.gradle(app)

  1. dependencies { 
  2.   compile 'org.aspectj:aspectjrt:1.8.6' 
  3. android.libraryVariants.all { variant -> 
  4.   JavaCompile javaCompile = variant.javaCompile 
  5.   javaCompile.doLast { 
  6.     String[] args = [ 
  7.         "-showWeaveInfo"
  8.         "-1.5"
  9.         "-inpath", javaCompile.destinationDir.toString(), 
  10.         "-aspectpath", javaCompile.classpath.asPath, 
  11.         "-d", javaCompile.destinationDir.toString(), 
  12.         "-classpath", javaCompile.classpath.asPath, 
  13.         "-bootclasspath", android.bootClasspath.join(File.pathSeparator) 
  14.     ] 
  15.     MessageHandler handler = new MessageHandler(true); 
  16.     new Main().run(args, handler) 
  17.     def log = project.logger 
  18.     for (IMessage message : handler.getMessages(nulltrue)) { 
  19.       switch (message.getKind()) { 
  20.         case IMessage.ABORT: 
  21.         case IMessage.ERROR: 
  22.         case IMessage.FAIL: 
  23.           log.error message.message, message.thrown 
  24.           break; 
  25.         case IMessage.WARNING: 
  26.         case IMessage.INFO: 
  27.           log.info message.message, message.thrown 
  28.           break; 
  29.         case IMessage.DEBUG: 
  30.           log.debug message.message, message.thrown 
  31.           break; 
  32.       } 
  33.     } 
  34.   } 

②aop實(shí)現(xiàn)

定義annotation:

  1. @Target(ElementType.METHOD) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. public @interface CheckNetwork { 

代碼注解:

  1. @CheckNetwork() 
  2.     private void checkNetwork() { 
  3.         LogUtil.i("AnnotationFragment""檢測(cè)完畢"); 
  4.     } 

關(guān)鍵處理切入點(diǎn):

  1. @Aspect 
  2. public class CheckNetworkAspect { 
  3.     private static final String TAG = CheckNetworkAspect.class.getSimpleName(); 
  4.     /** 
  5.      * 找到處理的切點(diǎn) 
  6.      *   * *(..)  “**”表示是任意包名   “..”表示任意類(lèi)型任意多個(gè)參數(shù) 
  7.      */ 
  8.     @Pointcut("execution(@la.xiong.androidquick.demo.features.function.annotation.aspect.CheckNetwork  * *(..))"
  9.     public void executionCheckNetwork() { 
  10.     } 
  11.     /** 
  12.      * 處理切面 
  13.      * 
  14.      * @param joinPoint 
  15.      * @return 
  16.      */ 
  17.     @Around("executionCheckNetwork()"
  18.     public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable { 
  19.         MethodSignature signature = (MethodSignature) joinPoint.getSignature(); 
  20.         CheckNetwork annotation = signature.getMethod().getAnnotation(CheckNetwork.class); 
  21.         if (annotation != null) { 
  22.             Context context = AspectUtils.getContext(joinPoint.getThis()); 
  23.             if (NetworkUtils.isConnected()) { 
  24.                 Toast.makeText(context, "當(dāng)前網(wǎng)絡(luò)正常", Toast.LENGTH_SHORT).show(); 
  25.             } else { 
  26.                 Toast.makeText(context, "此時(shí)沒(méi)有網(wǎng)絡(luò)連接", Toast.LENGTH_SHORT).show(); 
  27.             } 
  28.             return joinPoint.proceed(); 
  29.         } 
  30.         return null
  31.     } 

總結(jié):

AOP 能夠?qū)崿F(xiàn)將日志紀(jì)錄,性能統(tǒng)計(jì),埋點(diǎn)統(tǒng)計(jì),安全控制,異常處理等代碼從具體的業(yè)務(wù)邏輯代碼中抽取出來(lái),放到統(tǒng)一的地方進(jìn)行處理;

利用 AOP 開(kāi)發(fā)者可以實(shí)現(xiàn)對(duì)業(yè)務(wù)邏輯中的不同部分進(jìn)行隔離,從而進(jìn)一步降低耦合,提高程序的可復(fù)用性,進(jìn)而提高開(kāi)發(fā)的效率;

可以自定義屬于你的功能比如:日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等等。

本文轉(zhuǎn)載自微信公眾號(hào)「Android開(kāi)發(fā)編程」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開(kāi)發(fā)編程公眾號(hào)。

 

責(zé)任編輯:姜華 來(lái)源: Android開(kāi)發(fā)編程
相關(guān)推薦

2024-05-21 09:55:43

AspectOrientedAOP

2019-11-01 08:49:07

JVM監(jiān)控性能

2012-02-10 10:32:33

JavaSpring

2023-11-07 16:00:25

面向切面編程開(kāi)發(fā)

2009-08-24 09:46:40

面向切面編程AOP

2015-07-28 17:11:00

編程技術(shù)提升

2011-04-26 09:33:04

SpringAOP

2013-03-20 17:18:07

Linux系統(tǒng)性能調(diào)優(yōu)

2013-09-17 10:37:03

AOPAOP教程理解AOP

2009-04-01 11:17:52

內(nèi)存分配調(diào)優(yōu)Oracle

2017-07-21 08:55:13

TomcatJVM容器

2012-06-20 11:05:47

性能調(diào)優(yōu)攻略

2021-03-04 08:39:21

SparkRDD調(diào)優(yōu)

2018-11-12 14:53:09

Redis性能調(diào)優(yōu)數(shù)據(jù)庫(kù)

2023-11-30 18:03:55

IDEA工具

2013-03-21 11:20:00

性能測(cè)試性能調(diào)優(yōu)測(cè)試

2011-03-10 14:40:54

LAMPMysql

2011-05-20 15:02:01

Oracle性能調(diào)優(yōu)

2018-02-03 10:16:05

JavaScript Web 應(yīng)用

2011-11-14 10:28:23

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)