安卓的反射機制

什么是注解

相信大家對于這行代碼很熟悉了

@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);

} ?

?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容