Java-注解蟆技、反射、ButterKnife

Java 注解(Annotation)又稱 Java 標(biāo)注斗忌,是 JDK5.0 引入的一種注釋機(jī)制质礼。 注解是元數(shù)據(jù)的一種形式,提供有關(guān)于程序但不屬于程序本身的數(shù)據(jù)织阳。注解對它們注解的代碼的操作沒有直接影響眶蕉。

注解本身沒有任何意義,單獨的注解就是一種注釋唧躲,他需要結(jié)合其他如反射造挽、插樁等技術(shù)才有意義。

注解聲明

聲明一個注解類型

Java中所有的注解弄痹,默認(rèn)實現(xiàn) Annotation 接口:

package java.lang.annotation;

public interface Annotation {

    boolean equals(Object obj);
    
    int hashCode();
    
    String toString();
    
    Class<? extends Annotation> annotationType();
}

與聲明一個"Class"不同的是饭入,注解的聲明使用 @interface 關(guān)鍵字。一個注解的聲明如下:

public @interface Bala{

}

元注解

定義注解時肛真,注解類也能夠使用其他的注解聲明谐丢。對注解類型進(jìn)行注解的注解類,我們稱之為 meta-annotation(元注解)蚓让。一般的乾忱,我們在定義自定義注解時,需要指定的元注解有兩個 :

另外還有@Documented 與 @Inherited元注解历极,前者用于被javadoc工具提取成文檔窄瘟,后者表示允許子類繼承父類中定義的注解。

@Target

注解標(biāo)記另一個注解执解,以限制可以應(yīng)用注解的Java元素類型寞肖。目標(biāo)注解指定以下元素類型之一作為其值:

  • ElementType.ANNOTATION_TYPE 可以應(yīng)用于注解類型纲酗。
  • ElementType.CONSTRUCTOR 可以應(yīng)用于構(gòu)造函數(shù)衰腌。
  • ElementType.FIELD 可以應(yīng)用于字段或?qū)傩浴?/li>
  • ElementType.LOCAL_VARIABLE 可以應(yīng)用于局部變量。
  • ElementType.METHOD 可以應(yīng)用于方法級注解觅赊。
  • ElementType.PACKAGE 可以應(yīng)用于包聲明右蕊。
  • ElementType.PARAMETER 可以應(yīng)用于方法的參數(shù)。
  • ElementType.TYPE 可以應(yīng)用于類的任何元素吮螺。

@Retention

注解指定標(biāo)記注解的存儲方式:

  • RetentionPolicy.SOURCE - 標(biāo)記的注解僅保留在源級別中饶囚,并被編譯器忽略帕翻。
  • RetentionPolicy.CLASS - 標(biāo)記的注解在編譯時由編譯器保留,但 Java 虛擬機(jī)(JVM)會忽略萝风。
  • RetentionPolicy.RUNTIME - 標(biāo)記的注解由 JVM 保留嘀掸,因此運(yùn)行時環(huán)境可以使用它。

@Retention 三個值中 SOURCE < CLASS < RUNTIME规惰,即CLASS包含了SOURCE睬塌,RUNTIME包含SOURCE、CLASS歇万。

示例:

//@Target(ElementType.TYPE) 只能在類上標(biāo)記該注解
@Target({ElementType.TYPE,ElementType.FIELD}) // 允許在類與類屬性上標(biāo)記該注解
@Retention(RetentionPolicy.SOURCE) //注解保留在源碼中
public @interface Bala {
}

級別 技術(shù) 說明
源碼 APT 在編譯期能夠獲取注解與注解聲明的類包括類中所有成員信息揩晴,一般用于生成額外的輔助類。
字節(jié)碼 字節(jié)碼增強(qiáng) 在編譯出Class后贪磺,通過修改Class數(shù)據(jù)以實現(xiàn)修改代碼邏輯目的硫兰。對于是否需要修改的區(qū)分或者修改為不同邏輯的判斷可以使用注解。
運(yùn)行時 反射 在程序運(yùn)行期間寒锚,通過反射技術(shù)動態(tài)獲取注解與其元素劫映,從而完成不同的邏輯判定。

注解類型元素

在上文元注解中刹前,允許在使用注解時傳遞參數(shù)苏研。我們也能讓自定義注解的主體包含 annotation type element (注解類型元素) 聲明,它們看起來很像方法腮郊,可以定義可選的默認(rèn)值摹蘑。

@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Bala {

    String value(); //無默認(rèn)值
    
    int age() default 1; //有默認(rèn)值

}

注意:在使用注解時,如果定義的注解中的類型元素?zé)o默認(rèn)值轧飞,則必須進(jìn)行傳值衅鹿。

@Bala("帥") //如果只存在value元素需要傳值的情況,則可以省略:元素名=
@Bala(value="帥",age = 2)
int i;

注解應(yīng)用場景

按照 @Retention 元注解定義的注解存儲方式过咬,注解可以被在三種場景下使用:

SOURCE

RetentionPolicy.SOURCE 大渤,作用于源碼級別的注解,可提供給IDE語法檢查掸绞、APT等場景使用泵三。

在類中使用 SOURCE 級別的注解,其編譯之后的class中會被丟棄衔掸。

IDE語法檢查

例如:@DrawableRes

public static void setDrawable(@DrawableRes int id) { }

在Android開發(fā)中烫幕, support-annotationsandroidx.annotation 中均有提供 @IntDef 注解,此注解的定義如下:

@Retention(SOURCE) //源碼級別注解
@Target({ANNOTATION_TYPE})
public @interface IntDef {

    int[] value() default {};
    boolean flag() default false;
    boolean open() default false;
    
}

Java中Enum(枚舉)的實質(zhì)是特殊單例的靜態(tài)成員變量敞映,在運(yùn)行期所有枚舉類作為單例较曼,全部加載到內(nèi)存中。比常量多5到10倍的內(nèi)存占用振愿。

此注解的意義在于能夠取代枚舉捷犹,實現(xiàn)如方法入?yún)⑾拗啤?/p>

如:我們定義方法test弛饭,此方法接收參數(shù) teacher 需要在:A、B 中選擇一個萍歉。如果使用枚舉能夠?qū)崿F(xiàn)為:

public enum Teacher{
    A , B
}

public void test(Teacher teacher) {

}

而現(xiàn)在為了進(jìn)行內(nèi)存優(yōu)化侣颂,我們現(xiàn)在不再使用枚舉,則方法定義為:

public static final int A = 1;
public static final int B = 2;
public void test(int teacher) {
}

然而此時枪孩,調(diào)用 test 方法由于采用基本數(shù)據(jù)類型int横蜒,將無法進(jìn)行類型限定。此時使用@IntDef增加自定義注解:

public static final int A = 1;
public static final int B = 2;

@IntDef(value = {A, B}) //限定為A销凑,B
@Target(ElementType.PARAMETER) //作用于參數(shù)的注解
@Retention(RetentionPolicy.SOURCE) //源碼級別注解
public @interface Teacher {
}

public void test(@Teacher int teacher) {
}

此時丛晌,我們再去調(diào)用 test方法,如果傳遞的參數(shù)不是 A 或者 B 則會顯示 Inspection 警告(編譯不會報錯)斗幼。

  • 可以修改此類語法檢查級別:

在AS -> Setting -> Editor -> Inspections -> 右邊界面搜索Incorrect constant

修改Severity等級

以上注解均為 SOURCE 級別澎蛛,本身IDEA/AS 就是由Java開發(fā)的,工具實現(xiàn)了對Java語法的檢查蜕窿,借助注解能對被注解的特定語法進(jìn)行額外檢查谋逻。

APT注解處理器

APT全稱為:"Anotation Processor Tools",意為注解處理器桐经。顧名思義毁兆,其用于處理注解。編寫好的Java源文
件阴挣,需要經(jīng)過 javac 的編譯气堕,翻譯為虛擬機(jī)能夠加載解析的字節(jié)碼Class文件。注解處理器是 javac 自帶的一個工具畔咧,用來在編譯時期掃描處理注解信息茎芭。你可以為某些注解注冊自己的注解處理器。 注冊的注解處理器由 javac 調(diào)起誓沸,并將注解信息傳遞給注解處理器進(jìn)行處理梅桩。

注解處理器是對注解應(yīng)用最為廣泛的場景。在Glide拜隧、EventBus3宿百、Butterknifer、Tinker洪添、ARouter等等常用框架中都有注解處理器的身影垦页。但是你可能會發(fā)現(xiàn),這些框架中對注解的定義并不是 SOURCE 級別薇组,更多的是 CLASS 級別外臂,別忘了:CLASS 包含了 SOURCE坐儿,RUNTIME 包含 SOURCE律胀、CLASS宋光。

CLASS

定義為 CLASS 的注解,會保留在class文件中炭菌,但是會被虛擬機(jī)忽略(即無法在運(yùn)行期反射獲取注解)罪佳。此時完全符合此種注解的應(yīng)用場景為字節(jié)碼操作。如:AspectJ黑低、熱修復(fù)Roubust中應(yīng)用此場景赘艳。
所謂字節(jié)碼操作即為,直接修改字節(jié)碼Class文件以達(dá)到修改代碼執(zhí)行邏輯的目的克握。在程序中有多處需要進(jìn)行是否登錄的判斷蕾管。

如果我們使用普通的編程方式,需要在代碼中進(jìn)行 if-else 的判斷菩暗,也許存在十個判斷點掰曾,則需要在每個判斷點加入此項判斷。此時停团,我們可以借助AOP(面向切面)編程思想旷坦,將程序中所有功能點劃分為: 需要登錄無需登錄 兩種類型,即兩個切面佑稠。對于切面的區(qū)分即可采用注解秒梅。

//Java源碼
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Login {
}

@Login
public void jumpA(){
    startActivity(new Intent(this,AActivity.class));
}

public void jumpB(){
    startActivity(new Intent(this,BActivity.class));
}

在上訴代碼中, jumpA 方法需要具備登錄身份舌胶。而 Login 注解的定義被設(shè)置為 CLASS 捆蜀。因此我們能夠在該類所編譯的字節(jié)碼中獲得到方法注解 Login 。在操作字節(jié)碼時幔嫂,就能夠根據(jù)方法是否具備該注解來修改class中該方法的內(nèi)容加入 if-else 的代碼段:

//Class字節(jié)碼
@Login
public void jumpA() {
    if (this.isLogin) {
        this.startActivity(new Intent(this, LoginActivity.class));
    } else {
        this.startActivity(new Intent(this, AActivity.class));
    }
}

public void jumpB() {
    startActivity(new Intent(this,BActivity.class));
}

注解能夠設(shè)置類型元素(參數(shù))漱办,結(jié)合參數(shù)能實現(xiàn)更為豐富的場景,如:運(yùn)行期權(quán)限判定等婉烟。

RUNTIME

注解保留至運(yùn)行期娩井,意味著我們能夠在運(yùn)行期間結(jié)合反射技術(shù)獲取注解中的所有信息。

處理注解

一般新建一個庫似袁。例如叫:Compiler洞辣,在app的gradle里添加依賴 annotationProcessor project(':Compiler')

新建一個類繼承 AbstractProcessor來處理注解

要添加SupportedAnnotationTypes注解,聲明自定義注解的全類名

import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

@SupportedAnnotationTypes("com.XXX.XXX.Bala")
public class BalaProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        Messager messager = processingEnv.getMessager();
        //NOTE是日志等級
        messager.printMessage(Diagnostic.Kind.NOTE, "================================");
        //xxxx
        // http
        //
        return false;
    }
}

注冊 BalaProcessor昙衅。

Compiler庫中的 src/main目錄下新建 resources/META-INF.services/javax.annotation.processing.Processor文件扬霜。一定要這個目錄和這個文件名。

Processor 文件中寫入 Processor 的全類名完成注冊

com.XXX.XXX.BalaProcessor

反射

一般情況下而涉,我們使用某個類時必定知道它是什么類著瓶,是用來做什么的,并且能夠獲得此類的引用啼县。于是我們直接對這個類進(jìn)行實例化材原,之后使用這個類對象進(jìn)行操作沸久。
反射則是一開始并不知道我要初始化的類對象是什么,自然也無法使用 new 關(guān)鍵字來創(chuàng)建對象了余蟹。這時候卷胯,我們使用 JDK 提供的反射 API 進(jìn)行反射調(diào)用。反射就是在運(yùn)行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意方法和屬性;并且能改變它的屬性威酒。是Java被視為動態(tài)語言的關(guān)鍵窑睁。

Java反射機(jī)制主要提供了以下功能:

  • 在運(yùn)行時構(gòu)造任意一個類的對象
  • 在運(yùn)行時獲取或者修改任意一個類所具有的成員變量和方法
  • 在運(yùn)行時調(diào)用任意一個對象的方法(屬性)

Class

反射始于Class, Class是一個類葵孤,封裝了當(dāng)前對象所對應(yīng)的類的信息担钮。 一個類中有屬性,方法尤仍,構(gòu)造器等裳朋,比如說有一個Person類,一個Order類吓著,一個Book類鲤嫡,這些都是不同的類,現(xiàn)在需要一個類绑莺,用來描述類暖眼,這就是
Class,它應(yīng)該有類名纺裁,屬性诫肠,方法,構(gòu)造器等欺缘。Class是用來描述類的類栋豫。

Class類是一個對象照鏡子的結(jié)果,對象可以看到自己有哪些屬性谚殊,方法丧鸯,構(gòu)造器,實現(xiàn)了哪些接口等等嫩絮。對于每個類而言丛肢,JRE 都為其保留一個不變的 Class 類型的對象。一個 Class 對象包含了特定某個類的有關(guān)信息剿干。 對象只能由系統(tǒng)建立對象蜂怎,一個類(而不是一個對象)在 JVM 中只會有一個Class實例。

獲得 Class 對象

獲取Class對象的三種方式

  1. 通過類名獲取 類名.class
  2. 通過對象獲取 對象名.getClass()
  3. 通過全類名獲取 Class.forName(全類名) classLoader.loadClass(全類名)
  • 使用 Class 類的 forName 靜態(tài)方法
public static Class<?> forName(String className)
  • 直接獲取某一個對象的 class
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;

  • 調(diào)用某個對象的 getClass() 方法
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();

判斷是否為某個類的實例

一般地置尔,我們用 instanceof 關(guān)鍵字來判斷是否為某個類的實例杠步。同時我們也可以借助反射中 Class 對象的
isInstance() 方法來判斷是否為某個類的實例,它是一個 native 方法:

public native boolean isInstance(Object obj);

判斷是否為某個類的類型

public boolean isAssignableFrom(Class<?> cls)

創(chuàng)建實例

通過反射來生成對象主要有兩種方式。

  • 使用Class對象的newInstance()方法來創(chuàng)建Class對象對應(yīng)類的實例幽歼。
Class<?> c = String.class;
Object str = c.newInstance();

  • 先通過Class對象獲取指定的Constructor對象朵锣,再調(diào)用Constructor對象的newInstance()方法來創(chuàng)建實例。這種方法可以用指定的構(gòu)造器構(gòu)造類的實例试躏。
//獲取String所對應(yīng)的Class對象
Class<?> c = String.class;
//獲取String類帶一個String參數(shù)的構(gòu)造器
Constructor constructor = c.getConstructor(String.class);
//根據(jù)構(gòu)造器創(chuàng)建實例
Object obj = constructor.newInstance("23333");
System.out.println(obj);

獲取構(gòu)造器信息

得到構(gòu)造器的方法

Constructor getConstructor(Class[] params) -- 獲得使用特殊的參數(shù)類型的public構(gòu)造函數(shù)(包括父類)
Constructor[] getConstructors() -- 獲得類的所有公共構(gòu)造函數(shù)
Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定參數(shù)類型的構(gòu)造函數(shù)(包括私有)
Constructor[] getDeclaredConstructors() -- 獲得類的所有構(gòu)造函數(shù)(與接入級別無關(guān))

獲取類構(gòu)造器的用法與上述獲取方法的用法類似猪勇。主要是通過Class類的getConstructor方法得到Constructor類的一個實例设褐,而Constructor類有一個newInstance方法可以創(chuàng)建一個對象實例:

public T newInstance(Object ... initargs)

獲取類的成員變量(字段)信息

獲得字段信息的方法

Field getField(String name) -- 獲得命名的公共字段
Field[] getFields() -- 獲得類的所有公共字段
Field getDeclaredField(String name) -- 獲得類聲明的命名的字段
Field[] getDeclaredFields() -- 獲得類聲明的所有字段

調(diào)用方法

獲得方法信息的方法

Method getMethod(String name, Class[] params) -- 使用特定的參數(shù)類型颠蕴,獲得命名的公共方法
Method[] getMethods() -- 獲得類的所有公共方法
Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數(shù)類型,獲得類聲明的命名的方法
Method[] getDeclaredMethods() -- 獲得類聲明的所有方法

當(dāng)我們從類中獲取了一個方法后助析,我們就可以用 invoke() 方法來調(diào)用這個方法犀被。 invoke 方法的原型為:

public Object invoke(Object obj, Object... args)

利用反射創(chuàng)建數(shù)組

數(shù)組在Java里是比較特殊的一種類型,它可以賦值給一個Object Reference 其中的Array類為
java.lang.reflect.Array類外冀。我們通過Array.newInstance()創(chuàng)建數(shù)組對象寡键,它的原型是:

public static Object newInstance(Class<?> componentType, int length);

反射獲取泛型真實類型

當(dāng)我們對一個泛型類進(jìn)行反射時,需要得到泛型中的真實數(shù)據(jù)類型雪隧,來完成如json反序列化的操作西轩。此時需要通過 Type 體系來完成。 Type 接口包含了一個實現(xiàn)類(Class)和四個實現(xiàn)接口脑沿,他們分別是:

  • TypeVariable

    • 泛型類型變量藕畔。可以泛型上下限等信息庄拇;
  • ParameterizedType

    • 具體的泛型類型注服,可以獲得元數(shù)據(jù)中泛型簽名類型(泛型真實類型)
  • GenericArrayType

    • 當(dāng)需要描述的類型是泛型類的數(shù)組時,比如List[],Map[]措近,此接口會作為Type的實現(xiàn)溶弟。
  • WildcardType

    • 通配符泛型,獲得上下限信息瞭郑;
TypeVariable
public class TestType <K extends Comparable & Serializable, V> {

    K key;
    V value;
    
    public static void main(String[] args) throws Exception {
        // 獲取字段的類型
        Field fk = TestType.class.getDeclaredField("key");
        Field fv = TestType.class.getDeclaredField("value");
        
        TypeVariable keyType = (TypeVariable)fk.getGenericType();
        TypeVariable valueType = (TypeVariable)fv.getGenericType();
        // getName 方法
        System.out.println(keyType.getName()); // K
        System.out.println(valueType.getName()); // V
        // getGenericDeclaration 方法
        System.out.println(keyType.getGenericDeclaration()); // class com.test.TestType
        System.out.println(valueType.getGenericDeclaration()); // class com.test.TestType
        // getBounds 方法
        System.out.println("K 的上界:"); // 有兩個
        for (Type type : keyType.getBounds()) { // interface java.lang.Comparable
            System.out.println(type); // interface java.io.Serializable
        }
        System.out.println("V 的上界:"); // 沒明確聲明上界的, 默認(rèn)上界是 Object
        for (Type type : valueType.getBounds()) { // class java.lang.Object
            System.out.println(type);
        }
    }
}
ParameterizedType
public class TestType {

    Map<String, String> map;
    
    public static void main(String[] args) throws Exception {
        Field f = TestType.class.getDeclaredField("map");
        System.out.println(f.getGenericType()); // java.util.Map<java.lang.String,java.lang.String>
        ParameterizedType pType = (ParameterizedType) f.getGenericType();
            System.out.println(pType.getRawType()); // interface java.util.Map
        for (Type type : pType.getActualTypeArguments()) {
            System.out.println(type); // 打印兩遍: class java.lang.String
        }
    }
}

GenericArrayType
public class TestType<T> {

    List<String>[] lists;
    
    public static void main(String[] args) throws Exception {
        Field f = TestType.class.getDeclaredField("lists");
        GenericArrayType genericType = (GenericArrayType) f.getGenericType();
        System.out.println(genericType.getGenericComponentType());
    }
}
WildcardType
public class TestType {

    private List<? extends Number> a; // 上限
    private List<? super String> b; //下限
    
    public static void main(String[] args) throws Exception {
        Field fieldA = TestType.class.getDeclaredField("a");
        Field fieldB = TestType.class.getDeclaredField("b");
        // 先拿到泛型類型
        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
        // 再從泛型里拿到通配符類型
        WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
        WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
        // 方法測試
        System.out.println(wTypeA.getUpperBounds()[0]); // class java.lang.Number
        System.out.println(wTypeB.getLowerBounds()[0]); // class java.lang.String
        // 看看通配符類型到底是什么, 打印結(jié)果為: ? extends java.lang.Number
        System.out.println(wTypeA);
    }
}

Gson反序列化
static class Response<T> {
    T data;
    int code;
    String message;
    
    @Override
    public String toString() {
        return "Response{" +
        "data=" + data +
        ", code=" + code +
        ", message='" + message + '\'' +
        '}';
    }

    public Response(T data, int code, String message) {
        this.data = data;
        this.code = code;
        this.message = message;
    }
}

static class Data {
    String result;
    
    public Data(String result) {
        this.result = result;
    }
    
    @Override
    public String toString() {
        return "Data{" +
        "result=" + result +
        '}';
    }
}

    static class ChildTypeRefrence{
        Response<Data>  t;
    }

public static void main(String[] args) {
    Response<Data> dataResponse = new Response(new Data("數(shù)據(jù)"), 1, "成功");
    Gson gson = new Gson();
    String json = gson.toJson(dataResponse);
    System.out.println(json);
    
    //為什么TypeToken要定義為抽象類辜御?
    /**
     *  有花括號: 代表是匿名內(nèi)部類,創(chuàng)建一個匿名內(nèi)部類的實例對象
     *  沒花括號:創(chuàng)建實例對象
     * 匿名內(nèi)部類是一個類屈张,是class我抠。對象只是一個對象,拿不到泛型的具體信息袜茧,因為泛型已經(jīng)被擦除了菜拓。但是類是有ChildTypeRefrence來保存泛型的簽名
     */
    Response<Data> resp = gson.fromJson(json, new TypeToken<Response<Data>>() {
    }.getType());
    System.out.println(resp.data.result);
}

在進(jìn)行GSON反序列化時,存在泛型時笛厦,可以借助 TypeToken 獲取Type以完成泛型的反序列化纳鼎。但是為什么 TypeToken 要被定義為抽象類呢?
因為只有定義為抽象類或者接口,這樣在使用時贱鄙,需要創(chuàng)建對應(yīng)的實現(xiàn)類劝贸,此時確定泛型類型,編譯才能夠?qū)⒎盒蛃ignature信息記錄到Class元數(shù)據(jù)中逗宁。

應(yīng)用

模擬ButterKnife

  • 添加注解 InjectView

import androidx.annotation.IdRes;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

  • 設(shè)置注解
@InjectView(R.id.tv)
private TextView tv;
  • 反射獲取注解并賦值

import android.app.Activity;
import android.view.View;

import java.lang.reflect.Field;

public class InjectUtils {


    public static void injectView(Activity activity) {
        Class<? extends Activity> cls = activity.getClass();

        //獲得此類所有的成員
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field filed : declaredFields) {
            // 判斷屬性是否被InjectView注解聲明
            if (filed.isAnnotationPresent(InjectView.class)){
                InjectView injectView = filed.getAnnotation(InjectView.class);
                //獲得了注解中設(shè)置的id
                int id = injectView.value();
                View view = activity.findViewById(id);
                //反射設(shè)置 屬性的值
                filed.setAccessible(true); //設(shè)置訪問權(quán)限映九,允許操作private的屬性
                try {
                    //反射賦值
                    filed.set(activity,view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

  • 實際設(shè)置值
public class MainActivity extends AppCompatActivity {


    @InjectView(R.id.tv)
    private TextView tv;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectUtils.injectView(this);
        
        tv.setText("XXXXXX");

    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瞎颗,隨后出現(xiàn)的幾起案子件甥,更是在濱河造成了極大的恐慌,老刑警劉巖哼拔,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件引有,死亡現(xiàn)場離奇詭異,居然都是意外死亡倦逐,警方通過查閱死者的電腦和手機(jī)譬正,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來檬姥,“玉大人曾我,你說我怎么就攤上這事〗∶瘢” “怎么了抒巢?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荞雏。 經(jīng)常有香客問我虐秦,道長,這世上最難降的妖魔是什么凤优? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任悦陋,我火速辦了婚禮,結(jié)果婚禮上筑辨,老公的妹妹穿的比我還像新娘俺驶。我一直安慰自己,他們只是感情好棍辕,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布暮现。 她就那樣靜靜地躺著,像睡著了一般楚昭。 火紅的嫁衣襯著肌膚如雪栖袋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天抚太,我揣著相機(jī)與錄音塘幅,去河邊找鬼昔案。 笑死,一個胖子當(dāng)著我的面吹牛电媳,可吹牛的內(nèi)容都是我干的踏揣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼匾乓,長吁一口氣:“原來是場噩夢啊……” “哼捞稿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拼缝,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤娱局,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后珍促,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铃辖,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡剩愧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年猪叙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仁卷。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡穴翩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锦积,到底是詐尸還是另有隱情芒帕,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布丰介,位于F島的核電站背蟆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏哮幢。R本人自食惡果不足惜带膀,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望橙垢。 院中可真熱鬧垛叨,春花似錦、人聲如沸柜某。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春登淘,著一層夾襖步出監(jiān)牢的瞬間笛洛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工真竖, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留钥飞,地道東北人百框。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓琳要,卻偏偏與公主長得像寡具,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稚补,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容