1.什么是注解開發(fā)望伦?
控制反轉(zhuǎn)(Inversion of Control):減少大量重復代碼的書寫。如ButterKnife,xUtils...
2.效果預覽
3.怎樣進行注解開發(fā)(屬性注解開發(fā)為例)?
3.1 創(chuàng)建Annotation
//注解使用時所在的地方 ElementType.FIELD -> 方法上面 ElementType.METHOD -> 屬性上面ElementType.TYPE -> 類上面
@Target(ElementType.FIELD)
//檢查的時間 RetentionPolicy.SOURCE -> 編碼時(如override) RetentionPolicy.RUNTIME -> 運行時 RetentionPolicy.CLASS -> 編譯時
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewById {
//@ViewById(R.id.tv_test) 當注解的屬性只有一個時隙疚,可以命名為 value姆另,這樣在使用時可以使用快捷方式 – 直接傳入值,而不是聲明屬性名
int value();
}
3.2 創(chuàng)建類實現(xiàn)注解內(nèi)部邏輯
public class ViewUtils {
public static void inject(Activity activity) {
//傳兩個相同的參數(shù)鸣戴?意思不一樣哦
injectField(activity, activity);
}
}
/**
* 屬性注解
* @param activity 用于尋找目標控件
* @param object 用于反射的類
*/
private static void injectField(Activity activity, Object object) {
//1.找到所有的屬性
Field[] fields = object.getClass().getDeclaredFields();
//2.找到ViewById注解所在的屬性
for (Field field : fields) {
ViewById fieldAnnotation = field.getAnnotation(ViewById.class);
//如果屬性存在
if (fieldAnnotation != null) {
//3.通過注解屬性中的value值找到對應的控件
int resId = fieldAnnotation.value();
View view = activity.findViewById(resId);
//如果id對應的控件存在
if (view != null) {
//可以調(diào)用屬性的私有方法
field.setAccessible(true);
try {
//4.調(diào)用field的set方法重新賦值
field.set(object, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
3.3 在activity中使用
@ViewById(R.id.tv_test)
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewUtils.inject(this);
}
textView.setText("woochen123");
4.和效果圖不一樣?那在補充一點吧
@OnClick注解核心代碼:
/**
* 點擊事件注解
* @param activity
* @param object
*/
private static void injectMethod(Activity activity, Object object) {
//1.找到所在類所有的方法
Method[] methods = object.getClass().getDeclaredMethods();
//2.找到含有注解Onclick的方法
for (Method method : methods) {
OnClick methodAnnotation = method.getAnnotation(OnClick.class);
if(methodAnnotation != null){
//如果方法存在
//3.拿到其中的value值粘拾,并找到相應的控件
int[] resIds = methodAnnotation.value();
for (int resId : resIds) {
View view = activity.findViewById(resId);
if(view != null){
//如果id對應的控件存在窄锅,調(diào)用onClickListener
view.setOnClickListener(new DeclaredClickListener(object,method));
}
}
}
}
}
/**
* 自定義點擊事件監(jiān)聽器
*/
private static class DeclaredClickListener implements View.OnClickListener {
private Method mMethod;
private Object mObject;
public DeclaredClickListener(Object object, Method method) {
mMethod = method;
mObject = object;
}
@Override
public void onClick(View v) {
try {
//可以調(diào)用私有方法
mMethod.setAccessible(true);
//默認調(diào)用一個參數(shù)
mMethod.invoke(mObject,v);
} catch (Exception e) {
try {
//如果拋出異常調(diào)用無參數(shù)的方法
mMethod.invoke(mObject,null);
} catch (Exception e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
5.知識點補充
5.1元注解:修飾其他的注解
@Documented:讓注解信息出現(xiàn)在 document 中
@Retention : 指出注解如何存儲,支持以下三種參數(shù)
- RetentionPolicy.SOURCE : 注解只保留在源碼中缰雇,編譯時會忽略
- RetentionPolicy.CLASS : 更高一級入偷,編譯時被編譯器保留,但是運行時會被 JVM 忽略
- RetentionPolicy.RUNTIME : 最高級械哟,運行時會被保留疏之,可以被運行時訪問
@Target :指出注解作用于(修飾)什么對象,支持以下幾種參數(shù) - ElementType.TYPE : 作用于任何類暇咆、接口锋爪、枚舉
- ElementType.FIELD : 作用于一個域或者屬性
- ElementType.METHOD : 作用于一個方法
- ElementType.PARAMTER : 作用于參數(shù)
- ElementType.CONSTRUCTOR : 作用于構(gòu)造函數(shù)
- ElementType.LOCAL_VARIABLE : 作用于本地變量
- ElementType. ANNOTATION_TYPE : 作用于注解
- ElementType.PACKAGE : 作用于包
@Inherited :當前注解是否可以繼承
5.2注解的特點:
- 形式上和接口很像,都是定義方法爸业,但是區(qū)別在于注解中定義的方法實質(zhì)上是屬性其骄,而返回值是指屬性的類型
- 可以給定義的注解方法賦上默認的值
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Author {
String name() default "woochen123";
String date();
}
5.3注解處理器的類型
運行時處理器
編譯時處理器
6.再說一句
本例中的代碼,是從項目中簡化后提出的扯旷,可能有些地方顯得多余拯爽,可能有些邏輯不夠完善。意思應該是表達出來了(伸手黨就沒辦法咯-_-!)
7.小結(jié)
本例的原理是利用反射+運行時檢查來實現(xiàn)的注解钧忽,原理與xutils的view注入相同毯炮。還有一種是編譯時檢查,ButterKnife是比較有代表性的惰瓜, 它的實現(xiàn)是通過字節(jié)流在本地生成相關(guān)文件否副。前者由于用到反射,會在一定程度上消耗性能(相比視圖的渲染崎坊,就是微不足道了)备禀;后者會將生成的文件打包進apk中,在一定程度上增加apk的體積(相比巨大的三方庫的引入,這也算不上什么啦)曲尸。