Android annotation


注解是代碼里的特殊標記,這些標記可以在編譯、類加載、運行時被讀取,並執行相應的處理。

在運行時讀取需要使用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("華山論劍");
    }
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM