如何做一個簡單的Android注解框架(JBInject)

反射(Reflect)

程序集包含模塊,而模塊包含類型,類型又包含成員。反射則提供了封裝程序集、模塊和類型的對象。您可以使用反射動態(tài)地創(chuàng)建類型的實例,將類型綁定到現(xiàn)有對象,或從現(xiàn)有對象中獲取類型。然后,可以調(diào)用類型的方法或訪問其字段和屬性.

Person.java

public class Person {
    
    private String  name;
    private int     age;
                    
    private void intro() {
        System.out.println("我叫" + name + ",我今年" + age + "歲.");
    }
}

TestDemo.java


public class TestDemo {
    
    public static void main(String[] args) throws Exception {
        
        Person person = new Person();
        Class<?> cls = person.getClass();
        // 獲取Person運(yùn)行時的類
        
        Field nameField = cls.getDeclaredField("name");
        Field ageField = cls.getDeclaredField("age");
        // 獲取name和age屬性
        // cls.getDeclaredFields(); //獲取所有屬性
        
        nameField.setAccessible(true);
        ageField.setAccessible(true);
        // 打破name和age屬性的封裝性
        
        nameField.set(person, "小明");
        ageField.set(person, 19);
        // 為person對象對應(yīng)的屬性設(shè)置值
        
        Method method = cls.getDeclaredMethod("intro");
        // 獲取intro方法
        // cls.getDeclaredMethods(); //獲取所有方法
        
        method.setAccessible(true);
        // 打破intro方法的封裝性
        
        method.invoke(person);
        //調(diào)用person的intro方法,
        // method.invoke(person, "arg1","arg2"...); 支持不定長參數(shù)作為方法的參數(shù)
    }
}

注解(Annotation)

定義:注解(Annotation),也叫元數(shù)據(jù)。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對這些元素進(jìn)行說明,注釋。

Person.java

public class Person {
    
    private String  name;
    private int     age;
                    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "我叫" + name + ",今年" + age + "歲";
    }
    
}

PersonInject.java


package snippet;

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

@Target(ElementType.FIELD) // 改注解只能注解屬性
                            // @Target({ElementType.FIELD,ElementType.METHOD})可指定多種類型
/**
 * TYPE(類型) FIELD(屬性) METHOD(方法) PARAMETER(參數(shù)) CONSTRUCTOR(構(gòu)造函數(shù))
 * LOCAL_VARIABLE(局部變量) ANNOTATION_TYPE,PACKAGE(包),
 */

@Retention(RetentionPolicy.RUNTIME)
/**
 * SOURCE:代表的是這個Annotation類型的信息只會保留在程序源碼里,源碼如果經(jīng)過了編譯之后,Annotation的數(shù)據(jù)就會消失,
 * 并不會保留在編譯好的.class文件里面。
 * 
 * ClASS:意思是這個Annotation類型的信息保留在程序源碼里,同時也會保留在編譯好的.class文件里面,在執(zhí)行的時候,
 * 并不會把這一些信息加載到虛擬機(jī)(JVM)中去.注意一下,當(dāng)你沒有設(shè)定一個Annotation類型的Retention值時,系統(tǒng)默認(rèn)值是CLASS.
 * 
 * RUNTIME:表示在源碼、編譯好的.class文件中保留信息,在執(zhí)行的時候會把這一些信息加載到JVM中去的.
 */

public @interface PersonInject {
    String name();
    
    int age();
    // 上面兩個可以類比函數(shù)的形參
}


TestDemo.java


public class TestDemo {
    
    @PersonInject(name = "小明", age = 19) // 僅僅是添加注解
    Person person;
    
    public static void main(String[] args) throws Exception {
        
        // 現(xiàn)在才開始真正注射
        TestDemo demo = new TestDemo();
        Class<?> cls = demo.getClass();
        
        Field declaredField = cls.getDeclaredField("person");
        // 請看反射那一塊
        
        PersonInject personInject = declaredField.getAnnotation(PersonInject.class);
        String name = personInject.name();
        int age = personInject.age();
        // 獲取person屬性的注解和它的值
        
        Person person = new Person();
        person.setName(name);
        person.setAge(age);
        
        declaredField.setAccessible(true);
        declaredField.set(demo, person);
        // 設(shè)置值
        
        System.out.println(demo.person);
    }
    
}

開始編寫小型注解框架

好啦,現(xiàn)在我們把前面的東西用到Android里面吧.

布局文件


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10dp"
        android:text="這是原來的文字" />

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="改變文字" />

    <Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="改變文字" />

    

</LinearLayout>

ViewInject注解Demo

ViewInject.java


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    int value();
    // 這里默認(rèn)是value,如果是age的話注解是寫(age=19),如果是value的話可以直接(19)
}

MainActivity.java


public class MainActivity extends Activity implements OnClickListener {
    
    @ViewInject(R.id.tv)
    private TextView    tv;
                        
    @ViewInject(R.id.btn)
    private Button      btn;
                        
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        try {
            Class<?> cls = MainActivity.class;
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field field : declaredFields) {
                ViewInject viewInject = field.getAnnotation(ViewInject.class);
                if (viewInject != null) {
                    field.setAccessible(true);
                    View view = findViewById(viewInject.value());
                    field.set(MainActivity.this, view);
                }
            }
            
            btn.setOnClickListener(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    @Override
    public void onClick(View v) {
        tv.setText("ViewInject注解成功!");
    }
    
}


OnClick注解Demo

OnClick.java


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    int value();
}

MainActivity.java


package com.example.viewinjectdemo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import viewinject.OnClick;
import viewinject.ViewInject;

public class MainActivity extends Activity implements OnClickListener {
    
    @ViewInject(R.id.tv)
    private TextView    tv;
                        
    @ViewInject(R.id.btn)
    private Button      btn;
                        
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        try {
            Class<?> cls = MainActivity.class;
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field field : declaredFields) {
                ViewInject viewInject = field.getAnnotation(ViewInject.class);
                if (viewInject != null) {
                    field.setAccessible(true);
                    View view = findViewById(viewInject.value());
                    field.set(MainActivity.this, view);
                }
            }
            
            btn.setOnClickListener(this);
            
            Method[] declaredMethods = cls.getDeclaredMethods();
            for (final Method method : declaredMethods) {
                OnClick onClick = method.getAnnotation(OnClick.class);
                if (onClick != null) {
                    method.setAccessible(true);
                    View view = MainActivity.this.findViewById(onClick.value());
                    view.setOnClickListener(new OnClickListener() {
                        
                        @Override
                        public void onClick(View v) {
                            try {
                                method.invoke(MainActivity.this);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    
                }
                
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    @Override
    public void onClick(View v) {
        tv.setText("ViewInject注解成功!");
    }
    
    @OnClick(R.id.btn1)
    public void clickBtn() {
        tv.setText("OnClick注解成功!");
    }
    
}

封裝

從前面我們可以看得到,注射方法跟屬性的代碼都是固定的.
因此我們可以把代碼封裝成一個類JBInject,只需要Activity作為參數(shù)就可以了.

JBInject.java


public class JBInject {
    private static boolean flag;
    
    public static boolean inject(final Activity activity) {
        flag = true;
        try {
            Class<?> cls = activity.getClass();
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field field : declaredFields) {
                ViewInject viewInject = field.getAnnotation(ViewInject.class);
                if (viewInject != null) {
                    field.setAccessible(true);
                    View view = activity.findViewById(viewInject.value());
                    field.set(activity, view);
                }
            }
            
            Method[] declaredMethods = cls.getDeclaredMethods();
            for (final Method method : declaredMethods) {
                OnClick onClick = method.getAnnotation(OnClick.class);
                if (onClick != null) {
                    method.setAccessible(true);
                    View view = activity.findViewById(onClick.value());
                    view.setOnClickListener(new OnClickListener() {
                        
                        @Override
                        public void onClick(View v) {
                            try {
                                method.invoke(activity);
                            } catch (Exception e) {
                                e.printStackTrace();
                                flag = false;
                            }
                        }
                    });
                    
                }
                
            }
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }
    
}

MainActivity.java



public class MainActivity extends Activity implements OnClickListener {
    
    @ViewInject(R.id.tv)
    private TextView    tv;
                        
    @ViewInject(R.id.btn)
    private Button      btn;
                        
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        JBInject.inject(MainActivity.this);
        
        btn.setOnClickListener(this);
        
    }
    
    @Override
    public void onClick(View v) {
        tv.setText("ViewInject注解成功!");
    }
    
    @OnClick(R.id.btn1)
    public void clickBtn() {
        tv.setText("OnClick注解成功!");
    }
    
}

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

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,777評論 25 709
  • 上篇我們講述了,我們每個人都需要團(tuán)隊,那加入團(tuán)隊,我們都需要遵循什么原則呢?這篇我們來詳解一下。主要通過團(tuán)隊管理的...
    思維浪人閱讀 817評論 1 1
  • 從前有一個小女孩,她和父母去野外玩,小女孩看那些紛飛的楓葉,突出奇想:“我可以做一個楓葉書簽”,于是小女孩找起...
    程旭陽閱讀 315評論 1 2
  • 知識是良好的創(chuàng)造性思維的根本,但是死記硬背一些知識是遠(yuǎn)遠(yuǎn)不夠的,這些知識必須被消化,而且最終以“鮮活的結(jié)合和嶄新的...
    茗姐說閱讀 415評論 0 0

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