運行時註解

在看這篇文章以前對於不知道什麼是註解的建議先看上一篇《JAVA註解》 穿越門 ,若是知道的話就能夠跳過了。java

一. 概述

首先在講運行時註解以前,有必要先說一下註解其存在週期。對於JAVA自定義註解其存在的週期主要和其元註解android

@Retention
複製代碼

的賦值有關。git

元註解的賦值一共有以下三種:github

  • RetentionPolicy.SOURCE( 註解只在源碼階段保留,在編譯器進行編譯時它將被丟棄忽視。)
  • RetentionPolicy.CLASS (註解只被保留到編譯進行的時候,它並不會被加載到 JVM 中。 )
  • RetentionPolicy.RUNTIME (註解能夠保留到程序運行的時候,它會被加載進入到 JVM 中,因此在程序運行時能夠獲取到它們。)

這3個生命週期分別對應於:Java源文件(.java文件) ---> .class文件 ---> 內存中的字節碼bash

咱們的運行時註解對應標註爲ide

@RetentionPolicy.RUNTIME
複製代碼

的註解,即代碼在內存中運行時可獲取處理的註解。post

二.定義添加註解

2.1 自定義註解

本次自定義註解目標爲實現一個汽車類信息註解(CarInfo),該註解做用於Car類的值上。可實現不使用set方法,經過註解給該值添加對象並完成初始化的功能。性能

實現該註解第一步需在annotation包下自定義一個***CarInfo***的註解,代碼以下ui

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CarInfo {
    String name () default "" ;
    int size () default 0 ;
}
複製代碼

從元註解的信息可知,該註解可做用於值,生命週期到運行時一直存在,該註解主要包括車名和車數量兩個內容。this

2.2 添加註解

在MainActivity類中定義一個Car類的值,而後在他上面添加CarInfo註解,並添加name和size相關屬性。添加完該註解之後,咱們在代碼運行時就能獲取car值上的註解內容了。

@CarInfo(name = "BMW",size = 100)
    Car car;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        //這裏咱們要首先註冊一下這個類
        AnnotationCar.instance().inject(this);
        //當程序運行的時候這裏將會輸出該類Car的屬性值。
        Log.e("WANG","Car is "+car.toString());
    }

複製代碼

三.處理註解

運行時註解核心的內容就是註解處理。 註解處理有兩個核心問題:

  1. 如何經過代碼去獲取註解?
  2. 何時去獲取註解?

對於問題一,答案就是經過反射去獲取註解。 思路大概以下:

  1. 根據傳入的對象獲取到該對象對應的類。
  2. 經過該類獲取到類中的全部值
  3. 從這些值中篩選添加了CarInfo註解的值
  4. 獲取到這些值上的註解,根據註解內容,給該對象初始化賦值。

對於問題二,具體問題須要具體分析,不過通常可把時機放早點。我放在onCreate方法內。

no code no truth

註解處理相關代碼

public class AnnotationCar {
    private static AnnotationCar annotationCar;
    public static AnnotationCar instance(){
        synchronized (AnnotationCar.class){
            if(annotationCar == null){
                annotationCar = new AnnotationCar();
            }
            return annotationCar;
        }
    }

    public void inject(Object o){
        Class<?> aClass = o.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field:declaredFields) {
            if(field.getName().equals("car") && field.isAnnotationPresent(CarInfo.class)) {
                CarInfo annotation = field.getAnnotation(CarInfo.class);
                Class<?> type = field.getType();
                if(MainActivity.Car.class.equals(type)) {
                    try {
                        field.setAccessible(true);
                        field.set(o, new MainActivity.Car(annotation.name(), annotation.size()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

複製代碼

註解注入過程

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        //這裏咱們要首先註冊一下這個類
        AnnotationCar.instance().inject(this);
        //當程序運行的時候這裏將會輸出該類Car的屬性值。
        Log.e("WANG","Car is "+car.toString());
    }

複製代碼

運行結果

2018-12-25 17:07:09.935 12026-12026/android.weifeng.com.annotationtest E/WANG: Car is Car{name='BMW', size=100}
複製代碼

獲取到了註解中的內容,註解成功。

四.思考

  1. 運行時處理註解內容過程須要大量用到Java反射,性能必然受影響。
  2. 運行時註解注入放到onCreate中,會影響該Activity的啓動時間,可是許多狀況須要又不得不放在onCreate中處理。
  3. 針對上面的問題,能夠使用編譯時註解來代替運行時註解。編譯時註解是在代碼編譯階段就生成了相應的代碼,不存在大量反射的問題,同時在onCreate中代碼運行時間也能夠縮短。

示例代碼地址 :github.com/yitianbu/An…

相關文章
相關標籤/搜索