注解是代碼里的特殊標記,這些標記可以在編譯、類加載、運行時被讀取,並執行相應的處理。
在運行時讀取需要使用Java反射機制進行處理。
一、注解的作用:
注解將一些本來重復性的工作,變成程序自動完成,簡化和自動化該過程。比如用於生成Java doc,比如編譯時進行格式檢查,比如自動生成代碼等,用於提升軟件的質量和提高軟件的生產效率。
二、注解都有哪些
1、Java提供了四種元注解,專門負責新注解的創建工作,即注解其他注解。
@Target
定義了Annotation所修飾的對象范圍,取值:
ElementType.CONSTRUCTOR:用於描述構造器
ElementType.FIELD:用於描述域
ElementType.LOCAL_VARIABLE:用於描述局部變量
ElementType.METHOD:用於描述方法
ElementType.PACKAGE:用於描述包
ElementType.PARAMETER:用於描述參數
ElementType.TYPE:用於描述類、接口(包括注解類型) 或enum聲明
@Retention
定義了該Annotation被保留的時間長短,取值:
- RetentionPoicy.SOURCE:注解只保留在源文件,當Java文件編譯成class文件的時候,注解被遺棄;用於做一些檢查性的操作,比如 @Override 和 @SuppressWarnings
- RetentionPoicy.CLASS:注解被保留到class文件,但jvm加載class文件時候被遺棄,這是默認的生命周期;用於在編譯時進行一些預處理操作,比如生成一些輔助代碼(如 ButterKnife)
- RetentionPoicy.RUNTIME:注解不僅被保存到class文件中,jvm加載class文件之后,仍然存在;用於在運行時去動態獲取注解信息。
@Documented
標記注解,用於描述其它類型的注解應該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化,不用賦值。
@Inherited
標記注解,允許子類繼承父類的注解。 這里一開始有點理解不了,需要斷句一下,允許子類繼承父類的注解。
2、Android SDK內置的注解
Android SDK 內置的注解都在包com.android.support:support-annotations里,下面以'com.android.support:support-annotations:25.2.0'為例
資源引用限制類:用於限制參數必須為對應的資源類型
@AnimRes @AnyRes @ArrayRes @AttrRes @BoolRes @ColorRes等
線程執行限制類:用於限制方法或者類必須在指定的線程執行
@AnyThread @BinderThread @MainThread @UiThread @WorkerThread
參數為空性限制類:用於限制參數是否可以為空
@NonNull @Nullable
類型范圍限制類:用於限制標注值的值范圍
@FloatRang @IntRange
類型定義類:用於限制定義的注解的取值集合
@IntDef @StringDef
其他的功能性注解:
@CallSuper @CheckResult @ColorInt @Dimension @Keep @Px @RequiresApi @RequiresPermission @RestrictTo @Size @VisibleForTesting
3、自定義注解。。。。
三、如何使用Android Annotation
build.gradle
dependencies {
androidTestCompile('com.android.support:support-annotations:26.1.0') {
force = true
}
}
舉例子:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface BtnOnClick { int[] value(); }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface LayoutInject { int value() default -1; }
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OnClick { int value(); }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ViewById { int value(); }
public class AnnotationParse { private static AnnotationParse instance; public static AnnotationParse getInstance() { if (instance == null) { synchronized (AnnotationParse.class) { if (instance == null) { instance = new AnnotationParse(); return instance; } } } return instance; } public void bind(Activity activity) throws IllegalAccessException { try { injectLayout(activity); bindView(activity); bindOnClick(activity); injectEvent(activity); } catch (Exception ex) { } } private static void bindView(Activity activity) throws IllegalAccessException { //1、獲取字節碼 Class<? extends Activity> aClass = activity.getClass(); //2、獲取Activity中變量 Field[] declaredField = aClass.getDeclaredFields(); for (Field field : declaredField) { //設置允許暴力反射 field.setAccessible(true); //3、獲取變量上的注釋 ViewById annotation = field.getAnnotation(ViewById.class); if (annotation != null) { //4、獲取注釋上的值 int id = annotation.value(); //5、通過ID獲取控件 View view = activity.findViewById(id); //6、控件賦值給變量 field.set(activity, view); } } } //對OnClick注解的“解釋” private static void bindOnClick(final Activity activity) { //1、獲取字節碼對象 final Class<? extends Activity> aClass = activity.getClass(); //2、獲取所有的方法 Method[] declaredMethods = aClass.getDeclaredMethods(); //3、遍歷所有的方法 for (final Method method : declaredMethods) { method.setAccessible(true); //4、獲取方法上的注釋 OnClick annotation = method.getAnnotation(OnClick.class); if (annotation != null) { int id = annotation.value(); View view = activity.findViewById(id); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { //執行注解的方法 method.invoke(activity, null); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }); } } } private static void injectEvent(final Activity activity) { Class<? extends Activity> clazz = activity.getClass(); Method[] methods = clazz.getDeclaredMethods(); for (final Method method2 : methods) { BtnOnClick click = method2.getAnnotation(BtnOnClick.class); if (click != null) { int[] viewId = click.value(); method2.setAccessible(true); Object listener = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(), new Class[]{View.OnClickListener.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method2.invoke(activity, args); } }); try { for (int id : viewId) { View v = activity.findViewById(id); Method setClickListener = v.getClass().getMethod("setOnClickListener", View.OnClickListener.class); setClickListener.invoke(v, listener); } } catch (Exception e) { e.printStackTrace(); } } } } private static int mLayoutId = -1; /** * 注解布局Layout id */ private static void injectLayout(Activity activity) { Class<?> clazz = activity.getClass(); if (clazz.getAnnotations() != null) { if (clazz.isAnnotationPresent(LayoutInject.class)) { LayoutInject inject = clazz.getAnnotation(LayoutInject.class); mLayoutId = inject.value(); activity.setContentView(mLayoutId); } } } }
@LayoutInject(R.layout.activity_main) public class MainActivity extends AppCompatActivity { @ViewById(R.id.tvName) TextView tvName; @ViewById(R.id.tvAge) TextView tvAge; @ViewById(R.id.tvGender) TextView tvGender; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main); try { AnnotationParse.getInstance().bind(this); } catch (IllegalAccessException e) { e.printStackTrace(); } tvName.setText("測試名字"); tvAge.setText("28"); tvGender.setText("男"); } @BtnOnClick({R.id.tvName, R.id.tvAge, R.id.tvGender}) public void btnOnClick(View view) { switch (view.getId()) { case R.id.tvName: tvName.setText("華山論劍"); break; case R.id.tvAge: tvAge.setText("四百多歲"); break; case R.id.tvGender: tvGender.setText("女"); break; } } @OnClick(R.id.tvName) public void textOnClick() { tvName.setText("華山論劍"); } }