什么是注解
相信大家對于這行代碼很熟悉了
@Override
但是肯定很多人都只是知道這行代碼是重寫父類方法的時候會用到,但并不知道它是什么。
其實這就是一種注解,可以理解成它標識了變量或者方法的某種屬性。
那么看看它的具體實現
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
根據上面這些信息我們得出這幾個問題
1) 關鍵字@interface : @interface是Java中表示聲明一個注解類的關鍵字。使用@interface 表示我們已經繼承了java.lang.annotation.Annotation類,這是一個注解的基類接口,
2) 注解再次被注解 : 注解的注解叫做元注解包括 @Retention: 定義注解的保留策略;@Target:定義注解的作用目標; @Document:說明該注解將被包含在javadoc中; @Inherited:說明子類可以繼承父類中的該注解四種。
3) 注解的注解里面的參數
@Retention(RetentionPolicy.SOURCE)//注解僅存在于源碼中,在class字節碼文件中不包含
@Retention(RetentionPolicy.CLASS)// 默認的保留策略,注解會在class字節碼文件中存在,但運行時無法得
@Retention(RetentionPolicy.RUNTIME)// 注解會在class字節碼文件中存在,在運行時可以通過反射獲取到
@Target(ElementType.TYPE) ? //接口、類、枚舉、注解
@Target(ElementType.FIELD) //字段、枚舉的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法參數
@Target(ElementType.CONSTRUCTOR) ?//構造函數
@Target(ElementType.LOCAL_VARIABLE)//局部變量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
現在我們大體上了解了注解是什么東西,那么我想做一個這樣的效果。
之前我們獲取控件使用的是這樣的代碼
TextView text = (TextView) findViewById(R.id.text);
當我們的布局比較復雜的時候,獲取控件的代碼就得寫好長,而且都是重復的。這時候注解式綁定就應運而生了,比如XUtils框架等就實現了這些功能。
實現效果
通過注解實現setContentView、findViewById、setOnClickListener
效果
@ContentView(id = R.layout.activity_main)
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
? ?@ViewInject(id = R.id.button1, clickable = true)
? ?private Button button1;
? ?@ViewInject(id = R.id.button2)
? ?private Button button2;
? ?@Override
? ?protected void onCreate(Bundle savedInstanceState) {
? ? ? ?super.onCreate(savedInstanceState);
? ? ? ?AnnotateUtils.inJectContentView(this);
? ? ? ?AnnotateUtils.inJectView(this);
? ?}
? ?@Override
? ?public void onClick(View v) {
? ?}
}
邏輯實現
1) 編寫兩個類Override的Annotation:ContentView、ViewInject
2) 編寫一個AnnoteUtils類用來檢測添加了注解的類、變量、方法,并且根據值執行對應的操作。
代碼實現
ContentView.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
? ?int id();//layout資源值
}
ElementType.TYPE表示著這個注解作用于類;RetentionPolicy.RUNTIME表示這個注解在運行時可以通過反射獲取到
ViewInject.java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
? ?int id();//控件id
? ?boolean clickable() default false;
}
ElementType.TYPE表示著這個注解作用于字段;
AnnotateUtils.java
public class AnnotateUtils {
? ?public static void injectViews(Object object, View sourceView){
? ? ? ?Field[] fields = object.getClass().getDeclaredFields();
? ? ? ?for (Field field : fields){
? ? ? ? ? ?ViewInject viewInject = field.getAnnotation(ViewInject.class);
? ? ? ? ? ?if(viewInject != null){
? ? ? ? ? ? ? ?int viewId = viewInject.id();
? ? ? ? ? ? ? ?boolean clickable = viewInject.clickable();
? ? ? ? ? ? ? ?if(viewId != -1){
? ? ? ? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ? ? ? ?field.setAccessible(true);
? ? ? ? ? ? ? ? ? ? ? ?field.set(object, sourceView.findViewById(viewId));
? ? ? ? ? ? ? ? ? ? ? ?if(clickable == true){
? ? ? ? ? ? ? ? ? ? ? ? sourceView.findViewById(viewId).setOnClickListener((View.OnClickListener) (object));
? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ?} catch (Exception e) {
? ? ? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?}
? ? ? ? ? ?}
? ? ? ?}
? ?}
? ?public static void inJectContentView(Activity activity){
? ? ? ?Class activityClass = ?activity.getClass();
? ? ? ?ContentView contentView = activityClass.getAnnotation(ContentView.class);
? ? ? ?if(contentView != null){
? ? ? ? ? ?int layoutId = contentView.id();
? ? ? ? ? ?try {
? ? ? ? ? ? ? ?Method method = activityClass.getMethod("setContentView", int.class);
? ? ? ? ? ? ? ?method.invoke(activity, layoutId);
? ? ? ? ? ?} catch (Exception e) {
? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ?}
? ? ? ?}
? ?}
}
原理就是在AnnotateUtils通過傳入的Object對象獲得在類中注解了的字段,方法以及類本身。被執行對應的操作。具體原理在下一小節詳細講述。
反射機制
JAVA反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
用處:
1) 在運行時判斷任意一個對象所屬的類;
2) 在運行時構造任意一個類的對象;
3) 在運行時判斷任意一個類所具有的成員變量和方法;
4) 在運行時調用任意一個對象的方法;
5) 生成動態代理。
Field[] fields = object.getClass().getDeclaredFields();
這句代碼的意思就是getClass獲得類,然后getDeclaredFields獲得類中的所有屬性。
類似的方法有 :
getName():獲得類的完整名字。
getFields():獲得類的public類型的屬性。
getDeclaredFields():獲得類的所有屬性。
getMethods():獲得類的public類型的方法。
getDeclaredMethods():獲得類的所有方法。
getMethod(String name, Class[] parameterTypes):獲得類的特定方法,name參數指定方法的名字,parameterTypes參數指定方法的參數類型。
getConstructors():獲得類的public類型的構造方法。
getConstructor(Class[] parameterTypes):獲得類的特定構造方法,parameterTypes參數指定構造方法的參數類型。
newInstance():通過類的不帶參數的構造方法創建這個類的一個對象。
Method method = activityClass.getMethod("setContentView", int.class);
method.invoke(activity, layoutId);
getMethod中的第一個參數是methodname,第二個參數是參數類型集合,通過這兩個參數得到要執行的Method。
method.invoke中的第一個參數是執行這個方法的對象,第二個參數是方法參數。執行該Method.invoke方法的參數是執行這個方法的對象owner,和參數數組args,可以這么理解:owner對象中帶有參數args的method方法。返回值是Object,也既是該方法的返回值。
再次基礎上還有
public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
Class ownerClass = owner.getClass();
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName,argsClass);
return method.invoke(owner, args);
}
public Object invokeStaticMethod(String className, String methodName,
Object[] args) throws Exception {
Class ownerClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName,argsClass);
return method.invoke(null, args);
} ?
?