AndroidStudio 配置 AspectJ 環(huán)境實(shí)現(xiàn)AOP

昨天看了一段android配置aspectj實(shí)現(xiàn)AOP的直播視頻御铃,就試著自己配置了一下渊抽,可能是因?yàn)槲易约旱腁ndroidStudio環(huán)境的問題绘梦,碰到了不少的坑(其實(shí)還是因?yàn)閷?duì)gradle理解的不多)寝衫,但總歸是配置好了稀拐,就分享一下握截。

試了兩種方式飞崖,不過項(xiàng)目下的build.gradle,沒什么變化谨胞,直接看一下代碼吧:
build.gradle(項(xiàng)目下)

buildscript {
    ext {
        //android appcompat支持庫(kù)版本
        androidSupportVersion = '26.1.0'
        //編譯的 SDK 版本固歪,如API20
        compileSdkVersion = 26
        //構(gòu)建工具的版本,其中包括了打包工具aapt胯努、dx等,如API20對(duì)應(yīng)的build-tool的版本就是20.0.0
        buildToolsVersion = "26.0.2"
        //兼容的最低 SDK 版本
        minSdkVersion = 15
        //向前兼容牢裳,保存新舊兩種邏輯,并通過 if-else 方法來(lái)判斷執(zhí)行哪種邏輯
        targetSdkVersion = 26
        //kotlin版本號(hào)
        kotlin_version = '1.2.10'

        kotlinVersion = "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
        appcompatV7 = "com.android.support:appcompat-v7:$androidSupportVersion"
        appcompatDesign = "com.android.support:design:$androidSupportVersion"
        constraintLayout = 'com.android.support.constraint:constraint-layout:1.0.2'
    }
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath 'org.aspectj:aspectjtools:1.8.13'
        classpath 'org.aspectj:aspectjweaver:1.8.13'
    }

}

allprojects {
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

看著一大堆叶沛,主要就是下面這幾行配置蒲讯,其他的是我自己項(xiàng)目中用到的,根據(jù)自己需要配置就行灰署。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.13'
        classpath 'org.aspectj:aspectjweaver:1.8.13'
    }
}

repositories {
    mavenCentral()
}

其實(shí)這幾行配置在app的build.gradle里也是可以的判帮,但是因?yàn)轫?xiàng)目下的build.gradle里已經(jīng)有buildscript {}、allprojects {repositories{} }溉箕,就配置在這里了晦墙。

然后有兩種配置方式:

第一種
只有一個(gè)主Module app的情況下,配置app的build.gradle:

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'org.greenrobot.greendao'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    defaultConfig {
        applicationId "填入自己的applicationId"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"
        //Lambda配置
//        jackOptions.enabled = true
//        android.compileOptions.sourceCompatibility 1.8
        buildConfigField "boolean", "LOG", "true"http:// 顯示Log
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        //支持矢量圖
        vectorDrawables.useSupportLibrary = true
        ndk {
            //選擇要添加的對(duì)應(yīng)cpu類型的.so庫(kù)肴茄。
            abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            buildConfigField "boolean", "LOG", "false"http:// 顯示Log
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }


    }

    //Lambda配置
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    dataBinding {
        enabled true
    }

    greendao {
        schemaVersion 1//數(shù)據(jù)庫(kù)版本號(hào)
        daoPackage 'com.test.qby.newtestapplication.greendao'//設(shè)置DaoMaster偎痛、DaoSession、Dao包名
        targetGenDir 'src/main/java'//設(shè)置DaoMaster独郎、DaoSession踩麦、Dao目錄
        //targetGenDirTest:設(shè)置生成單元測(cè)試目錄
        //generateTests:設(shè)置自動(dòng)生成單元測(cè)試用例
    }

    lintOptions {
        abortOnError true
    }

}


dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation rootProject.ext.kotlinVersion
    implementation rootProject.ext.appcompatV7
    implementation rootProject.ext.constraintLayout
    compile rootProject.ext.appcompatDesign
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    compile 'jp.wasabeef:glide-transformations:3.0.1'
    // If you want to use the GPU Filters
    compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'
    //騰訊bugly
    compile 'com.tencent.bugly:crashreport:latest.release'
    compile 'com.tencent.bugly:nativecrashreport:latest.release'
    //retrofit
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'
    //rxJava
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    // Because RxAndroid releases are few and far between, it is recommended you also
    // explicitly depend on RxJava's latest version for bug fixes and new features.
    compile 'io.reactivex.rxjava2:rxjava:2.1.8'
    //greenDao
    compile 'org.greenrobot:greendao:3.2.0'
    //換膚功能
    compile 'com.zhy:changeskin:4.0.2'
    //AOP面向切面編程,加入這行就不用在libs下引入jar包了氓癌,不然要寫成compile file(libs/aspectjrt.jar)
    compile 'org.aspectj:aspectjrt:1.8.13'
}
/*
//在項(xiàng)目下配置了谓谦,此處就不需要了
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.13'
        classpath 'org.aspectj:aspectjweaver:1.8.13'
    }
}

repositories {
    mavenCentral()
}
*/

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

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.5",
                         "-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
            }
        }
    }
}

這一個(gè)gradle主要的東西就是這些:

//AOP面向切面編程,加入這行就不用在libs下引入jar包了贪婉,不然要寫成compile file(libs/aspectjrt.jar)
compile 'org.aspectj:aspectjrt:1.8.13'

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

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.5",
                         "-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
            }
        }
    }
}

下面那一堆是用命令在編譯最后做一些關(guān)聯(lián)的反粥,具體的我也不懂,只管加上好了。

第二種
有多個(gè)module都需要用到aspectj才顿,特別是組件開發(fā)的情況下莫湘,不可能每個(gè)module都配置一下,所以就需要新建一個(gè)aspectj的module作為項(xiàng)目的library郑气。
app下build.gradle需要修改:

//AOP面向切面編程幅垮,加入這行就不用在libs下引入jar包了,不然要寫成compile file(libs/aspectjrt.jar)
compile 'org.aspectj:aspectjrt:1.8.13'

去掉尾组,改為

implementation project(':aspectjlib')

不過上面這句在你添加module依賴的時(shí)候會(huì)自動(dòng)生成忙芒。

新建library的build.gradle配置如下:

apply plugin: 'com.android.library'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation rootProject.ext.appcompatV7
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    //AOP
    compile 'org.aspectj:aspectjrt:1.8.13'
}

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

android.libraryVariants.all { variant ->
    JavaCompile javaCompile = variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.5",
                         "-inpath", javaCompile.destinationDir.toString(),
                         "-aspectpath", javaCompile.classpath.asPath,
                         "-d", javaCompile.destinationDir.toString(),
                         "-classpath", javaCompile.classpath.asPath,
                         "-bootclasspath", android.bootClasspath.join(
                File.pathSeparator)]

        MessageHandler handler = new MessageHandler(true)
        new Main().run(args, handler)

        def log = project.logger
        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:
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break
            }
        }
    }
}

注意:下面那一堆跟app的gradle中的稍微有點(diǎn)區(qū)別,一個(gè)是module,一個(gè)是library讳侨,gradle中的東西不一樣呵萨。

兩種配置方式基本就是這樣了,使用方法我也是剛了解一點(diǎn)跨跨,記錄一下簡(jiǎn)單的計(jì)算性能的用法吧
自定義注解類:

package com.test.qby.aspectjlib.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by qby on 2018/1/26 0026.
 * 自定義注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IFirstAnnotation {
    String value();
}

@Target 注解目標(biāo)潮峦,表示注解使用在什么地方,這里是METHOD方法勇婴;@Retention 保留策略忱嘹,表示注解調(diào)用時(shí)機(jī),這里RUNTIME運(yùn)行時(shí)

切面類

import android.widget.Toast;

import com.test.qby.aspectjlib.annotation.IFirstAnnotation;
import com.test.qby.newtestapplication.app.MyApplication;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.util.Locale;

/**
 * Created by qby on 2018/1/26 0026.
 * 自定義注解行為
 */
@Aspect
public class MethodBehaviorAspect {
    private static final String TAG = "aspect_aby";

    @Pointcut("execution(@com.test.qby.aspectjlib.annotation.IFirstAnnotation * *(..))")
    public void firstMethodAnnotationBehavior() {
    }

    @Pointcut("execution(* com.test.qby.newtestapplication.ui.MainActivity.aspectClick(android.view.View))")
    public void secondMethodAnnotationBehavior() {
    }

    @Around("firstMethodAnnotationBehavior()")
    public Object wavePointcutAround(ProceedingJoinPoint joinPoint) throws Throwable {

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 類名
        String className = methodSignature.getDeclaringType().getSimpleName();
        // 方法名
        String methodName = methodSignature.getName();
        // 功能名
        IFirstAnnotation behaviorTrace = methodSignature.getMethod()
                .getAnnotation(IFirstAnnotation.class);
        String value = behaviorTrace.value();
//        String value = "點(diǎn)擊";
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;
        Log.e(TAG, String.format("%s類中%s方法執(zhí)行%s功能,耗時(shí):%dms", className, methodName, value, duration));
        Toast.makeText(MyApplication.getContext(), String.format(Locale.CHINESE, "%s類中%s方法執(zhí)行%s功能,耗時(shí):%dms", className, methodName, value, duration), Toast.LENGTH_SHORT).show();
        return result;
    }
}

@Aspect指定切面類咆耿;@Pointcut切入點(diǎn)德谅;@Around是切入方式Advice的一種,表示在切入點(diǎn)前后插入代碼萨螺,還有@Before窄做、@After;Pointcut語(yǔ)法慰技,execution椭盏,表示根據(jù)Advice在執(zhí)行方法內(nèi)部代碼前后插入代碼,call,表示根據(jù)Advice在調(diào)用方法前后插入代碼......

頁(yè)面調(diào)用

@IFirstAnnotation("測(cè)試Aspect")
public void aspectClick(View view) {
     try {
         Thread.sleep(new Random().nextInt(1000));
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
 }

@IFirstAnnotation調(diào)用注解吻商,()內(nèi)部為在IFirstAnnotation中寫的value的值掏颊,去掉value()后此處去掉()
注意:在MethodBehaviorAspect 類中如果有用到Context,可直接使用joinPoint.getTarget()類型轉(zhuǎn)換成Context艾帐,這里是由于項(xiàng)目使用了databinding乌叶,部分getTarget()獲取到的值不能強(qiáng)轉(zhuǎn)為Context,所以這里用的MyApplication獲取的Context

這只是個(gè)人的初步嘗試柒爸,里面當(dāng)然還有很多內(nèi)容需要去學(xué)准浴,剛看了CSDN上有人寫的幾篇關(guān)于AOP的內(nèi)容,都挺詳細(xì)的捎稚,給出其中一個(gè)地址乐横,自己看吧:http://blog.csdn.net/xwh_1230/article/details/78225258

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末求橄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子葡公,更是在濱河造成了極大的恐慌罐农,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件催什,死亡現(xiàn)場(chǎng)離奇詭異涵亏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蛆楞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門溯乒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)夹厌,“玉大人豹爹,你說(shuō)我怎么就攤上這事∶疲” “怎么了臂聋?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)或南。 經(jīng)常有香客問我孩等,道長(zhǎng),這世上最難降的妖魔是什么采够? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任肄方,我火速辦了婚禮,結(jié)果婚禮上蹬癌,老公的妹妹穿的比我還像新娘权她。我一直安慰自己,他們只是感情好逝薪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布隅要。 她就那樣靜靜地躺著,像睡著了一般董济。 火紅的嫁衣襯著肌膚如雪步清。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天虏肾,我揣著相機(jī)與錄音廓啊,去河邊找鬼。 笑死封豪,一個(gè)胖子當(dāng)著我的面吹牛谴轮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撑毛,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼书聚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼唧领!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起雌续,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤斩个,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后驯杜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體受啥,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年鸽心,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了滚局。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡顽频,死狀恐怖藤肢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情糯景,我是刑警寧澤嘁圈,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蟀淮,受9級(jí)特大地震影響最住,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜怠惶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一涨缚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧策治,春花似錦脓魏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至讽膏,卻和暖如春檩电,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背府树。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工俐末, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奄侠。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓卓箫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親垄潮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子烹卒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,167評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理闷盔,服務(wù)發(fā)現(xiàn),斷路器旅急,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,822評(píng)論 6 342
  • WHY 如果說(shuō)OOP(面向?qū)ο蟮某绦蛟O(shè)計(jì))的主要思想是將問題歸分到相應(yīng)的對(duì)象(類)中去實(shí)現(xiàn),再把相關(guān)類模塊化使得模...
    野生大P閱讀 957評(píng)論 0 8
  • 想想不能讓7天假期廢宅家里,于是熬通宵整了假期玩廢計(jì)劃集谣辞,plan 1迫摔、2、3……連夜定了車票泥从、酒店句占、車,天...
    怪誕城之夜閱讀 166評(píng)論 1 1