1. 程式人生 > >Android 如何編寫基於編譯時註解的專案---轉載張鴻洋博文

Android 如何編寫基於編譯時註解的專案---轉載張鴻洋博文

一、概述

Android應用開發中,我們常常為了提升開發效率會選擇使用一些基於註解的框架,但是由於反射造成一定執行效率的損耗,所以我們會更青睞於編譯時註解的框架,例如:

  • butterknife免去我們編寫View的初始化以及事件的注入的程式碼。
  • fragmentargs輕鬆的為fragment新增引數資訊,並提供建立方法。
  • ParcelableGenerator可實現自動將任意物件轉換為Parcelable型別,方便物件傳輸。

類似的庫還有非常多,大多這些的庫都是為了自動幫我們完成日常編碼中需要重複編寫的部分(例如:每個Activity中的View都需要初始化,每個實現Parcelable介面的物件都需要編寫很多固定寫法的程式碼)。

這裡並不是說上述框架就一定沒有使用反射了,其實上述其中部分框架內部還是有部分實現是依賴於反射的,但是很少而且一般都做了快取的處理,所以相對來說,效率影響很小。

但是在使用這類專案的時候,有時候出現錯誤會難以除錯,主要原因還是很多使用者並不瞭解這類框架其內部的原理,所以遇到問題時會消耗大量的時間去排查。

那麼,於情於理,在編譯時註解框架這麼火的時刻,我們有理由去學習:

  • 如何編寫一個機遇編譯時註解的專案

首先,是為了瞭解其原理,這樣在我們使用類似框架遇到問題的時候,能夠找到正確的途徑去排查問題;其次,我們如果有好的想法,發現某些程式碼需要重複建立,我們也可以自己來寫個框架方便自己日常的編碼,提升編碼效率;最後也算是自身技術的提升。

注:以下使用IDE為Android Studio.

本文將以編寫一個View注入的框架為線索,詳細介紹編寫此類框架的步驟。

二、編寫前的準備

在編寫此類框架的時候,一般需要建立多個module,例如本文即將實現的例子:

  • ioc-annotation 用於存放註解等,Java模組
  • ioc-compiler 用於編寫註解處理器,Java模組
  • ioc-api 用於給使用者提供使用的API,本例為Andriod模組
  • ioc-sample 示例,本例為Andriod模組

那麼除了示例以為,一般要建立3個module,module的名字你可以自己考慮,上述給出了一個簡單的參考。當然如果條件允許的話,有的開發者喜歡將存放註解和API這兩個module合併為一個module。

對於module間的依賴,因為編寫註解處理器需要依賴相關注解,所以:

ioc-compiler依賴ioc-annotation

我們在使用的過程中,會用到註解以及相關API

所以ioc-sample依賴ioc-api;ioc-api依賴ioc-annotation

三、註解模組的實現

註解模組,主要用於存放一些註解類,本例是模仿butterknife實現View注入,所以本例只需要一個註解類:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Retention</span>(RetentionPolicy.CLASS)
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Target</span>(ElementType.FIELD)
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> @<span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">interface</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">BindView</span>
{</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> value();
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

我們設定的保留策略為Class,註解用於Field上。這裡我們需要在使用時傳入一個id,直接以value的形式進行設定即可。

你在編寫的時候,分析自己需要幾個註解類,並且正確的設定@Target以及@Retention即可。

四、註解處理器的實現

定義完成註解後,就可以去編寫註解處理器了,這塊有點複雜,但是也算是有章可循的。

該模組,我們一般會依賴註解模組,以及可以使用一個auto-service

build.gradle的依賴情況如下:

<code class="language-xml hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">dependencies {
    compile 'com.google.auto.service:auto-service:1.0-rc2'
    compile project (':ioc-annotation')
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

auto-service庫可以幫我們去生成META-INF等資訊。

(1)基本程式碼

註解處理器一般繼承於AbstractProcessor,剛才我們說有章可循,是因為部分程式碼的寫法基本是固定的,如下:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@AutoService</span>(Processor.class)
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">IocProcessor</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">AbstractProcessor</span>{</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Filer mFileUtils;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Elements mElementUtils;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Messager mMessager;
    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">init</span>(ProcessingEnvironment processingEnv){
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.init(processingEnv);
        mFileUtils = processingEnv.getFiler();
        mElementUtils = processingEnv.getElementUtils();
        mMessager = processingEnv.getMessager();
    }
    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Set<String> <span class="hljs-title" style="box-sizing: border-box;">getSupportedAnnotationTypes</span>(){
        Set<String> annotationTypes = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> LinkedHashSet<String>();
        annotationTypes.add(BindView.class.getCanonicalName());
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> annotationTypes;
    }
    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> SourceVersion <span class="hljs-title" style="box-sizing: border-box;">getSupportedSourceVersion</span>(){
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> SourceVersion.latestSupported();
    }
    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">process</span>(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>

在實現AbstractProcessor後,process()方法是必須實現的,也是我們編寫程式碼的核心部分,後面會介紹。

我們一般會實現getSupportedAnnotationTypes()getSupportedSourceVersion()兩個方法,這兩個方法一個返回支援的註解型別,一個返回支援的原始碼版本,參考上面的程式碼,寫法基本是固定的。

除此以外,我們還會選擇複寫init()方法,該方法傳入一個引數processingEnv,可以幫助我們去初始化一些輔助類:

  • Filer mFileUtils; 跟檔案相關的輔助類,生成JavaSourceCode.
  • Elements mElementUtils;跟元素相關的輔助類,幫助我們去獲取一些元素相關的資訊。
  • Messager mMessager;跟日誌相關的輔助類。

這裡簡單提一下Elemnet,我們簡單認識下它的幾個子類,根據下面的註釋,應該已經有了一個簡單認知。

<code class="hljs haml has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">Element 
  -<span class="ruby" style="box-sizing: border-box;"> <span class="hljs-constant" style="box-sizing: border-box;">VariableElement</span> /<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">/一般代表成員變數
</span></span>  -<span class="ruby" style="box-sizing: border-box;"> <span class="hljs-constant" style="box-sizing: border-box;">ExecutableElement</span> /<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">/一般代表類中的方法
</span></span>  -<span class="ruby" style="box-sizing: border-box;"> <span class="hljs-constant" style="box-sizing: border-box;">TypeElement</span> /<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">/一般代表代表類
</span></span>  -<span class="ruby" style="box-sizing: border-box;"> <span class="hljs-constant" style="box-sizing: border-box;">PackageElement</span> /<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">/一般代表Package</span></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>

(2)process的實現

process中的實現,相比較會比較複雜一點,一般你可以認為兩個大步驟:

  • 收集資訊
  • 生成代理類(本文把編譯時生成的類叫代理類)

什麼叫收集資訊呢?就是根據你的註解宣告,拿到對應的Element,然後獲取到我們所需要的資訊,這個資訊肯定是為了後面生成JavaFileObject所準備的。

例如本例,我們會針對每一個類生成一個代理類,例如MainActivity我們會生成一個MainActivity$$ViewInjector。那麼如果多個類中聲明瞭註解,就對應了多個類,這裡就需要:

  • 一個類物件,代表具體某個類的代理類生成的全部資訊,本例中為ProxyInfo
  • 一個集合,存放上述類物件(到時候遍歷生成代理類),本例中為Map<String, ProxyInfo>,key為類的全路徑。

這裡的描述有點模糊沒關係,一會結合程式碼就好理解了。

a.收集資訊

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Map<String, ProxyInfo> mProxyMap = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> HashMap<String, ProxyInfo>();
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">process</span>(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
    mProxyMap.clear();
    Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//一、收集資訊</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Element element : elements){
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//檢查element型別</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!checkAnnotationUseValid(element)){
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
        }
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//field type</span>
        VariableElement variableElement = (VariableElement) element;
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//class type</span>
        TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//TypeElement</span>
        String qualifiedName = typeElement.getQualifiedName().toString();

        ProxyInfo proxyInfo = mProxyMap.get(qualifiedName);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (proxyInfo == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>){
            proxyInfo = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ProxyInfo(mElementUtils, typeElement);
            mProxyMap.put(qualifiedName, proxyInfo);
        }
        BindView annotation = variableElement.getAnnotation(BindView.class);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> id = annotation.value();
        proxyInfo.mInjectElements.put(id, variableElement);
    }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li></ul>

首先我們呼叫一下mProxyMap.clear();,因為process可能會多次呼叫,避免生成重複的代理類,避免生成類的類名已存在異常。

然後,通過roundEnv.getElementsAnnotatedWith拿到我們通過@BindView註解的元素,這裡返回值,按照我們的預期應該是VariableElement集合,因為我們用於成員變數上。

接下來for迴圈我們的元素,首先檢查型別是否是VariableElement.

然後拿到對應的類資訊TypeElement,繼而生成ProxyInfo物件,這裡通過一個mProxyMap進行檢查,key為qualifiedName即類的全路徑,如果沒有生成才會去生成一個新的,ProxyInfo與類是一一對應的。

接下來,會將與該類對應的且被@BindView宣告的VariableElement加入到ProxyInfo中去,key為我們宣告時填寫的id,即View的id。

這樣就完成了資訊的收集,收集完成資訊後,應該就可以去生成代理類了。

b.生成代理類

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">process</span>(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//...省略收集資訊的程式碼,以及try,catch相關</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>(String key : mProxyMap.keySet()){
        ProxyInfo proxyInfo = mProxyMap.get(key);
        JavaFileObject sourceFile = mFileUtils.createSourceFile(
                proxyInfo.getProxyClassFullName(), proxyInfo.getTypeElement());
            Writer writer = sourceFile.openWriter();
            writer.write(proxyInfo.generateJavaCode());
            writer.flush();
            writer.close();
    }
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li></ul>

可以看到生成代理類的程式碼非常的簡短,主要就是遍歷我們的mProxyMap,然後取得每一個ProxyInfo,最後通過mFileUtils.createSourceFile來建立檔案物件,類名為proxyInfo.getProxyClassFullName(),寫入的內容為proxyInfo.generateJavaCode().

看來生成Java程式碼的方法都在ProxyInfo裡面。

c.生成Java程式碼

這裡我們主要關注其生成Java程式碼的方式。 
下面主要看生成Java程式碼的方法:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">#ProxyInfo
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//key為id,value為對應的成員變數</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Map<Integer, VariableElement> mInjectElements = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> HashMap<Integer, VariableElement>();

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> String <span class="hljs-title" style="box-sizing: border-box;">generateJavaCode</span>(){
    StringBuilder builder = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> StringBuilder();
    builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"package "</span> + mPackageName).append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">";\n\n"</span>);
    builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"import com.zhy.ioc.*;\n"</span>);
    builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"public class "</span>).append(mProxyClassName).append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" implements "</span> + SUFFIX + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"<"</span> + mTypeElement.getQualifiedName() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">">"</span>);
    builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n{\n"</span>);
    generateMethod(builder);
    builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n}\n"</span>);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> builder.toString();
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">generateMethod</span>(StringBuilder builder){
     builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"public void inject("</span>+mTypeElement.getQualifiedName()+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" host , Object object )"</span>);
    builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n{\n"</span>);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> id : mInjectElements.keySet()){
        VariableElement variableElement = mInjectElements.get(id);
        String name = variableElement.getSimpleName().toString();
        String type = variableElement.asType().toString() ;

        builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" if(object instanceof android.app.Activity)"</span>);
        builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n{\n"</span>);
        builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"host."</span>+name).append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" = "</span>);
        builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"("</span>+type+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">")(((android.app.Activity)object).findViewById("</span>+id+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"));"</span>);
        builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n}\n"</span>).append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"else"</span>).append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n{\n"</span>);
        builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"host."</span>+name).append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" = "</span>);
        builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"("</span>+type+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">")(((android.view.View)object).findViewById("</span>+id+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"));"</span>);
        builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n}\n"</span>);
    }
    builder.append(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n}\n"</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li></ul>

這裡主要就是靠收集到的資訊,拼接完成的代理類物件了,看起來會比較頭疼,不過我給出一個生成後的程式碼,對比著看會很多。

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> com.zhy.ioc_sample;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.zhy.ioc.*;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MainActivity</span>$$<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewInjector</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">implements</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewInjector</span><<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">com</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">zhy</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ioc_sample</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MainActivity</span>>{</span>
    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">inject</span>(com.zhy.sample.MainActivity host , Object object ){
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(object <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">instanceof</span> android.app.Activity){
            host.mTv = (android.widget.TextView)(((android.app.Activity)object).findViewById(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2131492945</span>));
        }
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{
            host.mTv = (android.widget.TextView)(((android.view.View)object).findViewById(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2131492945</span>));
        }
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>

這樣對著上面程式碼看會好很多,其實就死根據收集到的成員變數(通過@BindView宣告的),然後根據我們具體要實現的需求去生成java程式碼。

這裡注意下,生成的程式碼實現了一個介面ViewInjector<T>,該介面是為了統一所有的代理類物件的型別,到時候我們需要強轉代理類物件為該介面型別,呼叫其方法;介面是泛型,主要就是傳入實際類物件,例如MainActivity,因為我們在生成代理類中的程式碼,實際上就是實際類.成員變數的方式進行訪問,所以,使用編譯時註解的成員變數一般都不允許private修飾符修飾(有的允許,但是需要提供getter,setter訪問方法)。

這裡採用了完全拼接的方式編寫Java程式碼,你也可以使用一些開源庫,來通過Java api的方式來生成程式碼,例如:

A Java API for generating .java source files.

到這裡我們就完成了代理類的生成,這裡任何的註解處理器的編寫方式基本都遵循著收集資訊、生成代理類的步驟。

五、API模組的實現

有了代理類之後,我們一般還會提供API供使用者去訪問,例如本例的訪問入口是

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//Activity中</span>
 Ioc.inject(Activity);
 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//Fragment中,獲取ViewHolder中</span>
 Ioc.inject(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, view);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

模仿了butterknife,第一個引數為宿主物件,第二個引數為實際呼叫findViewById的物件;當然在Actiivty中,兩個引數就一樣了。

  • API一般如何編寫呢?

其實很簡單,只要你瞭解了其原理,這個API就幹兩件事:

  • 根據傳入的host尋找我們生成的代理類:例如MainActivity->MainActity$$ViewInjector
  • 強轉為統一的介面,呼叫介面提供的方法。

這兩件事應該不復雜,第一件事是拼接代理類名,然後反射生成物件,第二件事強轉呼叫。

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> &l