Android注解框架

java的注解是在5.0中引入的,我们平时在开发中也是见过一些注解的,其一般是在代码中以@开头的,比如:@override、@RequiresApi。注解(Annotation),也叫元数据,一种代码级别的说明,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。


google在SDK中提供了一些专门用于android的注解,在包:android.support.annotation中。需要注意的是我们添加注解后只能给编译器提够信息来验证我们写的代码是否和我们想要的逻辑是一样的,并不能给编译后的代码带来逻辑的矫正。也就是说注解只是用来给我们写代码的时候发现错误,而不能给我们写完代码编译出来的代码提供任何的影响。


support包中对我们非常有用,并且可以经常使用到的有:ColorRes、IdRes、MainThread、NonNull、WorkerThread。当然我们还可以定义自己的逻辑去实现一个注解类,比如使用@FindId(id=xxx)这个注解去实现findViewById的功能,类似于开源框架butterknife。需要注意的是注解是通过发射的方式去获取数据的,对性能有一定的影响。

java为我们提够了4个元注解,元注解就是解释注解的注解,我们需要实现自定义注解类也是基于此来实现的。首先来看这四中注解:

1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited

这些都定义在包:java.lang.annotation中,android默认是引入lang包的,所以可以直接使用这些元注解。

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。


定义注解格式:
  public @interface 注解名 {定义体}

注解参数的可支持数据类型:

  1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
  2.String类型
  3.Class类型
  4.enum类型
  5.Annotation类型
  6.以上所有类型的数组

下面我们来正式着手自定义注解,其功能就是绑定布局文件中的控件。


首先定义布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:onClick="loadImg"
        android:text="显示图片" />

    <ImageView
        android:id="@+id/imgv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY" />
</LinearLayout>

接着来看我们写的注解类:

@Retention(RetentionPolicy.RUNTIME)
public @interface FindId {
    int id() default R.id.imgv;//default后面可以定义一个该属性的默认值
}

然后我们来看看activity里面的代码:

public class MainActivity extends AppCompatActivity {
    private static final String IMGURL = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494851795717&di=4bffc5349ce44a187f23834d830c6103&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01ef6e5568b1f000000127168b75c2.jpg%40900w_1l_2o_100sh.jpg";
    @FindId(id = R.id.imgv)
    ImageView imgv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BindUtils.bind(this);
    }

    public void loadImg(View view) throws Exception {
        new AsyncTask<Void, Void, Bitmap>() {
            @Override
            protected Bitmap doInBackground(Void... params) {
                try {
                    return BitmapFactory.decodeStream(new URL(IMGURL).openStream());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);
                imgv.setImageBitmap(bitmap);
            }
        }.execute();
    }

}

我们来看其中使用到的工具类BindUtils:

public class BindUtils {
    public static void bind(Activity activity) {
        try {
            Class<?> clazz = activity.getClass();
            Field[] fields = clazz.getDeclaredFields();//获得Activity中声明的字段
            for (Field field : fields) {
                // 查看这个字段是否有我们自定义的注解类标志的
                if (field.isAnnotationPresent(FindId.class)) {
                    FindId inject = field.getAnnotation(FindId.class);
                    int id = inject.id();
                    if (id > 0) {
                        field.setAccessible(true);
                        field.set(activity, activity.findViewById(id));//给我们要找的字段设置值
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }
}


manifest文件中添加网络权限所有的代码就完了,看一下运行效果:


点击显示图片按钮后的显示效果:


需要注意的是我们这里必须使用反射的方式去绑定,对性能是有影响的,butterknife等注解类的框架也是有这个问题的,所以对性能要求高的app是不会使用这种框架的,如何取舍取决于实际情况。


总结

注解的使用我建议大家使用support库中提供的来做编译阶段的错误校验,这种使用方式在编译后是不会有性能影响的。对于使用butterknife这种类似的运行时注解是不建议大家使用的,一切对应用性能有影响的东西我们必须谨慎使用,否则在以后的性能优化的时候会带来很多的工作量。我们应该平时就积累性能方面的知识,并且在编码是避免此类问题,这样开发出来的app才会给用户一个好的体验,更多的知识欢迎关注我的公众号和微博,让我们一块学习,一块进步吧!





欢迎关注我的微信公众号“android教科书”,最新最好的文章第一时间送到手!可以扫描下面的二维码来关注:






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值