4

Android Studio中使用apt

 2 years ago
source link: http://www.androidchina.net/6972.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Android Studio中使用apt

你还在对着枯燥的重复代码一味复制粘贴吗?这样跟搬砖有何区别?你是否曾想过:你用代码编写出一个自动化的APP,但为何代码本身却缺少了活力?掌握Android-apt,杜绝重复代码,让你写代码如写诗般优雅。

二、何为apt?

apt意为:annotation processing tool(注解处理工具),这家伙可神奇了,它能通过注解,在编译期自动生成特定的Java文件,实现自动编写代码。

问:有什么用?凭我自己本事能写出来的代码,为什么要自动化?
大哥,你这是又想施展你的复制粘贴大法了吗?稍安勿躁,细看完这篇文章,你会爱上这家伙的。

鼎鼎大名的ButterKnife、Dagger2这两个开源库,相信你一定有听过,你应该知道我为什么提到它们了吧。没错!这两个开源库都是基于apt的。

三、说了这么多,要怎么用啊?别急,我们先搭建环境(基于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里,添加如下依赖。到此,我们的环境配置工作就告一段落了。

这里写图片描述
(其中:1.auto-service是用于注解后自动在特定路径下生成配置文件;2.javapoet是用于配合apt便捷生成java文件的工具。相信这样解释大家还云里雾里,不要着急,继续往下看)

四、环境搭建好了,接下来就是秀操作时间

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文件,那我们就需要拟构出一个目标类。假设我们要生成这样一个类

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类型参数,格式为:包名+类名);
(3)采用javapoet书写代码构建逻辑,具体用法去这里看看;
(4)生成代码块的主要逻辑是:遍历所有被@Test注解过的类,取出注解内容及类名打印出来。

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一下,你会发现,我们的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");
  }
}

恍然大悟!原来,是这么玩的!这时候,你是否已经感觉到apt的魅力了呢?是的,它能帮你干掉重复代码,让你杜绝掉复制粘贴。

APT使用案例:APT实用案例一:状态模式之就算违背开闭原则又何妨?

转载请注明:Android开发中文站 » Android Studio中使用apt


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK