Android Studio中使用apt
一、前言
你還在對著枯燥的重復代碼一味復制粘貼嗎?這樣跟搬磚有何區(qū)別?你是否曾想過:你用代碼編寫出一個自動化的APP,但為何代碼本身卻缺少了活力?掌握Android-apt,杜絕重復代碼,讓你寫代碼如寫詩般優(yōu)雅。
二、何為apt?
apt意為:annotation processing tool(注解處理工具),這家伙可神奇了,它能通過注解,在編譯期自動生成特定的Java文件,實現(xiàn)自動編寫代碼。
問:有什么用?憑我自己本事能寫出來的代碼,為什么要自動化?
大哥,你這是又想施展你的復制粘貼大法了嗎?稍安勿躁,細看完這篇文章,你會愛上這家伙的。
鼎鼎大名的ButterKnife、Dagger2這兩個開源庫,相信你一定有聽過,你應該知道我為什么提到它們了吧。沒錯!這兩個開源庫都是基于apt的。
三、說了這么多,要怎么用啊?別急,我們先搭建環(huán)境(基于gradle插件2.2.0以上版本)
1.在android studio中新建一個Java module,用于存裝注解處理邏輯,名字隨便啦,反正我一般都取名:apt。很重要的事:在app module中添加注解處理依賴:annotationProcessor project(‘:apt’)
(解釋原因:由于android的module中不包含有apt相關類,因此需要新建一個java module來編寫apt邏輯。什么?你不信?不信你寫個類繼承AbstractProcessor試試)
2.再次新建一個module(android、java都可以),用于存裝注解,名字也隨便,反正我這里取名為:anno,并且在app、apt的build.gradle文件下,添加依賴compile project(‘:anno’)
(為什么要新建module去盛裝注解類,而不放到app module或者apt module中去:最主要的原因就是app module與apt module不能直接相互依賴,至于為什么不能直接依賴,我就不細說了,總之一句話:不信你試試看就知道嘍!)
3.在apt的build.gradle里,添加如下依賴。到此,我們的環(huán)境配置工作就告一段落了。
(其中:1.auto-service是用于注解后自動在特定路徑下生成配置文件;2.javapoet是用于配合apt便捷生成java文件的工具。相信這樣解釋大家還云里霧里,不要著急,繼續(xù)往下看)
四、環(huán)境搭建好了,接下來就是秀操作時間
1.首先,在anno module里新建一個注解類
- @Retention(RetentionPolicy.SOURCE)
- @Target(ElementType.METHOD)
- public @interface Test {
- String value();
- }
2.在apt module里新建一個注解處理類,繼承于AbstractProcessor
- public class TestProcessor extends AbstractProcessor{
- @Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- return false;
- }
- }
3.既然說apt是要自動生成java文件,那我們就需要擬構(gòu)出一個目標類。假設我們要生成這樣一個類
- public class TestClass {
- public static void main(String[] args){
- System.out.println("Hallo world!");
- }
- }
4.操作注解處理類,生成目標java文件
- @AutoService(Processor.class)
- @SupportedAnnotationTypes({
- "com.aop.anno.Test"
- })
- public class TestProcessor extends AbstractProcessor{
- @Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- //生成TestClass類
- TypeSpec.Builder tb = TypeSpec.classBuilder("TestClass")
- .addModifiers(Modifier.PUBLIC);
- //生成main方法
- MethodSpec.Builder mb = MethodSpec.methodBuilder("main")
- .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
- .returns(void.class)
- .addParameter(String[].class, "args");
- //生成代碼塊,并添加到main方法中
- for(TypeElement e : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Test.class))){
- CodeBlock cb = CodeBlock.builder()
- .addStatement("$T.out.println(\"$L + $L\")", System.class,
- e.getAnnotation(Test.class).value(), e.getSimpleName())
- .build();
- mb.addCode(cb);
- }
- tb.addMethod(mb.build());
- JavaFile jf = JavaFile.builder("com.example.apt", tb.build()).build();
- //將代碼寫入java文件中
- try {
- jf.writeTo(processingEnv.getFiler());
- } catch (IOException e) {
- e.printStackTrace();
- }
- return true;
- }
- }
大致說下步驟:
(1)添加@AutoService(Processor.class)注解,這個注解會自動在指定路徑下生成一個配置文件:
apt/build/classes/main/META-INF/services/javax.annotation.processing.Processor;
(2)添加@SupportedAnnotationTypes注解,配置這個類所要處理的注解類型。(傳入String類型參數(shù),格式為:包名+類名);
(3)采用javapoet書寫代碼構(gòu)建邏輯,具體用法去這里看看;
(4)生成代碼塊的主要邏輯是:遍歷所有被@Test注解過的類,取出注解內(nèi)容及類名打印出來。
5.在類上添加@Test注解,這里就用MainActivity來試試
- @Test("abc")
- public class MainActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
- }
6.rebuild工程,在app/build/generated/source/apt/debug路徑下找到目標java文件。至此,大功告成
TestClass代碼如下:
- public class TestClass {
- public static void main(String[] args) {
- System.out.println("abc + MainActivity");
- }
- }
五、然而并沒什么卵用?
確實,到此為止,我們確實是用了幾十行代碼去生成了一個5行代碼的TestClass,這種操作來說看起來可以用4個字來形容:閑得蛋疼。
然而,接下來的操作,會讓你耳目一新。首先我們新建幾個測試類,假如我們新建了這樣4個測試類:ActivityA,ActivityB,ActivityC,ActivityD,并且都給他們加上注解@Test。然后rebuild一下,你會發(fā)現(xiàn),我們的TestClass變了樣:
- public class TestClass {
- public static void main(String[] args) {
- System.out.println("A + ActivityA");
- System.out.println("B + ActivityB");
- System.out.println("C + ActivityC");
- System.out.println("D + ActivityD");
- System.out.println("abc + MainActivity");
- }
- }
恍然大悟!原來,是這么玩的!這時候,你是否已經(jīng)感覺到apt的魅力了呢?是的,它能幫你干掉重復代碼,讓你杜絕掉復制粘貼。