|
79 | 79 |
|
80 | 80 |
|
81 | 81 |
|
82 |
| -<meta name="description" content="知耻而后勇,知弱而图强"> |
| 82 | +<meta name="description" content="1234title: 安卓注解处理器-processordate: 2019-01-04 19:02:34tags: annotation、processor、自动生成java文件category: 注解 最近在学习安卓开源框架发现,很多的开源框架都使用到了注解处理器,例如EventBus3.0。本文通过一个简单的Demo来介绍如何使用注解处理器。Demo链接为https://github.com"> |
83 | 83 | <meta name="keywords" content="Android">
|
84 | 84 | <meta property="og:type" content="article">
|
85 | 85 | <meta property="og:title" content="夏勇的个人博客">
|
86 | 86 | <meta property="og:url" content="http://yoursite.com/2019/01/04/安卓注解处理器-processor/index.html">
|
87 | 87 | <meta property="og:site_name" content="夏勇的个人博客">
|
88 |
| -<meta property="og:description" content="知耻而后勇,知弱而图强"> |
| 88 | +<meta property="og:description" content="1234title: 安卓注解处理器-processordate: 2019-01-04 19:02:34tags: annotation、processor、自动生成java文件category: 注解 最近在学习安卓开源框架发现,很多的开源框架都使用到了注解处理器,例如EventBus3.0。本文通过一个简单的Demo来介绍如何使用注解处理器。Demo链接为https://github.com"> |
89 | 89 | <meta property="og:locale" content="zh-Hans">
|
90 |
| -<meta property="og:updated_time" content="2019-01-04T11:09:33.772Z"> |
| 90 | +<meta property="og:image" content="http://yoursite.com/2019/01/04/安卓注解处理器-processor/框架流程.png"> |
| 91 | +<meta property="og:image" content="http://yoursite.com/2019/01/04/安卓注解处理器-processor/annotation.png"> |
| 92 | +<meta property="og:updated_time" content="2019-01-04T11:11:19.343Z"> |
91 | 93 | <meta name="twitter:card" content="summary">
|
92 | 94 | <meta name="twitter:title" content="夏勇的个人博客">
|
93 |
| -<meta name="twitter:description" content="知耻而后勇,知弱而图强"> |
| 95 | +<meta name="twitter:description" content="1234title: 安卓注解处理器-processordate: 2019-01-04 19:02:34tags: annotation、processor、自动生成java文件category: 注解 最近在学习安卓开源框架发现,很多的开源框架都使用到了注解处理器,例如EventBus3.0。本文通过一个简单的Demo来介绍如何使用注解处理器。Demo链接为https://github.com"> |
| 96 | +<meta name="twitter:image" content="http://yoursite.com/2019/01/04/安卓注解处理器-processor/框架流程.png"> |
94 | 97 |
|
95 | 98 |
|
96 | 99 |
|
@@ -367,7 +370,51 @@ <h1 class="post-title" itemprop="name headline"></h1>
|
367 | 370 |
|
368 | 371 |
|
369 | 372 |
|
370 |
| - |
| 373 | + <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">title: 安卓注解处理器-processor</span><br><span class="line">date: 2019-01-04 19:02:34</span><br><span class="line">tags: annotation、processor、自动生成java文件</span><br><span class="line">category: 注解</span><br></pre></td></tr></table></figure> |
| 374 | +<p>最近在学习安卓开源框架发现,很多的开源框架都使用到了注解处理器,例如EventBus3.0。本文通过一个简单的Demo来介绍如何使用注解处理器。Demo链接为<a href="https://github.com/cugyong/AnnotationProcessor-sample" target="_blank" rel="noopener">https://github.com/cugyong/AnnotationProcessor-sample</a> ,如果喜欢的话,欢迎大家给star。<a id="more"></a></p> |
| 375 | +<h2 id="Demo需求描述"><a href="#Demo需求描述" class="headerlink" title="Demo需求描述"></a>Demo需求描述</h2><p>用户通过执行一个传入参数为A(类对象)的静态方法,该方法会最终把参数A中加了特定注解的所有方法执行一遍。</p> |
| 376 | +<h2 id="需求实现"><a href="#需求实现" class="headerlink" title="需求实现"></a>需求实现</h2><p><img src="/2019/01/04/安卓注解处理器-processor/框架流程.png" alt="框架流程"></p> |
| 377 | +<p>整个项目分为四个部分:</p> |
| 378 | +<ul> |
| 379 | +<li>注解–要使用的注解类型,这部分通常也可以放在lib中;</li> |
| 380 | +<li>注解处理器–要对注解进行处理的逻辑,包括收集有特定注解类型的方法信息以及生成特定的java文件;</li> |
| 381 | +<li>lib–封装合适的接口,供具体调用方调用;</li> |
| 382 | +<li>sample–具体的调用方逻辑。</li> |
| 383 | +</ul> |
| 384 | +<p>首先新建一个安卓工程,点击运行展示的是hello world。</p> |
| 385 | +<h3 id="注解"><a href="#注解" class="headerlink" title="注解"></a>注解</h3><p>在上述工程中new->Module->Java Library,新建一个Java Library Module,命名为annotation。在该Module下创建一个文件AnnotationTest.java,</p> |
| 386 | +<p><img src="/2019/01/04/安卓注解处理器-processor/annotation.png" alt="1546591844724"></p> |
| 387 | +<p>AnnotationTest.java里面代码如下:</p> |
| 388 | +<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@Retention(RetentionPolicy.CLASS)</span><br><span class="line">@Target(ElementType.METHOD)</span><br><span class="line">public @interface AnnotationTest {</span><br><span class="line"> String name() default "test";</span><br><span class="line">}</span><br></pre></td></tr></table></figure> |
| 389 | +<p>1、注解@Retention按生命周期来划分可分为3类:</p> |
| 390 | +<ul> |
| 391 | +<li>RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;</li> |
| 392 | +<li>RetentionPolicy.CLASS:注解被保留到class文件,当jvm加载class文件时候被遗弃,这是默认的生命周期;</li> |
| 393 | +<li>RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。</li> |
| 394 | +</ul> |
| 395 | +<p>这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。</p> |
| 396 | +<p>2、注解@Target表示修饰的注解能使用的范围,ElementType.METHOD表示@AnnotationTest注解只能作用在方法上。</p> |
| 397 | +<h3 id="注解处理器"><a href="#注解处理器" class="headerlink" title="注解处理器"></a>注解处理器</h3><p>参照上部分,在工程中new->Module->Java Library,新建一个Java Library Module, 在该Module下创建一个文件ProcessorTest.java。在该Module下的build.gradle的dependencies中添加如下配置:</p> |
| 398 | +<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 自动为processor注册</span><br><span class="line">implementation 'com.google.auto.service:auto-service:1.0-rc2'</span><br><span class="line">// 该Module依赖上部分建立的annotation Module</span><br><span class="line">implementation project(':annotation')</span><br></pre></td></tr></table></figure> |
| 399 | +<p>com.google.auto.service:auto-service:1.0-rc2依赖的作用是为注解处理器自动注册,它会生成META-INF文件夹。</p> |
| 400 | +<p>注解处理器ProcessorTest的定义如下,其中@AutoService(Processor.class)就是build.gradle中加的依赖帮助其自动注册。</p> |
| 401 | +<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">@AutoService(Processor.class) // 自动为ProcessorTest注册,生成META-INF文件</span><br><span class="line">public class ProcessorTest extends AbstractProcessor{</span><br></pre></td></tr></table></figure> |
| 402 | +<p>注解处理器ProcessorTest主要包含以下几个部分:</p> |
| 403 | +<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public synchronized void init(ProcessingEnvironment processingEnvironment) {</span><br><span class="line"> super.init(processingEnvironment);</span><br><span class="line"></span><br><span class="line"> mMessager = processingEnvironment.getMessager();</span><br><span class="line"> mFiler = processingEnvironment.getFiler();</span><br><span class="line">}</span><br></pre></td></tr></table></figure> |
| 404 | +<p>init方法是注解处理器会自动调用的初始化方法,其中mFiler是用来生成java源文件的工具,mMessager是用来打印日志的,它们的具体使用会在后面介绍。</p> |
| 405 | +<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public Set<String> getSupportedAnnotationTypes() {</span><br><span class="line"> Set<String> supportAnnotationTypes = new HashSet<>();</span><br><span class="line"> supportAnnotationTypes.add(AnnotationTest.class.getCanonicalName());</span><br><span class="line"> return supportAnnotationTypes;</span><br><span class="line">}</span><br></pre></td></tr></table></figure> |
| 406 | +<p>getSupportedAnnotationTypes()方法返回该注解处理器支持的注解类型,这里返回的就是我们之前声明的新的注解类型@AnnotationTest。</p> |
| 407 | +<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public SourceVersion getSupportedSourceVersion() {</span><br><span class="line"> return SourceVersion.latestSupported();</span><br><span class="line">}</span><br></pre></td></tr></table></figure> |
| 408 | +<p>getSupportedSourceVersion()方法一般就按照上述实现就行。</p> |
| 409 | +<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {</span><br><span class="line"> // 打印日志</span><br><span class="line"> mMessager.printMessage(Diagnostic.Kind.NOTE, "process start");</span><br><span class="line"> Map<String, List<String>> collectInfos = new HashMap<>();</span><br><span class="line"> for (TypeElement annotation: annotations){</span><br><span class="line"> Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(annotation);</span><br><span class="line"> for (Element element: elements){</span><br><span class="line"> // 检查element是否符合我们定义的规范</span><br><span class="line"> if (!checkValid(element)){</span><br><span class="line"> mMessager.printMessage(Diagnostic.Kind.NOTE, "checkValid not pass");</span><br><span class="line"> return false;</span><br><span class="line"> }else {</span><br><span class="line"> ExecutableElement executableElement = (ExecutableElement) element;</span><br><span class="line"> // 获取被注解的方法所在的类</span><br><span class="line"> TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement();</span><br><span class="line"> // 获取类的全名,包括包名</span><br><span class="line"> String classFullName = typeElement.getQualifiedName().toString();</span><br><span class="line"> // 被注解的方法的名字</span><br><span class="line"> String methodName = executableElement.getSimpleName().toString();</span><br><span class="line"> List<String> methods = collectInfos.get(classFullName);</span><br><span class="line"> if (methods == null){</span><br><span class="line"> methods = new ArrayList<>();</span><br><span class="line"> collectInfos.put(classFullName, methods);</span><br><span class="line"> }</span><br><span class="line"> methods.add(methodName);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> for (Map.Entry<String, List<String>> entry: collectInfos.entrySet()){</span><br><span class="line"> mMessager.printMessage(Diagnostic.Kind.NOTE, entry.getKey());</span><br><span class="line"> // 生成java源文件</span><br><span class="line"> createJavaFile(entry.getKey(), entry.getValue());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return true;</span><br><span class="line">}</span><br></pre></td></tr></table></figure> |
| 410 | +<p>process方法是我们的主要逻辑处理的地方,主要逻辑就是收集所有有@AnnotationTest注解的方法以及其所在的类信息,然后根据每个类信息,生成一个新的类文件,并在新的类文件的特定方法中调用所有关联的注解方法。生成java源文件将使用Filer对象,具体如何使用请下载demo看源代码。</p> |
| 411 | +<p><strong>注:</strong> </p> |
| 412 | +<p>1、当你点击buid project时,注解处理器将会执行,而Messager对象打印出来的日志信息可以在Gradle Console窗口中看到。</p> |
| 413 | +<p>2、如果你在该Module中使用中文注解,因为该Module为java library,可能会报GBK编码错误,解决办法是在该Module的build.gradle中添加如下代码:</p> |
| 414 | +<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">//指定编译的编码</span><br><span class="line">tasks.withType(JavaCompile){</span><br><span class="line"> options.encoding = "UTF-8"</span><br><span class="line">}</span><br></pre></td></tr></table></figure> |
| 415 | +<h3 id="Lib"><a href="#Lib" class="headerlink" title="Lib"></a>Lib</h3><p>在工程中new->Module->Android Library ,新建一个Android Library Module,封装接口给调用方使用,具体逻辑请参考demo。</p> |
| 416 | +<p>最终该demo的功能是点击Hello world文字,会依此执行MainActivity中使用@AnnotationTest注解的方法。</p> |
| 417 | + |
371 | 418 |
|
372 | 419 | </div>
|
373 | 420 |
|
@@ -435,8 +482,17 @@ <h1 class="post-title" itemprop="name headline"></h1>
|
435 | 482 |
|
436 | 483 |
|
437 | 484 |
|
| 485 | + <ul class="sidebar-nav motion-element"> |
| 486 | + <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap"> |
| 487 | + 文章目录 |
| 488 | + </li> |
| 489 | + <li class="sidebar-nav-overview" data-target="site-overview-wrap"> |
| 490 | + 站点概览 |
| 491 | + </li> |
| 492 | + </ul> |
| 493 | + |
438 | 494 |
|
439 |
| - <section class="site-overview-wrap sidebar-panel sidebar-panel-active"> |
| 495 | + <section class="site-overview-wrap sidebar-panel"> |
440 | 496 | <div class="site-overview">
|
441 | 497 | <div class="site-author motion-element" itemprop="author" itemscope="" itemtype="http://schema.org/Person">
|
442 | 498 |
|
@@ -481,6 +537,22 @@ <h1 class="post-title" itemprop="name headline"></h1>
|
481 | 537 | </section>
|
482 | 538 |
|
483 | 539 |
|
| 540 | + <!--noindex--> |
| 541 | + <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"> |
| 542 | + <div class="post-toc"> |
| 543 | + |
| 544 | + |
| 545 | + |
| 546 | + |
| 547 | + |
| 548 | + |
| 549 | + <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#Demo需求描述"><span class="nav-text">Demo需求描述</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#需求实现"><span class="nav-text">需求实现</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#注解"><span class="nav-text">注解</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#注解处理器"><span class="nav-text">注解处理器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Lib"><span class="nav-text">Lib</span></a></li></ol></li></ol></div> |
| 550 | + |
| 551 | + |
| 552 | + </div> |
| 553 | + </section> |
| 554 | + <!--/noindex--> |
| 555 | + |
484 | 556 |
|
485 | 557 |
|
486 | 558 |
|
|
0 commit comments