【Android】AOP 面向切面編程(一) -- AspectJ 處理網(wǎng)絡(luò)錯誤

什么是AOP循狰,與OOP的區(qū)別

OOP: (Object Oriented Programming) 面向?qū)ο蟮某绦蛟O(shè)計(jì)。所謂“對象”在顯式支持面向?qū)ο蟮恼Z言中券勺,一般是指類在內(nèi)存中裝載的實(shí)例绪钥,具有相關(guān)的成員變量和成員函數(shù)(也稱為:方法)。

AOP: (Aspect Oriented Programming) 面向切面編程关炼。是目前軟件開發(fā)中的一個熱點(diǎn)程腹,也是Spring框架中容。利用AOP可以對業(yè)務(wù)邏輯的各個部分進(jìn)行隔離儒拂,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低寸潦,提高程序的可重用性,同時提高了開發(fā)的效率社痛。

[圖片上傳失敗...(image-926a06-1512701224758)]

AOP的適用范圍

埋點(diǎn)见转,日志記錄,性能統(tǒng)計(jì)蒜哀,安全控制斩箫,事務(wù)處理,異常處理等等撵儿。

AOP方式

  1. 代碼預(yù)編譯 -- AspectJ
  2. 運(yùn)行期動態(tài)代理

AspectJ介紹

AspectJ是一個面向切面的框架乘客,它擴(kuò)展了Java語言。AspectJ定義了AOP語法淀歇,所以它有一個專門的編譯器用來生成遵守Java字節(jié)編碼規(guī)范的Class文件易核。

AspectJ概念

  1. pointcut

    是一個(組)基于正則表達(dá)式的表達(dá)式,有點(diǎn)繞浪默,就是說他本身是一個表達(dá)式牡直,但是他是基于正則語法的缀匕。通常一個pointcut,會選取程序中的某些我們感興趣的執(zhí)行點(diǎn)井氢,或者說是程序執(zhí)行點(diǎn)的集合弦追。
    一個切入點(diǎn)通過一個普通的方法定義來提供岳链,并且切入點(diǎn)表達(dá)式使用@Pointcut注解來聲明花竞,注解的方法返回類型必須為void型。
    要建立復(fù)雜的切入點(diǎn)表達(dá)式掸哑,可以通過&&约急、||和!進(jìn)行組合苗分,也可以通過名字引用切入點(diǎn)表達(dá)式厌蔽。

    // 匹配所有com.fastaoe.aspectjdemo包下以test結(jié)尾的類的方法都會被執(zhí)行
    @Pointcut("execution(*com.fastaoe.aspectjdemo.*test * *(..))")  
    public void pointcut1(){}  
    
    // 匹配所有com.fastaoe.aspectjdemo.biz包下所有的類
    @Pointcut("within(com.fastaoe.aspectjdemo.biz.*)")  
    public void pointcut2(){} 
    
    // 同時匹配2個方法
    @Pointcut("pointcut1()&&pointcut2()")  
    private void tradingOperation(){} 
    
    aop_pointcut
  1. joinPoint

    通過pointcut選取出來的集合中的具體的一個執(zhí)行點(diǎn),我們就叫JoinPoint.

  2. Advice

    在選取出來的JoinPoint上要執(zhí)行的操作摔癣、邏輯奴饮。關(guān)于5種類型,我不多說择浊,不懂的同學(xué)自己補(bǔ)基礎(chǔ)戴卜。

    • Before Advice:@Before
    • After returning advice:@AfterReturning,可在通知體內(nèi)得到返回的實(shí)際值琢岩;
    • After throwing advice:@AfterThrowing
    • After (finally) advice : @After 最終通知必須準(zhǔn)備處理正常和異常兩種返回情況投剥,它通常用于釋放資源。
    • Around advice : @Around 環(huán)繞通知使用@Around注解來聲明担孔,通知方法的第一個參數(shù)必須是ProceedingJoinPoint類型江锨,在通知內(nèi)部調(diào)用ProceedingJoinPoint的Proceed()方法會導(dǎo)致執(zhí)行真正的方法,傳入一個Object[]對象糕篇,數(shù)組中的值將被作為一個參數(shù)傳遞給方法啄育。
  3. aspect

    就是我們關(guān)注點(diǎn)的模塊化。這個關(guān)注點(diǎn)可能會橫切多個對象和模塊拌消,事務(wù)管理是橫切關(guān)注點(diǎn)的很好的例子挑豌。它是一個抽象的概念,從軟件的角度來說是指在應(yīng)用程序不同模塊中的某一個領(lǐng)域或方面拼坎。又pointcut和advice組成浮毯。

  4. Target

    被aspectj橫切的對象。我們所說的joinPoint就是Target的某一行泰鸡,如方法開始執(zhí)行的地方债蓝、方法類調(diào)用某個其他方法的代碼。

AspectJ例子

一般情況下盛龄,如果我們需要在獲取網(wǎng)絡(luò)數(shù)據(jù)的時候需要判斷網(wǎng)絡(luò)是否存在饰迹,如果不存在芳誓,Toast提示用戶的話,代碼是這樣的啊鸭。

public void getNetData1() {
   if (isNetworkAvailable(this)) {
       Toast.makeText(this, "開始獲取新的網(wǎng)絡(luò)信息1", Toast.LENGTH_LONG).show();
   } else {
       Toast.makeText(this,"請檢查您的網(wǎng)絡(luò)",Toast.LENGTH_LONG).show();
   }
}

public void getNetData2() {
   if (isNetworkAvailable(this)) {
       Toast.makeText(this, "開始獲取新的網(wǎng)絡(luò)信息2", Toast.LENGTH_LONG).show();
   } else {
       Toast.makeText(this,"請檢查您的網(wǎng)絡(luò)",Toast.LENGTH_LONG).show();
   }
}

/**
* 檢查當(dāng)前網(wǎng)絡(luò)是否可用
*
* @return
*/
private static boolean isNetworkAvailable(Context context) {
   ConnectivityManager connectivityManager = (ConnectivityManager)
           context.getSystemService(Context.CONNECTIVITY_SERVICE);
   if (connectivityManager != null) {
       NetworkInfo[] networkInfo = connectivityManager.getAllNetworkInfo();

       if (networkInfo != null && networkInfo.length > 0) {
           for (int i = 0; i < networkInfo.length; i++) {
               if (networkInfo[i].getState() == NetworkInfo.State.CONNECTED) {
                   return true;
               }
           }
       }
   }
   return false;
}

如果只有幾個這樣的代碼段維護(hù)還是比較簡單的锹淌,但是如果在整個項(xiàng)目中有非常多的網(wǎng)絡(luò)請求,如果都是這樣的方式來判斷網(wǎng)絡(luò)是否可用的話赠制,這就是非常痛苦的事情赂摆。
并且當(dāng)產(chǎn)品的需求改變的時候,每個代碼段都需要修改钟些,很有可能修改的時候會出現(xiàn)修改不完全有遺漏的地方烟号,并且回歸測試的時候?qū)τ跍y試人員來說也是非常痛苦的事情。
所以我們需要其他的方式來改變這個現(xiàn)狀政恍,其中比較好的方式就是AOP汪拥。

  1. 引入aspectjrt.jar

    aspectj-downloads

    下載完成之后添加到app的libs中,并在build.gradle引入

    compile files('libs/aspectjrt.jar')
    
  1. 在build.gradle(app)中添加

    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;
                }
            }
        }
    }
    
    
  2. 添加注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface CheckNet {
    }
    
  3. 創(chuàng)建AspectJ文件

    1. 需要在類上引用Aspect注解
    2. @Pointcut("execution(@com.fastaoe.aspectjdemo.CheckNet * *(..))") - 定義切入點(diǎn)篙耗,也就是需要處理的方法迫筑,切入點(diǎn)的內(nèi)容是一個表達(dá)式,來描述切入哪些對象的哪些方法宗弯,("excute (*add*(..))")切入點(diǎn)表達(dá)表示將要切入所有以add開頭的方法脯燃,該方法可帶任意個數(shù)的參數(shù)
    3. @Around("checkNetBehavior()") - 定義處理的核心方法
    @Aspect
    public class SectionAspect {
    
        @Pointcut("execution(@com.fastaoe.aspectjdemo.CheckNet * *(..))")
        public void checkNetBehavior() {
    
        }
    
        @Around("checkNetBehavior()")
        public Object checkNet(ProceedingJoinPoint joinPoint) throws Throwable {
            Log.d("SectionAspect", "checkNetStart");
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            CheckNet annotation = signature.getMethod().getAnnotation(CheckNet.class);
            if (annotation != null) {
                Object object = joinPoint.getThis();
                Context context = getContext(object);
                if (context != null) {
                    if (!isNetworkAvailable(context)) {
                        Toast.makeText(context,"請檢查您的網(wǎng)絡(luò)",Toast.LENGTH_LONG).show();
                        return null;
                    }
                }
            }
            return joinPoint.proceed();
        }
    
        /**
         * 通過對象獲取上下文
         *
         * @param object
         * @return
         */
        private Context getContext(Object object) {
            if (object instanceof Activity) {
                return (Activity) object;
            } else if (object instanceof Fragment) {
                Fragment fragment = (Fragment) object;
                return fragment.getActivity();
            } else if (object instanceof View) {
                View view = (View) object;
                return view.getContext();
            }
            return null;
        }
    
        /**
         * 檢查當(dāng)前網(wǎng)絡(luò)是否可用
         *
         * @return
         */
        private static boolean isNetworkAvailable(Context context) {
            ConnectivityManager connectivityManager = (ConnectivityManager)
                    context.getSystemService(Context.CONNECTIVITY_SERVICE);
            if (connectivityManager != null) {
                NetworkInfo[] networkInfo = connectivityManager.getAllNetworkInfo();
    
                if (networkInfo != null && networkInfo.length > 0) {
                    for (int i = 0; i < networkInfo.length; i++) {
                        if (networkInfo[i].getState() == NetworkInfo.State.CONNECTED) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    }
    
  4. 使用注解

    @CheckNet
    private void getNetData() {
        Toast.makeText(this, "開始獲取新的網(wǎng)絡(luò)信息", Toast.LENGTH_LONG).show();
    }
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市罕伯,隨后出現(xiàn)的幾起案子曲伊,更是在濱河造成了極大的恐慌,老刑警劉巖追他,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坟募,死亡現(xiàn)場離奇詭異,居然都是意外死亡邑狸,警方通過查閱死者的電腦和手機(jī)懈糯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來单雾,“玉大人赚哗,你說我怎么就攤上這事」瓒眩” “怎么了屿储?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渐逃。 經(jīng)常有香客問我够掠,道長,這世上最難降的妖魔是什么茄菊? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任疯潭,我火速辦了婚禮赊堪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘竖哩。我一直安慰自己哭廉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布相叁。 她就那樣靜靜地躺著遵绰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钝荡。 梳的紋絲不亂的頭發(fā)上街立,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天舶衬,我揣著相機(jī)與錄音埠通,去河邊找鬼。 笑死逛犹,一個胖子當(dāng)著我的面吹牛端辱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播虽画,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼舞蔽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了码撰?” 一聲冷哼從身側(cè)響起渗柿,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脖岛,沒想到半個月后朵栖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柴梆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年陨溅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绍在。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡门扇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出偿渡,到底是詐尸還是另有隱情臼寄,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布溜宽,位于F島的核電站吉拳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏坑质。R本人自食惡果不足惜合武,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一临梗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧稼跳,春花似錦盟庞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至红淡,卻和暖如春不狮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背在旱。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工摇零, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桶蝎。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓驻仅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親登渣。 傳聞我的和親對象是個殘疾皇子噪服,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容