Android Studio中使用apt
创始人
2025-02-13 05:41:03
0

一、前言

你还在对着枯燥的重复代码一味复制粘贴吗?这样跟搬砖有何区别?你是否曾想过:你用代码编写出一个自动化的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里新建一个注解类

  1. @Retention(RetentionPolicy.SOURCE) 
  2. @Target(ElementType.METHOD) 
  3. public @interface Test { 
  4.     String value(); 
  5. }  

2.在apt module里新建一个注解处理类,继承于AbstractProcessor

  1. public class TestProcessor extends AbstractProcessor{ 
  2.     @Override 
  3.     public boolean process(Set annotations, RoundEnvironment roundEnv) { 
  4.         return false; 
  5.     } 
  6. }  

3.既然说apt是要自动生成java文件,那我们就需要拟构出一个目标类。假设我们要生成这样一个类

  1. public class TestClass { 
  2.  
  3.     public static void main(String[] args){ 
  4.         System.out.println("Hallo world!"); 
  5.     } 
  6.  
  7. }  

4.操作注解处理类,生成目标java文件

  1. @AutoService(Processor.class) 
  2. @SupportedAnnotationTypes({ 
  3.         "com.aop.anno.Test" 
  4. }) 
  5. public class TestProcessor extends AbstractProcessor{ 
  6.  
  7.     @Override 
  8.     public boolean process(Set annotations, RoundEnvironment roundEnv) { 
  9.  
  10.         //生成TestClass类 
  11.         TypeSpec.Builder tb = TypeSpec.classBuilder("TestClass") 
  12.                 .addModifiers(Modifier.PUBLIC); 
  13.  
  14.         //生成main方法 
  15.         MethodSpec.Builder mb = MethodSpec.methodBuilder("main") 
  16.                 .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 
  17.                 .returns(void.class) 
  18.                 .addParameter(String[].class, "args"); 
  19.  
  20.         //生成代码块,并添加到main方法中 
  21.         for(TypeElement e : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Test.class))){ 
  22.             CodeBlock cb = CodeBlock.builder() 
  23.                     .addStatement("$T.out.println(\"$L + $L\")", System.class,  
  24.                     e.getAnnotation(Test.class).value(), e.getSimpleName()) 
  25.                     .build(); 
  26.             mb.addCode(cb); 
  27.         } 
  28.  
  29.         tb.addMethod(mb.build()); 
  30.  
  31.         JavaFile jf = JavaFile.builder("com.example.apt", tb.build()).build(); 
  32.         //将代码写入java文件中 
  33.         try { 
  34.             jf.writeTo(processingEnv.getFiler()); 
  35.         } catch (IOException e) { 
  36.             e.printStackTrace(); 
  37.         } 
  38.  
  39.         return true; 
  40.     } 
  41. }  

大致说下步骤:

(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来试试

  1. @Test("abc") 
  2. public class MainActivity extends AppCompatActivity { 
  3.  
  4.     @Override 
  5.     protected void onCreate(Bundle savedInstanceState) { 
  6.         super.onCreate(savedInstanceState); 
  7.         setContentView(R.layout.activity_main); 
  8.     } 
  9. }  

6.rebuild工程,在app/build/generated/source/apt/debug路径下找到目标java文件。至此,大功告成   

 

TestClass代码如下:

  1. public class TestClass { 
  2.   public static void main(String[] args) { 
  3.     System.out.println("abc + MainActivity"); 
  4.   } 
  5. }  

五、然而并没什么卵用?

确实,到此为止,我们确实是用了几十行代码去生成了一个5行代码的TestClass,这种操作来说看起来可以用4个字来形容:闲得蛋疼。

然而,接下来的操作,会让你耳目一新。首先我们新建几个测试类,假如我们新建了这样4个测试类:ActivityA,ActivityB,ActivityC,ActivityD,并且都给他们加上注解@Test。然后rebuild一下,你会发现,我们的TestClass变了样:

  1. public class TestClass { 
  2.   public static void main(String[] args) { 
  3.     System.out.println("A + ActivityA"); 
  4.     System.out.println("B + ActivityB"); 
  5.     System.out.println("C + ActivityC"); 
  6.     System.out.println("D + ActivityD"); 
  7.     System.out.println("abc + MainActivity"); 
  8.   } 

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

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
P2P的自白|我不生产内容,我... 现在一提起P2P,人们就会联想到正在被有关部门“围剿”的互联网理财服务。×租宝事件使得劳...
Intel将Moblin社区控... 本周二,非营利机构Linux基金会宣布,他们将担负起Moblin社区的管理工作,而这之前,Mobli...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...