帶你了解APT

介紹

APT就是(Annotation Processing Tool )的簡稱渗常,就是可以在代碼編譯期間對注解進(jìn)行處理,并且生成Java文件拖云,減少手動的代碼輸入。

代表框架:
Dagger2
ButterKnife
EventBus
ARouter

作用

使用APT的優(yōu)點(diǎn)就是方便跃惫、簡單,可以少些很多重復(fù)的代碼坑雅。


APT處理要素

注冊處理器(AutoService) + 注解處理器(AbstractProcessor) + 代碼生成(javapoet)

1辈挂、 注冊處理器

注冊處理器有2種方式
一種是AutoService
首先引入

    implementation 'com.google.auto.service:auto-service:1.0-rc2'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc2'

然后再AbstractProcessor類中加上注解@AutoService(Processor.class)
這樣就注冊完成了
第二種是添加文件

image.png

2裹粤、 注解處理器(AbstractProcessor)

創(chuàng)建一個繼承AbstractProcessor 類就可以了终蒂,在process方法里,獲取我們需要的哪些注解遥诉。

public class BindViewProcessor extends AbstractProcessor {

public class BindViewProcessor extends AbstractProcessor {
    
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
    }

    /**
     * 指定注解
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> supportTypes = new LinkedHashSet<>();
        supportTypes.add(BindView.class.getCanonicalName());
        return supportTypes;
    }

    /**
     * 用來指定你使用的Java版本
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 掃描注解處理
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return true;
    }
3拇泣、 代碼生成

代碼生成也有2種方式來實(shí)現(xiàn)
1.StringBuilder來實(shí)現(xiàn),大概例子是這樣的矮锈,所有的代碼都是用字符串拼接起來霉翔,生成的代碼格式也很亂,而且很容易寫錯苞笨。
2.另外一種是使用javapoet生成代碼债朵,生成的代碼會自動排版,javapoet詳細(xì)用法

可以看下兩者的比較瀑凝。

   /**
     * 使用StringBuilder創(chuàng)建類序芦、而且還需要自己手寫導(dǎo)入包,這邊沒寫
     */
    public String generateJavaCode() {
        StringBuilder builder = new StringBuilder();
        builder.append("package ").append(mPackageName).append(";\n\n");
        builder.append('\n');
        builder.append("public class ").append(mBindingClassName);
        builder.append(" {\n");

        generateMethods(builder);//創(chuàng)建方法
        builder.append('\n');
        builder.append("}\n");
        return builder.toString();
    }

    /**
     * 使用javapoet創(chuàng)建類
     */
    public TypeSpec generateJavaCode2() {
        TypeSpec bindingClass = TypeSpec.classBuilder(mBindingClassName)
                .addModifiers(Modifier.PUBLIC)
                .addMethod(generateMethods2())//創(chuàng)建方法
                .build();
        return bindingClass;

    }

    /**
     * 使用StringBuilder創(chuàng)建方法
     */
    private void generateMethods(StringBuilder builder) {
        builder.append("public void bind(" + mTypeElement.getQualifiedName() + " target ) {\n");
        for (int id : mapId.keySet()) {
            VariableElement element = mapId.get(id);
            String name = element.getSimpleName().toString();
            builder.append("target." + name).append(" = ");
            builder.append("target.findViewById( " + id + ");\n");
        }
        builder.append("  }\n");
    }
  /**
     * 使用javapoet創(chuàng)建方法
     */
    private MethodSpec generateMethods2() {
        ClassName target = ClassName.bestGuess(mTypeElement.getQualifiedName().toString());
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind")
                .addModifiers(Modifier.PUBLIC)
                .returns(void.class)
                .addParameter(target, "target");

        for (int id : mapId.keySet()) {
            VariableElement element = mapId.get(id);
            String name = element.getSimpleName().toString();
            methodBuilder.addCode("target." + name + " = " + "target.findViewById( " + id + ");");
        }
        return methodBuilder.build();
    }

這三步完成我們就可以生成我們要的代碼了粤咪。
javapoet詳細(xì)用法


獲取注解對象

1谚中、運(yùn)行時注解: 通過 反射 機(jī)制獲取注解對象,會損耗性能寥枝,常用的框架有retrofit
2宪塔、編譯期注解: 通過 APT 方式獲取注解對象,不會造成性能損耗囊拜。常用的框架Dagger2, ButterKnife某筐、EventBusARouter

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
}

@Retention(RetentionPolicy.CLASS):表示編譯時注解
@Target(ElementType.FIELD):表示注解范圍為類成員(構(gòu)造方法艾疟、方法来吩、成員變量)

注解 @Target代表意思
@Target(ElementType.TYPE)                 接口、類蔽莱、枚舉弟疆、注解
@Target(ElementType.FIELD)                字段、枚舉的常量
@Target(ElementType.METHOD)               方法
@Target(ElementType.PARAMETER)            方法參數(shù)
@Target(ElementType.CONSTRUCTOR)          構(gòu)造函數(shù)
@Target(ElementType.LOCAL_VARIABLE)       局部變量
@Target(ElementType.ANNOTATION_TYPE)      注解
@Target(ElementType.PACKAGE)              包

詳細(xì)內(nèi)容

element 的概念

表示一個程序元素盗冷,比如包怠苔、類或者方法。
element 包含有
PackageElement 表示一個包程序元素
TypeElement 表示一個類或接口程序元素
VariableElement 表示一個字段仪糖、enum 常量柑司、方法或構(gòu)造方法參數(shù)迫肖、局部變量或異常參數(shù)
ExecutableElement 表示某個類或接口的方法、構(gòu)造方法或初始化程序(靜態(tài)或?qū)嵗┰艹郏ㄗ⒔忸愋驮?br> TypeParameterElement 表示一般類蟆湖、接口、方法或構(gòu)造方法元素的泛型參數(shù)
如下圖


public class Foo {        // TypeElement 類型元素

    private int a;      // VariableElement 變量元素
    private Foo other;  // VariableElement 變量元素

    public Foo() { // ExecutableElement 可執(zhí)行元素
    }

    public void setA(int newA ) { //   newA  代表是一個 VariableElement)
    }
}

Element 的源碼玻粪,源碼解析隅津。

public interface Element extends javax.lang.model.AnnotatedConstruct {
    /**
     * 返回該元素定義的類型。
     * 泛型元素定義了一系列類型劲室,而不僅僅是一個類型伦仍。如果這是一個泛型元素,則返回一個原型
     * 類型很洋。這是元素在對應(yīng)于它自己的正式類型參數(shù)的類型變量上的調(diào)用充蓝。例如,對于泛型類元素
     * C<N extends Number>喉磁,返回參數(shù)化類型C<N>谓苟。類型實(shí)用程序接口有更一般的方法來獲取元
     * 素定義的所有類型的范圍。 
     */
    TypeMirror asType();

    /**
     * 返回該元素的類型
     */
    ElementKind getKind();

    /**
     * 返回該元素的修飾符,包括注解.
     * 隱式修飾符也包含,比如接口方法中的public和static
     */
    Set<Modifier> getModifiers();

    /**
     * 返回該元素的簡單名稱.泛型類型的名稱不包括對其正式類型參數(shù)的任何引用协怒。
     * 舉例,java.util.Set<E>的簡單名稱是Set.
     * 如果該元素代表的是未命名包,則返回一個空 Name.
     * 如果代表的是構(gòu)造器,則返回<init>所對應(yīng)的Name.如果代表的是靜態(tài)代碼塊,則返回的是<clinit>
     * 如果代表的是匿名類或者是初始代碼塊,則返回一個空 Name.
     */
    Name getSimpleName();

    /**
     * 返回包圍該元素的最內(nèi)層的元素.
     * 如果這個元素的聲明緊接在另一個元素的聲明中娜谊,則返回另一個元素。
     * 如果這是頂級類型斤讥,則返回其包。
     * 如果這是一個包湾趾,則返回null芭商。
     * 如果這是類型參數(shù),則返回類型參數(shù)的泛型元素搀缠。
     * 如果這是一個方法或構(gòu)造函數(shù)參數(shù)铛楣,則返回聲明該參數(shù)的可執(zhí)行元素。
     */
    Element getEnclosingElement();

    /**
     * 返回該元素所包含的元素.
     * 類或接口被認(rèn)為包含了它直接聲明的字段艺普、方法簸州、構(gòu)造函數(shù)和成員類型.包直接包含了頂級類和接
     * 口,但不包含其子包歧譬。其他類型的元素目前不被認(rèn)為包含任何元素岸浑;然而,隨著這個API或編程語
     * 言的發(fā)展瑰步,這些元素可能會改變
     */
    List<? extends Element> getEnclosedElements();

    /**
     * 當(dāng)給定的參數(shù)和當(dāng)前類代表同一個元素時返回true,否則,返回false.
     * 注意矢洲,元素的標(biāo)識涉及不能直接從元素的方法中訪問的隱式狀態(tài),包括關(guān)于不相關(guān)類型的存在的
     * 狀態(tài)缩焦。即使“同一個”元素正在被建模读虏,由這些接口的不同實(shí)現(xiàn)創(chuàng)建的元素對象也不應(yīng)該期望相
     * 等责静;這類似于通過不同的類加載器加載的同一個類文件的Class對象是不同的
     *
     */
    @Override
    boolean equals(Object obj);

    /**
     * 基于Object.hashCode
     */
    @Override
    int hashCode();


    /**
     * 獲得直接聲明在該元素上的注解
     * 如果要獲得繼承的注解,使用Elements#getAllAnnotationMirrors(Element)方法.
     */
    @Override
    List<? extends AnnotationMirror> getAnnotationMirrors();


    @Override
    <A extends Annotation> A getAnnotation(Class<A> annotationType);


    <R, P> R accept(ElementVisitor<R, P> v, P p);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市盖桥,隨后出現(xiàn)的幾起案子灾螃,更是在濱河造成了極大的恐慌,老刑警劉巖揩徊,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腰鬼,死亡現(xiàn)場離奇詭異,居然都是意外死亡靴拱,警方通過查閱死者的電腦和手機(jī)垃喊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袜炕,“玉大人本谜,你說我怎么就攤上這事≠司剑” “怎么了乌助?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長陌知。 經(jīng)常有香客問我他托,道長,這世上最難降的妖魔是什么仆葡? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任赏参,我火速辦了婚禮,結(jié)果婚禮上沿盅,老公的妹妹穿的比我還像新娘把篓。我一直安慰自己,他們只是感情好腰涧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布韧掩。 她就那樣靜靜地躺著,像睡著了一般窖铡。 火紅的嫁衣襯著肌膚如雪疗锐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天费彼,我揣著相機(jī)與錄音滑臊,去河邊找鬼。 笑死敌买,一個胖子當(dāng)著我的面吹牛简珠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼聋庵,長吁一口氣:“原來是場噩夢啊……” “哼膘融!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起祭玉,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤氧映,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后脱货,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岛都,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年振峻,在試婚紗的時候發(fā)現(xiàn)自己被綠了臼疫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡扣孟,死狀恐怖烫堤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凤价,我是刑警寧澤鸽斟,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站利诺,受9級特大地震影響富蓄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慢逾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一立倍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侣滩,春花似錦帐萎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赁项。三九已至葛躏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悠菜,已是汗流浹背舰攒。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悔醋,地道東北人摩窃。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親猾愿。 傳聞我的和親對象是個殘疾皇子鹦聪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

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