Android注解的使用

說(shuō)明:本文主要用于記錄學(xué)習(xí)筆記
原文參見(jiàn):https://mp.weixin.qq.com/s/fJKAwpqn_hXRXRS4oh1dEw
注解使用范圍:類(lèi)、方法、變量、參數(shù)都可以被注解

可以干啥

  • 利用注解可以標(biāo)記源碼以便編譯器為源碼生成文檔和檢查代碼
  • 也可以讓編譯器和注解處理器在編譯時(shí)根據(jù)注解自動(dòng)生成代碼,甚至可以保留到運(yùn)行時(shí)以便改變運(yùn)行時(shí)的行為

Java 內(nèi)置了一些注解,如 @Override 注解用來(lái)表明該方法是重寫(xiě)父類(lèi)方法,編譯器會(huì)負(fù)責(zé)檢查該方法與父類(lèi)方法的聲明是否一致。@Deprecated 注解用來(lái)表明該元素已經(jīng)被廢棄不建議使用了。@SuppressWarnings 注解用來(lái)表示編譯器可以忽略特定警告。

注解類(lèi)型的聲明和接口的聲明類(lèi)似,不過(guò)需要使用 @interface 和元注解(用來(lái)定義注解的注解)描述,每個(gè)方法聲明定義了注解類(lèi)型的一個(gè)元素,且方法聲明不能包含任何參數(shù)或 throws,方法的返回類(lèi)型必須是原語(yǔ)類(lèi)型、String、Class、枚舉、注解和這些類(lèi)型的數(shù)組,方法可以有默認(rèn)值,如:

public @interface RequestForEnhancement {
    int    id();
    String synopsis();
    String engineer() default "[unassigned]"; 
    String date();    default "[unimplemented]"; 
}

定義完注解類(lèi)型后,就可以用它去注解一些聲明了。注解是一種特殊的修飾符,可以像 public、static 或 final 修飾符一樣使用,不過(guò)通常注解要寫(xiě)在這些修飾符之前。使用時(shí)為 @ 符號(hào)加注解類(lèi)型加元素值對(duì)列表并用括號(hào)括起來(lái),如:

@RequestForEnhancement(
    id       = 2868724,
    synopsis = "Enable time-travel",
    engineer = "Mr. Peabody",
    date     = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }

注解類(lèi)型也可以沒(méi)有方法/元素,被稱(chēng)為標(biāo)記注解類(lèi)型,如:

public @interface Preliminary { }

@Preliminary public class TimeTravel { ... }

如果注解類(lèi)型只有一個(gè)元素,那么元素應(yīng)該命名為 value,使用時(shí)也就可以忽略元素名和等號(hào)了,如:

public @interface Copyright {
    String value();
}

@Copyright("2002 Yoyodyne Propulsion Systems")
public class OscillationOverthruster { ... }

除了這些,很多注解還需要元注解(用來(lái)定義注解的注解)去描述,如:

@Documented//標(biāo)明改注解類(lèi)型可以被javadoc等工具文檔化
@Retention(RetentionPolicy.RUNTIME)//注解類(lèi)型保留時(shí)長(zhǎng)(枚舉)
@Target(ElementType.METHOD)//注解使用范圍
@Inherited//表示注解類(lèi)型被自動(dòng)繼承
public @interface MethodInfo {
    String author() default "Peabody";
    String date();
    int version() default 1;
}
  • @Documented 表明該注解類(lèi)型可以被 javadoc 等工具文檔化

  • @Retention 表明該注解類(lèi)型可以保留多長(zhǎng)時(shí)間,值為枚舉值 RetentionPolicy:

RetentionPolicy.SOURCE(只保留在源碼中,會(huì)被編譯器丟棄)

RetentionPolicy.CLASS(注解會(huì)被編譯器記錄在class文件中,但不需要被VM保留到運(yùn)行時(shí),這也是默認(rèn)的行為)

RetentionPolicy.RUNTIME(注解會(huì)被編譯器記錄在class文件中并被VM保留到運(yùn)行時(shí),所以可以通過(guò)反射獲取)
  • @Target 表明該注解類(lèi)型可以注解哪些程序元素,如果注解類(lèi)型不使用 @Target 描述那么表明默認(rèn)可以注解所有程序元素,值是枚舉數(shù)組 ElementType[]:
ElementType.TYPE(類(lèi)、接口(包括注解類(lèi)型)、枚舉的聲明)

ElementType.FIELD(字段(包括枚舉常量)的聲明)

ElementType.METHOD(方法的聲明)

ElementType.PARAMETER(形參的聲明)

ElementType.CONSTRUCTOR(構(gòu)造器的聲明)

ElementType.LOCAL_VARIABLE(本地變量的聲明)

ElementType.ANNOTATION_TYPE(注解類(lèi)型的聲明)

ElementType.PACKAGE(包的聲明)

ElementType.TYPE_PARAMETER(泛型參數(shù)的聲明)

ElementType.TYPE_USE(泛型的使用)
  • @Inherited 表明該注解類(lèi)型將被自動(dòng)繼承。也就是說(shuō),如果注解類(lèi)型被 @Inherited 注解,此時(shí)用戶(hù)查詢(xún)一個(gè)類(lèi)聲明的注解,而類(lèi)聲明沒(méi)被該注解類(lèi)型注解,那么將自動(dòng)查詢(xún)?cè)擃?lèi)父類(lèi)的注解類(lèi)型,以此類(lèi)推直到找到該注解類(lèi)型或達(dá)到頂層 Object 對(duì)象。

Android Support Library 提供了很多實(shí)用注解,如可以使用 @NonNull 注解進(jìn)行空檢查,使用 @UiThread、@WorkerThread 注解進(jìn)行線程檢查,使用 @IdRes 表明這個(gè)整數(shù)代表資源引用,還可以通過(guò) @IntDef、@StringDef 注解自定義注解來(lái)代替枚舉,如描述應(yīng)用中使用的字體文件:

public final class TypefaceManager {

    //真正的變量值,以前我們可以用枚舉(內(nèi)存占用大),或者直接定義字段(填錯(cuò)了不會(huì)報(bào)錯(cuò))
    public static final int FONT_TYPE_ICONIC = 0;
    public static final int FONT_TYPE_IMPACT = 1;
    public static final int FONT_TYPE_HELVETICA = 2;
    public static final int FONT_TYPE_DIN = 3;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({FONT_TYPE_ICONIC, FONT_TYPE_IMPACT, FONT_TYPE_HELVETICA, FONT_TYPE_DIN})
    @interface FontType {
    }

    private Context mContext;
    private SparseArray<Typeface> mTypefaceSparseArray;

    public TypefaceManager(Context context) {
        this.mContext = context;
        this.mTypefaceSparseArray = new SparseArray<>();
    }

    public static void setTypeface(TextView textView, @FontType int fontType) {
        Typeface localTypeface = MyApplication.getInstance().getTypefaceManager().getTypeface(fontType);
        if (localTypeface != null && localTypeface != textView.getTypeface()) {
            textView.setTypeface(localTypeface);
        }
    }

    public static void setTypeface(Paint paint, @FontType int fontType) {
        Typeface localTypeface = MyApplication.getInstance().getTypefaceManager().getTypeface(fontType);
        if (localTypeface != null && localTypeface != paint.getTypeface()) {
            paint.setTypeface(localTypeface);
        }
    }

    public Typeface getTypeface(@FontType int fontType) {
        Typeface typeface = mTypefaceSparseArray.get(fontType);
        if (typeface == null) {
            try {
                String path = null;
                if (fontType == FONT_TYPE_ICONIC) {
                    path = "fonts/fontawesome-webfont.ttf";
                } else if (fontType == FONT_TYPE_IMPACT) {
                    path = "fonts/impact.ttf";
                } else if (fontType == FONT_TYPE_HELVETICA) {
                    path = "fonts/Helvetica.ttf";
                } else if (fontType == FONT_TYPE_DIN) {
                    path = "fonts/ptdin.ttf";
                }
                typeface = Typeface.createFromAsset(mContext.getAssets(), path);
                this.mTypefaceSparseArray.put(fontType, typeface);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return typeface;
    }

}

下面來(lái)看看IntDef注解的定義

@Retention(RetentionPolicy.SOURCE)//只保留在源碼中,會(huì)被編譯器丟棄
@Target({ElementType.ANNOTATION_TYPE})//表示是用于注解A對(duì)直接B的聲明,可以從上方用法理解
public @interface IntDef {
    int[] value() default {};

    boolean flag() default false;
}

我們自己寫(xiě)個(gè)示例,對(duì)上面的用法做一個(gè)全面理解:

 public static final int CHECK_METHOD_COMPLETE = 1;//校驗(yàn)方法完整性
    public static final int TEST_METHOD = 2;//調(diào)試模式(測(cè)試方法調(diào)用)
    public static final int RELEASE = 0;//正式環(huán)境使用
    //調(diào)試模式
    public static @ModeType int mModeType = RELEASE;
    /**
     * 調(diào)試模式 0、正式使用(默認(rèn)模式) 1、校驗(yàn)方法是否完整實(shí)現(xiàn) 2、調(diào)試方法
     */
    @IntDef({CHECK_METHOD_COMPLETE, TEST_METHOD, RELEASE})
    @Retention(RetentionPolicy.SOURCE)//定義僅在源碼中保留,編譯后自動(dòng)移除
    public @interface ModeType {}
    public static void setModeType(@ModeType int type) {
        mModeType = type;
    }

然后我們接著去調(diào)用setModeType方法會(huì)發(fā)現(xiàn):同樣傳遞的值都是1,第一種寫(xiě)法就會(huì)報(bào)錯(cuò),而第二種則正常,這種寫(xiě)法既簡(jiǎn)捷又不容易出錯(cuò)。

示例

自定義注解

RUNTIME 注解&應(yīng)用示例

對(duì)于 @Retention(RetentionPolicy.RUNTIME) 的注解,注解會(huì)被編譯器記錄在 class 文件中并被 VM 保留到運(yùn)行時(shí),所以可以通過(guò)反射獲取,如:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface MethodInfo {
    String author() default "Peabody";
    String date();
    int version() default 1;
}

使用

public class App {
    @MethodInfo(
        author = “frank”,
        date = "2018/02/27",
        version = 2)
    public String getDescription() {
        return "no description";
    }
}

可以寫(xiě)個(gè)工具在運(yùn)行時(shí)利用反射獲取注解

public static void main(String[] args) {
    try {
        Class cls = Class.forName("com.frank.App");
        for (Method method : cls.getMethods()) {
            MethodInfo methodInfo = method.getAnnotation(
MethodInfo.class);
            if (methodInfo != null) {
                System.out.println("method author:" + methodInfo.author());
                System.out.println("method version:" + methodInfo.version());
                System.out.println("method date:" + methodInfo.date());
            }
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

這里需要注意,注解也是可以被混淆的,如果不想被混淆,也需要額外keep。

CLASS 注解&應(yīng)用示例

對(duì)于 @Retention(RetentionPolicy.CLASS) 的注解,注解會(huì)被編譯器記錄在 class 文件中,但不需要被 VM 保留到運(yùn)行時(shí),如:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
    @IdRes int value();
}

也就是說(shuō),這種編譯時(shí)注解適合用來(lái)在編譯時(shí)自動(dòng)生成代碼,這就需要 apt(Annotation Processing Tool)工具查找并執(zhí)行注解處理器(Annotation Processor)以生成源碼和文件,最終 javac 會(huì)編譯這些原始源文件和自動(dòng)生成的文件。Android Gradle 插件的 2.2 版本開(kāi)始支持注解處理器,你只需要使用 annotationProcessor 依賴(lài)注解處理器或者使用 javaCompileOptions.annotationProcessorOptions {} DSL指定注解處理器即可。定義注解處理器最簡(jiǎn)單的方式就是繼承 AbstractProcessor,在其 process 實(shí)現(xiàn)方法中實(shí)現(xiàn)注解元素的分析和源碼文件的生成。

以簡(jiǎn)化一系列 findViewById 為例:

public class MainActivity extends AppCompatActivity {

    TextView mTitleTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTitleTextView = (TextView) findViewById(R.id.titleTextView);
        mTitleTextView.setText("Hello World!");
    }
}

使用注解處理器后可以這么寫(xiě)

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.titleTextView)
    TextView mTitleTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        SimpleButterKnife.bind(this);//一行代碼搞定所有findviewbyid
        mTitleTextView.setText("Hello World!");
    }
}

實(shí)現(xiàn)方式就是利用注解和注解編譯器在編譯時(shí)自動(dòng)生成一個(gè)這樣的文件:

public class MainActivity_ViewBinding {
  public MainActivity target;

  public MainActivity_ViewBinding(MainActivity target) {
    this(target, target.getWindow().getDecorView());
  }

  public MainActivity_ViewBinding(MainActivity target, View source) {
    this.target = target;

    target.mTitleTextView = (TextView) source.findViewById(2131165300);
  }
}

在 SimpleButterKnife.bind(this); 的實(shí)現(xiàn)中加載這個(gè)類(lèi)并執(zhí)行構(gòu)造器就可以了。 實(shí)現(xiàn)起來(lái)也很簡(jiǎn)單,先新建一個(gè) java-library (這里注意是java-library)的module:simplebutterknife-annotations,用來(lái)聲明注解

@Retention(RetentionPolicy.CLASS)//存在字節(jié)碼中,但是不會(huì)保留到運(yùn)行時(shí)
@Target(ElementType.FIELD)//表示用于修飾字段
public @interface BindView {
    @IdRes int value();//入?yún)⑹且粋€(gè)int值 IdRes表示資源類(lèi)型的int值
}

simplebutterknife-annotations module依賴(lài):

//編譯用的android api
compileOnly 'com.google.android:android:4.1.1.4'
//注解
api 'com.android.support:support-annotations:27.0.2'

再新建一個(gè)注解處理器 java-library的module:simplebutterknife-compiler,用來(lái)對(duì)注解的元素進(jìn)行分析和生成源碼文件:
simplebutterknife-compiler module依賴(lài):

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':simplebutterknife-annotations')//自定義注解
    implementation 'com.google.auto:auto-common:0.10'
    api 'com.squareup:javapoet:1.9.0'//用于方便的生成java類(lèi)
    compileOnly 'com.google.auto.service:auto-service:1.0-rc4'//避免手動(dòng)創(chuàng)建meta文件 參見(jiàn) http://www.itdecent.cn/p/deeb39ccdc53
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'//加上這個(gè),避免高版本gradle生成失敗
}

注解處理器代碼:

package com.example.simplebutterknife_compiler;

import com.example.simplebutterknife_annotations.BindView;
import com.google.auto.common.MoreElements;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;

/**
 * 注解處理器
 * <p>
 * 利用了 Google 的 AutoService 為注解處理器自動(dòng)生成 metadata 文件并將注解處理器jar文件加入構(gòu)建路徑,
 * 這樣也就不需要再手動(dòng)創(chuàng)建并更新 META-INF/services/javax.annotation.processing.Processor 文件了
 *參考 java sip:http://www.itdecent.cn/p/deeb39ccdc53
 *
 */
@AutoService(Processor.class)
public class SimpleButterKnifeProcessor extends AbstractProcessor {

    /**
     * 方法指定可以支持最新的 Java 版本(直接指定最新的就可以了)
     *
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 方法指定該注解處理器用于處理哪些注解(我們這里只處理 @BindView 注解)
     *
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        //本例子 就是 處理BINDView
        types.add(BindView.class.getCanonicalName());
        return types;
    }

    /**
     * 檢索注解元素并生成代碼的是 process 方法的實(shí)現(xiàn):
     *
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        /**
         * 需要為每個(gè)包含注解的 Activity 都生成一個(gè)對(duì)應(yīng)的 _ViewBinding 文件,
         * 所以使用 Map 來(lái)存儲(chǔ)。BindingSet 存儲(chǔ) Activity 信息和它的 View 綁定信息,
         * View 綁定信息(ViewBinding)包括綁定 View 的類(lèi)型、View 的 ID 以及 View 的變量名。
         */
        Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
        /**
         * 查找所有被 @BindView 注解的程序元素(Element),為了簡(jiǎn)化,
         * 這里只認(rèn)為被注解的元素是 View 字段且它的外層元素(EnclosingElement)為 Activity 類(lèi):
         */
        for (Element element : roundEnvironment.getElementsAnnotatedWith(BindView.class)) {
            // 注解元素的外側(cè)元素,即 View 的所在 Activity 類(lèi)
            TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
            // 注解的 value 值,即 View 的 id
            int id = element.getAnnotation(BindView.class).value();
            // 注解元素的名字,即 View 變量名
            Name simpleName = element.getSimpleName();
            String name = simpleName.toString();
            // 注解元素的類(lèi)型,即 View 的類(lèi)型
            TypeMirror elementType = element.asType();
            TypeName type = TypeName.get(elementType);

            //然后把這些信息存到 Activity 對(duì)應(yīng)的 View 綁定中:
            BindingSet bindingSet = bindingMap.get(enclosingElement);
            if (bindingSet == null) {
                bindingSet = new BindingSet();
                TypeMirror typeMirror = enclosingElement.asType();
                TypeName targetType = TypeName.get(typeMirror);
                String packageName = MoreElements.getPackage(enclosingElement).getQualifiedName().toString();
                String className = enclosingElement.getQualifiedName().toString().substring(
                        packageName.length() + 1).replace('.', '$');
                ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding");
                bindingSet.targetTypeName = targetType;
                bindingSet.bindingClassName = bindingClassName;
                bindingMap.put(enclosingElement, bindingSet);
            }
            if (bindingSet.viewBindings == null) {
                bindingSet.viewBindings = new ArrayList<>();
            }
            ViewBinding viewBinding = new ViewBinding();
            viewBinding.type = type;
            viewBinding.id = id;
            viewBinding.name = name;
            bindingSet.viewBindings.add(viewBinding);
            /**
             * 確定完 Activity 信息和它對(duì)應(yīng)的 View 綁定信息后,為每個(gè) Activity 生成對(duì)應(yīng)的 XXX_ViewBinding.java 文件,
             * 文件內(nèi)容就是前面所說(shuō)類(lèi)綁定類(lèi)
             *
             * 雖然通過(guò)字符串拼接可以拼出這樣的文件內(nèi)容,但我們還得考慮 import,還得考慮大括號(hào)和換行,
             * 甚至還得考慮注釋和代碼美觀,所以利用 JavaPoet 來(lái)生成 .java 文件是個(gè)不錯(cuò)的選擇:
             */

            for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
                TypeElement typeElement = entry.getKey();
                BindingSet binding = entry.getValue();

                TypeName targetTypeName = binding.targetTypeName;
                ClassName bindingClassName = binding.bindingClassName;
                List<ViewBinding> viewBindings = binding.viewBindings;
                /**
                 * 生成binding類(lèi) public class MainActivity_ViewBinding {}
                 */
                TypeSpec.Builder viewBindingBuilder = TypeSpec.classBuilder(bindingClassName.simpleName())
                        .addModifiers(Modifier.PUBLIC);
                /**
                 * 生成binding類(lèi)中的成員變量 public MainActivity target;
                 */
                // public的target字段用來(lái)保存 Activity 引用
                viewBindingBuilder.addField(targetTypeName, "target", Modifier.PUBLIC);
                /**
                 * 生成構(gòu)造函數(shù)
                 *
                 *   public MainActivity_ViewBinding(MainActivity target) {
                 *     this(target, target.getWindow().getDecorView());
                 *   }
                 */
                MethodSpec.Builder activityViewBuilder = MethodSpec.constructorBuilder()
                        .addModifiers(Modifier.PUBLIC)
                        .addParameter(targetTypeName, "target");
                activityViewBuilder.addStatement("this(target, target.getWindow().getDecorView())");
                viewBindingBuilder.addMethod(activityViewBuilder.build());

                /**
                 * 生成構(gòu)造函數(shù)2
                 *
                 * public MainActivity_ViewBinding(MainActivity target, View source) {
                 *     this.target = target;
                 *
                 *     target.mTitleTextView = (TextView) source.findViewById(2131165300);
                 *   }
                 */
                MethodSpec.Builder viewBuilder = MethodSpec.constructorBuilder()
                        .addModifiers(Modifier.PUBLIC)
                        .addParameter(targetTypeName, "target")
                        .addParameter(ClassName.get("android.view", "View"), "source");
                viewBuilder.addStatement("this.target = target");
                viewBuilder.addCode("\n");
                for (ViewBinding temp : viewBindings) {//這里就是遍歷所有要綁定的元素,進(jìn)行findviewbyId
                    CodeBlock.Builder builder = CodeBlock.builder()
                            .add("target.$L = ", temp.name);
                    builder.add("($T) ", temp.type);
                    builder.add("source.findViewById($L)", CodeBlock.of("$L", temp.id));
                    viewBuilder.addStatement("$L", builder.build());
                }
                viewBindingBuilder.addMethod(viewBuilder.build());
                // 輸出 Java 文件
                JavaFile javaFile = JavaFile.builder(bindingClassName.packageName(), viewBindingBuilder.build())
                        .build();
                try {
                    javaFile.writeTo(processingEnv.getFiler());
                } catch (IOException e) {
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
                }
            }

        }

        return false;
    }

    class BindingSet {
        TypeName targetTypeName;
        ClassName bindingClassName;
        List<ViewBinding> viewBindings;
    }

    class ViewBinding {
        TypeName type;
        int id;
        String name;
    }
}

在 app module 中需要依賴(lài)注解 module 并注冊(cè)注解處理器 module:

dependencies {
    ...
    api project(':simplebutterknife-annotations')
    annotationProcessor project(':simplebutterknife-compiler')
}

這里有個(gè)注意點(diǎn),如果我們需要在kotlin代碼使用對(duì)用注解,需要做一些引入調(diào)整
首先在主工程引入kapt插件

apply plugin: 'kotlin-kapt'//引入kapt插件

然后把 annotationProcessor 變更為 kapt

dependencies {
    ...
    api project(':simplebutterknife-annotations')
    kapt project(':simplebutterknife-compiler')
}

app module 中的工具類(lèi) SimpleButterKnife 的 bind 方法只需要加載這個(gè)自動(dòng)生成的類(lèi)并執(zhí)行它的構(gòu)造器就行了:

public final class SimpleButterKnife {

    public static void bind(Activity target) {
        View sourceView = target.getWindow().getDecorView();
        Class<?> targetClass = target.getClass();
        String targetClassName = targetClass.getName();
        Constructor constructor;
        try {
            Class<?> bindingClass = targetClass.getClassLoader().loadClass(targetClassName + "_ViewBinding");
            constructor = bindingClass.getConstructor(targetClass, View.class);
        } catch (ClassNotFoundException e) {
            // TODO Not found. should try search its superclass
            throw new RuntimeException("Not found. should try search its superclass of " + targetClassName, e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("Unable to find binding constructor for " + targetClassName, e);
        }
        try {
            constructor.newInstance(target, sourceView);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Unable to invoke " + constructor, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("Unable to invoke " + constructor, e);
        } catch (InvocationTargetException e) {
            Throwable cause = e.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            }
            if (cause instanceof Error) {
                throw (Error) cause;
            }
            throw new RuntimeException("Unable to create binding instance.", cause);
        }
    }
}

重新構(gòu)建下工程,就可以在 build\generated\source\apt\debug 目錄中查看自動(dòng)生成的文件了:

package com.frank.simplebutterknife;

import android.view.View;
import android.widget.TextView;

public class MainActivity_ViewBinding {
  public MainActivity target;

  public MainActivity_ViewBinding(MainActivity target) {
    this(target, target.getWindow().getDecorView());
  }

  public MainActivity_ViewBinding(MainActivity target, View source) {
    this.target = target;

    target.mTitleTextView = (TextView) source.findViewById(2131165300);
  }
}

重新構(gòu)建下工程,就可以在 build\generated\source\apt\debug 目錄中查看自動(dòng)生成的文件了:


image.png

自測(cè)demo練習(xí)部分

demoall-sample工程


image.png

除了上面的butterknife,還有簡(jiǎn)單的注解處理并生成map集合的應(yīng)用處理,具體參見(jiàn)筆記代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容