本文題目叫Android反射,其實題目起得稍微有點問題,也可理解成java 反射,但是呢,本文是應用在Android環境上的,示例也以Android示例爲主;其次,Android先在已經有了官方AnnotationProcessor編譯時解析實現,已繼承在API中,由於它是編譯時執行,固然不會出現反射中的性能消耗;
反射還不太瞭解的童鞋,你可以先了解以下 java反射
一、什麼是註解
註解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,註釋;
java元註解:(元註解,簡單理解,就是註解上的註解)
@Override 它的作用是對覆蓋超類中方法的方法進行標記
@Deprecated 它的作用是對不應該再使用的方法添加註解
@SuppressWarnings 它的作用是對當前方法或字段異常時的警告
二、簡單使用註解
2.1 瞭解JDK1.5後加的內部註解
//基類
public abstract class Base {
public abstract void one();
@Deprecated //表示該方法爲過時方法,過時不再支持使用
public void two(){};
}
//繼承
public class Base2 extends Base {
@SuppressWarnings("deprecation")
@Override //表示該方法時繼承父類的方法
public void one() {
//當調備註"過時"標籤時,系統會提示有警報,然後調用SuppressWarnings可以取消工具上的報警狀態
super.two();
}
}
下圖爲沒加SuppressWarnings標籤的樣子
2.2 自定義註解的簡單使用
package annot;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定義註解
* @author mwt
*
* 注意了,自定義註解的標誌是“@interface”,要和接口interface區分開噢
*/
//如下代碼,示例中的@Retention指定了什麼時候加載註解類,RetentionPolicy.RUNTIME爲運行時
@Retention(RetentionPolicy.RUNTIME)
//@Target指定當前註解可以加在哪些地方,案例中就是加載到類和方法
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Test1 {
//default 表示整型id默認值爲123
public int id() default 123;
// 字符串類型
public String message() default "默認值";
}
//-----------------------註解使用-------------------------------
package annot;
/**
* 對自定義註解的使用
* @author mwt
*/
@Test1(message="自定義註解開始使用了") //改變單個屬性值
public class TestUse {
public static void main(String[] args) {
//改行代碼是用來判斷當前 “TestUse”類是否加了Test1註解
boolean hasAnnotation = TestUse.class.isAnnotationPresent(Test1.class);
//經過判斷如果加了註解,則進入下一步
if (hasAnnotation) {
//該行代碼是用來獲取當前註解
Test1 testAnnotation = TestUse.class.getAnnotation(Test1.class);
//獲取當前註解中的屬性
System.out.println("value:"+testAnnotation.id()+" \nmessage"+testAnnotation.message());
}
}
}
//----------------日誌輸出
value:123
message自定義註解開始使用了
三、在Android中簡單使用註解(基於反射)
3.1 首先我們先完成兩個自定義註解;分別是對layout和view的註解
package cn.annto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定義註解 - layout
* Created by mwt on 2018/9/26.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface LayoutBind {
//當只有一個屬性的時候,可以使用value來表示,在使用時可以省略掉key和=
public int value();
}
//---------------------------view 註解----------------------
package cn.annto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定義註解 -
* Created by mwt on 2018/9/26.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
public @interface ViewBind {
//當只有一個屬性的時候,可以使用value來表示,在使用時可以省略掉key和=
public int value();
}
3.2 寫一個解讀activity中註解的工具類
package cn.annto;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import java.lang.reflect.Field;
/**
* Created by mwt on 2018/9/26.
* 爲什麼要寫成抽象類呢,因爲我可以強制性繼承類重寫該方法
*/
public abstract class ActivityUntil extends AppCompatActivity {
private int layoutId;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initLayout();
initView();
doWrok();
}
protected abstract void doWrok();
/**
* 該方法主要就是從註解中獲取到註解入的值
*/
private void initLayout() {
//首先判斷一下是否屬於當前註解的子類 ;如果是提供給別人使用的話,務必要有判空操作
boolean isannotation = this.getClass().isAnnotationPresent(LayoutBind.class);
if (isannotation){
//獲取當前的註解
LayoutBind annotation = getClass().getAnnotation(LayoutBind.class);
//獲取註解中的值,並將該值賦給全局變量layoutid,然後供setContentView使用
layoutId = annotation.value();
//吧layout佈局引入
setContentView(layoutId);
}
}
/**
* 初始化activity中生命的字段
*/
private void initView() {
//獲取當前class對象
Class<? extends ActivityUntil> aClass = this.getClass();
//獲取到該class中自定義的所有字段
Field[] declaredFields = aClass.getDeclaredFields();
//便利所有的字段
for (Field field: declaredFields) {
//所有的字段便利出來了,但是我們只需要那些被加過註解的字段
if(null != field.getAnnotations() && field.isAnnotationPresent(ViewBind.class)){
field.setAccessible(true);//所有字段都可訪問修改
//獲取字段上的註解類
ViewBind viewBind = field.getAnnotation(ViewBind.class);
//獲取該字段通過註解注入 值,也就是 R.id.textview .....
int viewid = viewBind.value();
//現在字段獲取到了,對應view 的id也獲取到了,那就應該賦值了
try {
field.set(this,findViewById(viewid));
} catch (IllegalAccessException e) {
e.printStackTrace();
Log.e("error","not find view id");
}
}
}
}
}
3.3 自定義註解和解讀工具類都具備了,讓我們來使用一把吧
package cn.annto;
import android.widget.TextView;
@LayoutBind(R.layout.activity_main)
public class MainActivity extends ActivityUntil {
@ViewBind(R.id.one)
public TextView oneText;
@ViewBind(R.id.two)
public TextView twoText;
@Override
protected void doWrok() {
oneText.setText("註解測試");
twoText.setText("註解通過了");
}
}
運行效果:
本文中第三節Android demo 在github上有完整示例,https://github.com/mawantong/annto/tree/master