IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android 注解原理学习及自定义注解 -> 正文阅读

[移动开发]Android 注解原理学习及自定义注解

书山有路勤为径,学海无涯苦做舟,苦海无涯,回头是岸

android注解,是JDK5.0引入的一种注解机制,主要是用于减少一些繁琐的工作,比如:findViewById...。现在有许多框架帮我们实现了相关的注解,方便且实用。但闲暇之余还是可以了解一下其中的原理,知其然也知其所以然。

下面我们就以简单的例子,来简单的学习一下android注解:

一、android 注解介绍:

1.1、注解解释:

JDK5.0内置了3个标准注解,4个元注解

@Override:检查方法是否是重写方法

@Desprecated:标记元素被废弃,使用该注解的元素,编译器会发出警告。

@SuppressWarnings:指示编译器忽略警告信息。

@Target:标记注解可用在什么地方(元注解)

@Retention:标记注解可用在什么地方(元注解)

@Documented:标记注解是否出现在 Javadoc 中(元注解)

@Inherited:标记标注的Annotation是否具有继承性(元注解)

在JDK7.0之后,额外增加了3个注解:

@SafeVarargs:忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告

@FunctionalInterface:标识一个匿名函数或函数式接口

@Repeatable:标识某注解可以在同一个声明上使用多次

注解有多个,详细的这儿不做过多介绍,自定义注解时,主要用到@Target和@Retention两个元注解,下面介绍一下这两个注解

1.2、@Target使用介绍

@Target:标记用于什么地方,接收参数为枚举ElementType中的元素,下面来看一下ElementType这个枚举:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

通过ElementType这个枚举,我们可以看出,@Target可以用于类、属性、方法、参数、构造方法等等地方,参数可以设置多个,例如:

@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})

表示这个注解可用于属性、类、方法上面。

1.3、@Target使用介绍使用

@Retention:标记注解的策略,接收参数为枚举RetentionPolicy中的元素,我们来看一下RetentionPolicy:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,//源文件,编译时丢弃,class字节码文件中不包含

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,//默认策略,编译时保留,class字节码中存在,运行时丢弃,运行时无法获得

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME//编译时保留,运行时保留,可通过反射获得
}

具体含义看代码注释,此处不再重复解释

二、自定义注解:?

2.1、定义注解

定义注解时,需要使用到@interface关键字,类似下面的定义;

@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Myannotation {
    int viewId() default 0;
    int layoutId() default 0;
    int onclick() default 0;
}

?此处定义了一个可用于属性、类方法的注解,注解接收三个int类型的参数,但都有默认值,默认值为0 ,所以使用的时候可以只传入其中的一部分。

2.2、使用注解

2.1中定义的注解名称为Myannotation,使用注解的方式为在需要注解的属性、方法、类前面使用@Myannotation(参数)进行使用,例如以下代码:

@Myannotation(layoutId = R.layout.activity_main)//注解类
public class MainActivity extends AppCompatActivity {
    private TXCloudVideoView mVideoViewAnchor;

    @Myannotation(viewId = R.id.button_1)//注解属性
    private Button button1;

    @Myannotation(onclick = R.id.button_1)//注解方法
    public void clickButton(View v){
        Log.d("test", "被点击到了");

    }
}

2.3、注解如何生效??

注解不能凭空生效,需要我们去对他进行解释处理,例如以下代码:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AnnotionUtils.injectActivity(this);//处理注解
//        TestAsyncTask task = new TestAsyncTask();
        Integer[] years = new Integer[3];
        button1.setText("click me,click me");
}

?利用 AnnotionUtils.injectActivity(this);这句代码对注解进行处理

接下来,我们从injectActivity(Activity activity)这个方法开始,看一下整个的处理流程:

public static void injectActivity(Activity activity){
    if(activity.getClass().isAnnotationPresent(Myannotation.class)){
        Myannotation myannotation =  activity.getClass().getAnnotation(Myannotation.class);
        activity.setContentView(myannotation.layoutId());
        injectClick(activity);//处理点击方法
        injectView(activity);//处理属性
    }

}

?代码比较简单,主要的流程是通过isAnnotationPresent这个方法,判断指定类型的注释是否存在于此元素上,如果存在,则返回TRUE。,然后通过getAnnotation(Class<A> annotationClass)方法,去获取到对应的注解对象,通过activity的setContentView绑定对应的布局文件layoutId.

接下来,我们再来看一下处理点击的方法,代码如下:

public static void injectClick(Activity activity){
    Class<?> activityClass = activity.getClass();
    Method[] methods = activityClass.getMethods();//获取方法

    for (int i = 0;i < methods.length;i++) {

        if (methods[i].isAnnotationPresent(Myannotation.class)){
            Myannotation myannotation = methods[i].getAnnotation(Myannotation.class);
            Log.d("test", "  |   " + myannotation.onclick());
            int finalI = i;
            activity.findViewById(myannotation.onclick()).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        methods[finalI].invoke(activity,v);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

}

?首先通过activityClass.getMethods()方法获取到class中所有的方法(注:此处涉及到反射的知识点,我们下期一起学习一下反射)

然后遍历所有的方法,通过isAnnotationPresent方法判断,方法中是否含有Myannotation注解,如果含有,则做以下两步操作:

1、对传入的控件id,设置OnClickListener监听器

2、在onclick方法中调用被注解的方法。这儿要使用到反射中的invoke方法,先在此处简单的介绍一下这个方法,方法生命如下:

第一个参数,是调用此方法的对象,比如我们此处是要调用activity中的clickButton方法,所以第一个参数传入activity对象,Object... args是一个可变参数,clickButton接收哪些参数,此处就传入哪些参数。

methods[finalI].invoke(activity,v);在示例中,这样就达到了在点击id为button_1的button时,调用了activity中的clickButton方法。

最后我们来看一下处理属性的方法,代码如下:

这儿流程比较简单,也涉及到了反射的相关知识,简单来说分为两步:

1、 调用activityClass.getDeclaredFields()获取所有属性

2、通过isAnnotationPresent判断属性是否含有Myannotation,如果有,调用属性的set方法对属性进行赋值。

这里流程比较简单,但有两处需要注意(注释中也说明了):

1、获取属性,注意getDeclaredFields和getFields的区别,getFields只能获取到public的属性

2、获取到的私有属性赋值前,一定不能忘记设置setAccessible为true,否则不能修改成功

完整代码这边如下:

Myannotation:
@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Myannotation {
    int viewId() default 0;
    int layoutId() default 0;
    int onclick() default 0;
}

?MainActivity:

@Myannotation(layoutId = R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
    private TXCloudVideoView mVideoViewAnchor;

    @Myannotation(viewId = R.id.button_1)
    private Button button1;

    @Myannotation(onclick = R.id.button_1)
    public void clickButton(View v){
        Log.d("test", "被点击到了");

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AnnotionUtils.injectActivity(this);//处理注解
        Integer[] years = new Integer[3];
        button1.setText("click me,click me");
    }
}
AnnotionUtils:
public class AnnotionUtils {
    public static void injectActivity(Activity activity){
        if(activity.getClass().isAnnotationPresent(Myannotation.class)){
            Myannotation myannotation = activity.getClass().getAnnotation(Myannotation.class);
            activity.setContentView(myannotation.layoutId());
            injectClick(activity);//处理点击方法
            injectView(activity);//处理属性
        }

    }
    public static void injectView(Activity activity){

        Class<?> activityClass = activity.getClass();
        Field[] fields = activityClass.getDeclaredFields();//获取属性,注意getDeclaredFields和getFields的区别,getFields只能获取到public的属性
        for (int i = 0;i < fields.length;i++) {
            if (fields[i].isAnnotationPresent(Myannotation.class)){
                try {
                    Log.d("test", fields[i].getName() + "  |   " + fields[i].getAnnotation(Myannotation.class).viewId());
                    fields[i].setAccessible(true);//私有属性一定不能忘记设置access
                    fields[i].set(activity,activity.findViewById(fields[i].getAnnotation(Myannotation.class).viewId()));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

    }
    public static void injectClick(Activity activity){
        Class<?> activityClass = activity.getClass();
        Method[] methods = activityClass.getMethods();//获取方法

        for (int i = 0;i < methods.length;i++) {

            if (methods[i].isAnnotationPresent(Myannotation.class)){
                Myannotation myannotation = methods[i].getAnnotation(Myannotation.class);
                Log.d("test", "  |   " + myannotation.onclick());
                int finalI = i;
                activity.findViewById(myannotation.onclick()).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        try {
                            methods[finalI].invoke(activity,v);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }

    }
}

?xml布局文件:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.tencent.rtmp.ui.TXCloudVideoView
        android:id="@+id/video_view_anchor"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

java反射:java 反射原理学习_weixin_43863193的博客-CSDN博客?

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-09-21 00:40:22  更:2022-09-21 00:42:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 4:36:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码