<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 概述 * 編寫代碼時對目標元素進行注解 * 編譯時注解處理器會掃描注解,并通過 JavaPoet 自動生成 Java 文件 * 調用 ButterKnife.bind()方法時,會通過反射拿到之前為目標類生成的 TargetClassName_ViewBinding 類,并調用其構造方法,完成綁定 # 具體分析 ## 源碼組成 ButterKnife 的源碼主要的組成部分包括三個:butterknife、butterknife-annotations、butterknife-compiler。其中 butterknife-annotations 聲明了所有的注解;butterknife-compiler 為注解處理器,在編譯階段對注解進行處理;butterknife 則是我們調用的接口。接下來我們一一來看。 ## butterknife-annotations ButterKnife 提供了如下注解: * 綁定類型的注解:BindAnim、BindArray、BindBitmap、BindBool、BindColor、BindDimen、BindDrawable、BindFloat、BindFont、BindInt、BindString、BindView、BindViews * 監聽類型注解:OnCheckedChanged、OnClick、OnEditorAction、OnFocusChange、OnItemClick、OnItemLongClick、OnItemSelected、OnLongClick、OnPageChange、OnTextChanged、OnTouch、Optional ## butterknife-compiler 復習下在 EventBus 源碼分析時用到的注解處理器: 注解處理器用來在編譯時掃描和處理注解,會自動生成 .java 文件。 每一個注解處理器都繼承于 AbstractProcessor, ```java package com.example; public class MyProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment env){ } @Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { } @Override public Set<String> getSupportedAnnotationTypes() { } @Override public SourceVersion getSupportedSourceVersion() { } } ``` 其中必需的幾個方法如下: * init(ProcessingEnvironment env):會被外部的工具調用,傳入 ProcessingEnvironment 參數,我們可以從其中獲取到一些工具類 * process():在這里掃描、處理注解以及生成代碼 * getSupportedAnnotationsTypes():在這里定義本注解處理器會注冊到哪些注解上 * getSupportedSourceVersion():指定 Java 版本 那么首先來看看在 butterknife-compiler 中的關鍵類:ButterknifeProcessor。 ```java public final class ButterKnifeProcessor extends AbstractProcessor { // 初始化工具 @Override public synchronized void init(ProcessingEnvironment env) { super.init(env); ... elementUtils = env.getElementUtils(); typeUtils = env.getTypeUtils(); filer = env.getFiler(); } // 定義所有注解 @Override public Set<String> getSupportedAnnotationTypes() { Set<String> types = new LinkedHashSet<>(); for (Class<? extends Annotation> annotation : getSupportedAnnotations()) { types.add(annotation.getCanonicalName()); } return types; } // 在這里看到了前面聲明的所有注解 private Set<Class<? extends Annotation>> getSupportedAnnotations() { Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>(); annotations.add(BindColor.class); annotations.add(BindView.class); ... annotations.addAll(LISTENERS); return annotations; } // 對注解進行處理 @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {} } ``` ### process 方法 接下來我們重點看看 process 方法,也就是注解處理器是如何對注解進行操作的。 ```java // 對注解進行處理 @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) { // 掃描注解并解析,得到 Map Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env); // 生成 Java 文件 for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) { TypeElement typeElement = entry.getKey(); BindingSet binding = entry.getValue(); JavaFile javaFile = binding.brewJava(sdk, debuggable); try { javaFile.writeTo(filer); } catch (IOException e) { error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage()); } } return false; } ``` 通過參數 RoundEmviroment 我們可以拿到所有包含特定注解的被注解元素,生成一個 Key 為 TypeElement,Value 為 BindingSet 的 Map;接著遍歷 Map 生成相應的 Java 文件。 先來看看掃描注解并解析的源碼: ```java private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) { ... // Process each @BindAnim element. // 遍歷所有被注解的元素 for (Element element : env.getElementsAnnotatedWith(BindAnim.class)) { try { parseResourceAnimation(element, builderMap, erasedTargetNames); } catch (Exception e) { ... } } ... // Process each annotation that corresponds to a listener. for (Class<? extends Annotation> listener : LISTENERS) { findAndParseListener(env, listener, builderMap, erasedTargetNames); } // 把之前的 builderMap 轉換為 bindingMap ... while (!entries.isEmpty()) { Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst(); TypeElement type = entry.getKey(); BindingSet.Builder builder = entry.getValue(); TypeElement parentType = findParentType(type, erasedTargetNames); if (parentType == null) { bindingMap.put(type, builder.build()); } else { BindingSet parentBinding = bindingMap.get(parentType); if (parentBinding != null) { builder.setParent(parentBinding); bindingMap.put(type, builder.build()); } else { // Has a superclass binding but we haven't built it yet. Re-enqueue for later. entries.addLast(entry); } } } return bindingMap; } ``` 中間省略了一些注解的處理,每種注解處理方式基本一致,以 BindAnim 為例,其中的關鍵方法為 parseResourceAnimation,其他注解的該方法分別對應著(parseBindView、parseResourceString 等) ```java private void parseResourceAnimation(Element element, ... // Assemble information on the field. BindingSet.Builder builder = getOrCreateBindingBuilder(builderMap, enclosingElement); builder.addResource(new FieldAnimationBinding(getId(qualifiedId), name)); erasedTargetNames.add(enclosingElement); } ``` parseResourceAnimation 的作用為生成被注解元素所對應類的 Builder,并得到 builderMap。 接下來看看生成 Java 文件的代碼: ```java for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) { TypeElement typeElement = entry.getKey(); BindingSet binding = entry.getValue(); JavaFile javaFile = binding.brewJava(sdk, debuggable); try { javaFile.writeTo(filer); } catch (IOException e) { error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage()); } } ``` 這里使用 JavaPoet 生成了代碼。 源碼分析未完待續! 來看看 JavaPoet 為我們生成的 Java 文件(示例中 MainActivity 中有兩個點擊事件綁定): ```java public class MainActivity_ViewBinding implements Unbinder { private MainActivity target; private View view2131230756; private View view2131230758; @UiThread public MainActivity_ViewBinding(MainActivity target) { this(target, target.getWindow().getDecorView()); } @UiThread public MainActivity_ViewBinding(final MainActivity target, View source) { this.target = target; View view; view = Utils.findRequiredView(source, R.id.btn_alipay, "method 'onViewClicked'"); view2131230756 = view; view.setOnClickListener(new DebouncingOnClickListener() { @Override public void doClick(View p0) { target.onViewClicked(p0); } }); view = Utils.findRequiredView(source, R.id.btn_custom_view, "method 'onViewClicked'"); view2131230758 = view; view.setOnClickListener(new DebouncingOnClickListener() { @Override public void doClick(View p0) { target.onViewClicked(p0); } }); } @Override @CallSuper public void unbind() { if (target == null) throw new IllegalStateException("Bindings already cleared."); target = null; view2131230756.setOnClickListener(null); view2131230756 = null; view2131230758.setOnClickListener(null); view2131230758 = null; } } ``` 在構造方法中,可以看到我們熟悉的點擊事件,View 的綁定同理。 ## butterknife 在 ButterKnife 的標準用法中,會在 Activity 或 Fragment 的 onCreate 方法中調用 `ButterKnife.bind(this);` 進行綁定,我們就首先從 bind 方法入手。 ```java public static Unbinder bind(@NonNull Activity target) { View sourceView = target.getWindow().getDecorView(); return createBinding(target, sourceView); } private static Unbinder createBinding(@NonNull Object target, @NonNull View source) { Class<?> targetClass = target.getClass(); Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass); //noinspection TryWithIdenticalCatches Resolves to API 19+ only type. try { return constructor.newInstance(target, source); } catch (IllegalAccessException e) { ... } } ``` 可以看到,通過反射首先拿到 target 的類名,然后找到其對應的構造器,接著創建相應的對象。其中的 findBindingConstructorForClass 方法源碼如下: ```java private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) { Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls); if (bindingCtor != null) { return bindingCtor; } String clsName = cls.getName(); try { Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding"); //noinspection unchecked bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class); } catch (ClassNotFoundException e) { } catch (NoSuchMethodException e) { } BINDINGS.put(cls, bindingCtor); return bindingCtor; } ``` 可以看到,根據類名拿到 targetClassName_ViewBinding 類的構造器,并調用其構造方法,進行綁定 # 參考文檔 [ButterKnife 源碼分析](https://www.jianshu.com/p/1c449c1b0fa2)
                  <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>

                              哎呀哎呀视频在线观看