Android 運行時注解(APT)使用及組件初始化邏輯講解

在組件化開發(fā)過程中呆盖,我們經(jīng)常會遇到一個問題,一些全局變量如何共享贷笛,如Application的全局功效应又,還有在應(yīng)用初始化過程中能否同時對組件進行初始化操作。誠然乏苦,通過每次使用手動編寫株扛,或者維護一個組件初始化表通過反射方式進行動態(tài)初始化操作都是可以的。那有沒有一個更效率更方便的方式實現(xiàn)呢汇荐,答案是有多洞就,接下來我們就通過Demo了解APT的使用及組件初始化的實現(xiàn)邏輯。

GitHub

一 創(chuàng)建項目

首先我們先創(chuàng)建一個MyAnnotation的項目掀淘。同時添加兩個Java-lib的Module旬蟋。
其中AnnotationLib作為注解類。ProcessLib作為解析類革娄。


-w461

二 創(chuàng)建注解

然后我們在AnnotationLib下添加注解類InitAppliation 代碼如下

package kt.dongdong.annotationlib;


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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface InitAppliation {
}

其中元注解@Target和@Retention分別表示作用對象為class倾贰,留存范圍為源碼階段。其他相關(guān)參數(shù)意義可以自行查詢稠腊。
這樣我們注解就完成了躁染,接下來我們處理對于多解析類。

解析配置

在創(chuàng)建解析類前架忌,我們先在ProcessLib中做一些相關(guān)引用。

  implementation project(":AnnotationLib")
  implementation 'com.google.auto.service:auto-service:1.0-rc4'

然后我們需要創(chuàng)建一個解析類InitProcess我衬。并在類上添加注解與父類叹放。

@AutoService(Processor.class)
public class InitProcess extends AbstractProcessor {

    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }
}

其中@AutoService表示注冊此解析類,只有注冊了才能用挠羔。
然后繼承AbstractProcessor并實現(xiàn)相關(guān)方法井仰。

@Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

這個方法返回執(zhí)行的java版本,一般直接返回SourceVersion.latestSupported()即可破加,當(dāng)然也可以指定版本如:SourceVersion.RELEASE_7則定義為java7.

@Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> setTypes = new HashSet<>();
        setTypes.add(InitAppliation.class.getCanonicalName());
        return setTypes;
    }

getSupportedAnnotationTypes返回需要處理的注解的集合俱恶。其中InitAppliation.class.getCanonicalName()表示拿到注解類的完整路徑。

ps:以上兩個方法配置都可以用注解的形式實現(xiàn)。

接下來我們就需要實現(xiàn)自動生成類的部分了

@Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        // 參數(shù)annotations表示需要處理的注解的集合
        // 參數(shù)roundEnv通過getElementsAnnotatedWith方法可以獲取對應(yīng)使用注解的類

        if (annotations.isEmpty()) {
            return false;
        }
        for (TypeElement typeElement : annotations) {

            //判斷是否是要解析的注解類
            if (typeElement.toString().equals(InitApplication.class.getCanonicalName())) {
                //獲取對應(yīng)列表
                Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(typeElement);
                StringBuilder strImpory = new StringBuilder();
                StringBuilder strNew = new StringBuilder();

                for (Element element : elements) {
                    strImpory.append("import " + element.toString() + ";\n");
                    strNew.append(element.getSimpleName() + " "
                            + element.getSimpleName().toString() + "C = new "
                            + element.getSimpleName() + "();\n");
                    strNew.append(element.getSimpleName().toString() + "C.init(application);\n");
                }
                try {
                    //通過processingEnv.getFiler()創(chuàng)建類文件并寫入合是。
                    JavaFileObject fileObject = processingEnv.getFiler().createSourceFile("kt.dongdong.annotationlib.AllInit");
                    Writer writer = fileObject.openWriter();
                    writer.write("package kt.dongdong.annotationlib;\n\n");
                    writer.write("import android.app.Application;\n");
                    writer.write(strImpory.toString() + "\n\n\n");
                    writer.write("public class AllInit{\n\n");
                    writer.write("public AllInit(Application application){\n");
                    writer.write(strNew.toString());
                    writer.write("}\n}\n");
                    writer.close();//注意需要關(guān)閉流 不然文件沒有內(nèi)容

                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }
        return false;
    }
  1. 在上面代碼中我寫了些注釋了罪,主要流程是:
  2. 通過roundEnv.getElementsAnnotatedWith()獲取使用了對應(yīng)注解多所有類信息。
  3. 通過循環(huán)這些類編寫對應(yīng)import和初始化的代碼串聪全。
  4. 通過processingEnv.getFiler().createSourceFile()創(chuàng)建指定名稱的類文件泊藕。
  5. 然后開啟流編寫包名,引用难礼,類名娃圆,初始化并調(diào)用方法等信息
  6. 然后關(guān)閉流 writer.close()很重要 不然生成的類沒有內(nèi)容。

生成文件

先在App項目中添加引用

    implementation project(":AnnotationLib")
    annotationProcessor project(":ProcessLib")

這里要注意的是解析Lib引用使用annotationProcessor關(guān)鍵字

創(chuàng)建AppliationA蛾茉,AppliationB類并添加注解讼呢。然后執(zhí)行Build 》ReBuild Project。


-w412

便能在該目錄下生成文件了谦炬。
文件內(nèi)容為我們在解析類里面寫入的內(nèi)容

package kt.dongdong.annotationlib;

import android.app.Application;
import kt.dongdong.myannotation.AppliationA;
import kt.dongdong.myannotation.AppliationB;

public class AllInit{

public AllInit(Application application){
AppliationA AppliationAC = new AppliationA();
AppliationAC.init(application);
AppliationB AppliationBC = new AppliationB();
AppliationBC.init(application);
}
}

PS:這里有幾點需要注意的
如果你的Gradle版本高于5悦屏,也許會因為不兼容無法生成類文件。這時候需要在注解類的Lib里面添加下面的引用

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

或者你可以把gradle版本降低至4.6 吧寺,這時候生成類的地址變?yōu)閎uild>generated>source>apt下

使用方式

在上面我們知道可以生成對于的類文件窜管,但是在開發(fā)中我們未編譯時又如何去使用對應(yīng)的生成類呢。這時候我們就需要通過一個中間類以反射的形式進行初始化了稚机。

package kt.dongdong.myannotation;

import android.app.Application;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestAppInit {

    public static void init(Application application) {

        try {
            Class<?> clazz = Class.forName("kt.dongdong.annotationlib.AllInit");
            Class[] parameterTypes = {Application.class};
            Constructor constructor = clazz.getConstructor(parameterTypes);
            Object object = constructor.newInstance(application);


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }


    }

}

其中Class.forName的類名就是我們在解析類里生成文件的名稱幕帆。
然后在Application中進行調(diào)研


public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        TestAppInit.init(this);
    }
}

運行起來后查看日志


-w551

OK 致此通過注解形式初始化組件與APT的使用方式大致了解了就。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赖条,一起剝皮案震驚了整個濱河市失乾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纬乍,老刑警劉巖碱茁,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仿贬,居然都是意外死亡纽竣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門茧泪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜓氨,“玉大人,你說我怎么就攤上這事队伟⊙ù担” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵嗜侮,是天一觀的道長港令。 經(jīng)常有香客問我啥容,道長,這世上最難降的妖魔是什么顷霹? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任咪惠,我火速辦了婚禮,結(jié)果婚禮上泼返,老公的妹妹穿的比我還像新娘硝逢。我一直安慰自己,他們只是感情好绅喉,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布渠鸽。 她就那樣靜靜地躺著,像睡著了一般柴罐。 火紅的嫁衣襯著肌膚如雪徽缚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天革屠,我揣著相機與錄音凿试,去河邊找鬼。 笑死似芝,一個胖子當(dāng)著我的面吹牛那婉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播党瓮,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼详炬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了寞奸?” 一聲冷哼從身側(cè)響起呛谜,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎枪萄,沒想到半個月后隐岛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡瓷翻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年聚凹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片齐帚。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡元践,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出童谒,到底是詐尸還是另有隱情,我是刑警寧澤沪羔,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布饥伊,位于F島的核電站象浑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏琅豆。R本人自食惡果不足惜愉豺,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望茫因。 院中可真熱鬧蚪拦,春花似錦、人聲如沸冻押。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽洛巢。三九已至括袒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稿茉,已是汗流浹背锹锰。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留漓库,地道東北人恃慧。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像渺蒿,于是被迫代替她去往敵國和親痢士。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

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