<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] # ButterKnife源碼分析 > 基于 ButterKnife 8.8.0版本進行分析 ## ButterKnife 使用 ### 依賴說明 ``` dependencies { implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' } ``` ### 簡單使用 ```java class ExampleActivity extends Activity { @BindView(R.id.user) EditText username; @BindView(R.id.pass) EditText password; @BindString(R.string.login_error) String loginErrorMessage; @OnClick(R.id.submit) void submit() { // TODO call server... } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); // TODO Use fields... } } ``` ### 多模塊使用 > 在Library 中使用ButterKnife 以上操作是無法使用的 ,我們還需要借助`Butterknife插件`來實現功能 在您的**root build.gradle**文件中buildscript: ``` buildscript { repositories { mavenCentral() google() } dependencies { classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1' } } ``` #### 添加插件 apply ``` apply plugin: 'com.android.library' apply plugin: 'com.jakewharton.butterknife' ``` #### 插件使用Butterknife ``` class ExampleActivity extends Activity { @BindView(R2.id.user) EditText username; @BindView(R2.id.pass) EditText password; ... } ``` ### 問題說明 * 在Library中無法使用在注解中無法使用R的資源 > 原因很簡單,因為Lib模塊中 java注解無法使用變量 然而lib生成的R 文件資源都是 `public static` 不是 `public static final ` 簡單的說lib中生成的R文件不是常量 編譯器將會報錯:Attribute value must be constant ,如下圖: ![](https://ws1.sinaimg.cn/large/882b6a2aly1g0h9wcdpzcj20d002c755.jpg) [Butterknife 官網簡單使用說明](http://jakewharton.github.io/butterknife/) ## ButterKnife源碼分析 ### [源碼地址](https://github.com/JakeWharton/butterknife) ### 源碼module結構 #### 組件依賴關系 > ButterKnife 共7個組件,他們的依賴關系如下圖所示 butterknife-integration-test;該項目的測試用例--不做介紹 ![](http://ww1.sinaimg.cn/large/882b6a2aly1fznagq9al6j20iv0bwt8n.jpg) + butterknife:這個工程提供了 ButterKnife.bind(this),這是 ButterKnife 對外提供的門面。也是運行時,觸發 Activity 中 View 控件綁定的時機,提供android使用的API。 + butterknife-compiler:java-model 編譯期間將使用該工程,他的作用是解析注解,并且生成 Activity 中 View 綁定的 Java 文件。 + butterknife-annotations:java-model 將所有自定義的注解放在此工程下, 確保職責的單一。 + butterknife-gradle-plugin:gradle 插件,這是8.2.0版本起為了支持 library 工程而新增的一個插件工程。 + butterknife-lint:針對 butterknife-gradle-plugin 而做的靜態代碼檢查工具,非常有態度的一種做法,在下文做詳細介紹。 ### 編譯 生成Java代碼 > 前提以上基本用法已經加入工程 ##### 簡單使用 ```java public class MainActivity extends AppCompatActivity { @BindView(R.id.btn1) Button btn; @BindView(R.id.tv1) TextView tv; @OnClick(R.id.btn1) public void btnClick() { Toast.makeText(this, "Butterknfie 簡單使用", Toast.LENGTH_SHORT).show(); } } ``` #### Butterknife 之 編譯期 > android-apt(Annotation Processing Tool) ,在Java代碼的編譯時期,javac 會調用java注解處理器來生成輔助代碼。生成的代碼就在 `build/generated/source/apt` ```java public class MainActivity_ViewBinding<T extends MainActivity> implements Unbinder { protected T target; private View view2131427416; @UiThread public MainActivity_ViewBinding(final T target, View source) { this.target = target; View view; view = Utils.findRequiredView(source, R.id.btn1, "field 'btn' and method 'btnClick'"); //這里的 target 其實就是我們的 Activity //這個castView就是將得到的View轉化成具體的子View target.btn = Utils.castView(view, R.id.btn1, "field 'btn'", Button.class); view2131427416 = view; //為按鈕設置點擊事件 view.setOnClickListener(new DebouncingOnClickListener() { @Override public void doClick(View p0) { target.btnClick(); } }); target.tv = Utils.findRequiredViewAsType(source, R.id.tv1, "field 'tv'", TextView.class); } @Override @CallSuper public void unbind() { T target = this.target; if (target == null) throw new IllegalStateException("Bindings already cleared."); target.btn = null; target.tv = null; view2131427416.setOnClickListener(null); view2131427416 = null; this.target = null; } ``` * Utils.findRequiredView 方法的封裝 ```java // Utils.findRequiredView View view = source.findViewById(id); if (view != null) { return view; } ``` ### Butterknife 之 android-apt(Annotation Processing Tool) > APT(Annotation Processing Tool),即注解處理工具。在該方案中,通常有個必備的三件套,分別是注解處理器 Processor,注冊注解處理器 AutoService 和代碼生成工具 JavaPoet。 #### 注解處理器 Processor > ButterKnife 一切皆注解,因此首先需要個處理器來解析注解。 ButterKnifeProcessor 充當了該角色,其中 process 方法是觸發注解解析的入口,所有的神奇的事情從這里發生。 process 方法中主要做兩件事情,分別是: + 解析所有包含了 ButterKnife 注解的類 + 根據解析結果,使用 JavaPoet 生成相應的Java文件 ![ButterKnifeProcessor#process源碼](https://ws1.sinaimg.cn/large/882b6a2aly1g0hbw37sx4j214k0j8n0q.jpg) `findAndParseTargets(env)` 中解析注解的代碼非常冗長,依次對 `@BindArray` 、`@BindColor`、`@BindString`、`@BindView` 等注解進行解析,解析結果存放在 bindingMap 中。 這里重點關注下 bindingMap 的鍵值對。key 值為 TypeElement 對象 ,可以簡單的理解為被解析的類本身,而 value 值為 BindingSet 對象,該對象存放了解析結果,根據該結果,JavaPoet 將生成不同的 Java 文件, 以官方 sample 為例,其映射關系如下: | key | value |JavaPoet 根據 value 生成的文件 | | ------| ----- | ------| |SimpleActivity |BindingSet |SimpleActivity_ViewBinding.java |SimpleAdapter |BindingSet |SimpleAdapter$ViewHolder_ViewBinding.java ![](https://ws1.sinaimg.cn/large/882b6a2aly1g0hc6tnm3rj21340n4n7y.jpg) #### 注冊注解處理器 [AutoService](https://link.jianshu.com/?t=https://github.com/google/auto/tree/master/service) > 定義完注解處理器后,還需要告訴編譯器該注解處理器的信息,需在 `src/main/resource/META-INF/service` 目錄下增加 `javax.annotation.processing.Processor` 文件,并將注解處理器的類名配置在該文件中。 整個過程比較繁瑣,Google 為我們提供了更便利的工具,叫 `AutoService`,此時只需要為注解處理器增加 `@AutoService` 注解就可以了,如下: ```java @AutoService(Processor.class) public final class ButterKnifeProcessor extends AbstractProcessor { } ``` [注] [AutoService使用的是`java.util.ServiceLoader`] #### Java編寫器 [JavaPoet](https://github.com/square/javapoet) > 了解 JavaPoet ,最好的方式便是看[官方文檔](https://github.com/square/javapoet)。簡而言之,當我們寫一個類時,其實是有固定結構的,JavaPoet 提供了生成這些結構的 api 舉例如下: + 類:TypeSpec.classBuilder() + 構造器:MethodSpec.constructorBuilder() + 方法:MethodSpec.methodBuilder() + 參數:ParameterSpec.builder() + 屬性:FieldSpec.builder() + 程序片段:CodeBlock.builder() 以 ButterKnife 而言,他做的事情便是將注解處理器解析后的結果(實際上就是上文提到的 BindingSet 對象)生成 Activity_ViewBinding.java,該對象負責綁定 Activity 中的 View 控件以及設置監聽器等。 那么 JavaPoet 是如何處理的?實際上 ButterKnife 會將上文提到的 BindingSet 轉換成類似于下文所示的代碼: 示例如下: ```java // 創建類 TypeSpec typeSpec = TypeSpec.classBuilder("TestActivity_ViewBinding") .addModifiers(PUBLIC) // 類為public .addSuperinterface(UNBINDER) // 類為Unbinder的實現類 .addField(targetField) // 生成屬性 private TestActivity target .addMethod(constructorForActivity) // 生成構造器1 .addMethod(otherConstructor) // 生成構造器2 .addMethod(unBindeMethod) // 生成unbind()方法 .build(); // 生成 Java 文件 JavaFile javaFile = JavaFile.builder("com.zdg", typeSpec)//包名和類 .addFileComment("Generated code from Butter Knife. Do not modify!") .build(); javaFile.writeTo(System.out); ``` 最后總結下這三件套的協作流程,如下圖: ![](https://ws1.sinaimg.cn/large/882b6a2aly1g0hcpb81duj20bd0it41b.jpg) ### Butterknife 之 運行期 > 接下來我們來分析下運行期間發生的事情,相比于編譯期間,運行期間的邏輯簡單了許多。繼續使用上面的Demo例子 運行時的入口在于 `ButterKnife.bind(this)`,追溯源碼發現,最終將會執行以下邏輯: ```java // 最終將找到 SimpleActivity_ViewBinding 的構造器,并實例化 Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass); constructor.newInstance(target, source); ``` 也就是說 ButterKnife.bind(this) 等價于如下代碼: ```java View sourceView = activity.getWindow().getDecorView(); new SimpleActivity_ViewBinding(activity,sourceView); ``` 注:雖然這里使用了反射,但源碼中將 `Class.forName` 的結果緩存起來后再通過 `newInstance` 創建實例,避免重復加載類,提升性能。 編譯期間和運行期間相輔相成,這便是 android-apt 的普遍套路。 ### Library > 編譯時和運行時的問題解決了,還有最后一個問題:由 R 生成 R2 的意義是什么? 如果你細心的話會發現在官方的 sample-library 中,注解的值均是由 R2 來引用的,如下圖: ![](https://ws1.sinaimg.cn/large/882b6a2aly1g0hd2kvbtsj20ik041q62.jpg) 如果非 library 工程,則仍然引用系統生成的 R 文件。所以可以猜測:R2 的誕生是為 library 工程量身打造的。 `在上面我說過R文件的問題,library中生成的R文件資源文件不是常量 無法使用注解` `JakeWharton大神`他是怎么解決這個問題呢??? > 既然 R 不能滿足要求,那就自己構建一個 R2,由 R 復制而來,并且將其屬性都修改為 public static final 來修飾的常量。為了讓使用者對整個過程無感知,因此使用 gradle 插件來解決這個需求,這也是 `butterknife-gradle-plugin` 工程的由來。 #### butterknife-gradle-plugin `butterknife-gradle-plugin` 有兩個重要的第三方依賴,分別是 `javaparser` 和 `javapoet` ,前者用于解析 Java 文件,也就是解析 R 文件,后者用于將解析結果生成 R2 文件。 整個插件工程的源碼并不難理解,在生成 R2 文件時,要將屬性定義成 public static final ,在源碼中我們可以看到此邏輯,在 FinalRClassBuilder.addResourceField() 中 : ```java FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(int.class, fieldName) .addModifiers(PUBLIC, STATIC, FINAL) .initializer(fieldValue); ``` `butterknife` 插件在 `processResources` 的 Task 中執行,該任務通常用來完成文件的 copy。 有關插件的編寫 大家可以查看其他插件編寫教程 #### JakeWharton 給ButterKnife 的情懷和態度------butterknife-lint >一個靜態代碼檢查工具,用來驗證非法的 R2 引用。一旦在我們的業務項目里不小心引用了 R2 文件, 當執行 Lint 后,將會有如下圖的提示信息: ![](https://ws1.sinaimg.cn/large/882b6a2aly1g0hdkrf91gj21mu0bswmk.jpg) [參考文章](https://www.jianshu.com/p/b8b59fb80554)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看