Android-注解详解

lyccsu 2020-04-30

地址:https://blog.csdn.net/wzgiceman/article/details/53483665

Android-注解详解背景上一章,我们研究了 Java注解 的用法;紧跟着这章研究一下Android相关的注解的使用,比如我们熟悉的ButterKnife就是使用注解的方式实现对其使用,掌握注解的使用方法极大的方便了我们的开发效率
环境使用Android注解前需要导入相关的包
 compile ‘com.android.support:support-annotations:latest.integration‘1注意:如果我们已经引入了appcompat则没有必要再次引用support-annotations,因为appcompat默认包含了对其引用
使用Android注解给我们提供了三种主要和其他注释供我们使用:
IntDef和StringDef注解;
资源类型注解;
Null注解;
其他实用注解
IntDef和StringDef注解替代枚举这里我们采用假设一个问题然后一步步解决学习:假设有一个User对象,我们需要记录user类型的变量,如何实现呢?
方案一
public class UserI {    public static  int childe=0x1;    public static  int man=0x2;    public static  int girl=0x3;

    private int userType;
    public int getUserType() {        return userType;    }
    public void setUserType(int userType) {        this.userType = userType;    }}12345678910111213141516估计大家常用的就是这样的方式去解决这样的需求,但是上述实现存在一个问题:setUserType设置的是一个int类型的变量,既然如此我们可以如此调用:
    UserI userI=new UserI();    /*正确调用*/    userI.setUserType(userI.childe);
    /*错误调用*/    userI.setUserType(100);123456错误方式下的调用也不会抛出异常,所以这样的实现方式存在逻辑泄漏的危险!
方案二
既然如此:想必这个时候大家想到的解决办法就是枚举了,下面研究下枚举的实现
public class UserE {
    private UserEmun userType;
    public UserEmun getUserType() {        return userType;    }
    public void setUserType(UserEmun userType) {        this.userType = userType;    }
    public static enum UserEmun {        childe,        man,        girl    }}123456789101112131415161718调用:
   UserE userE=new UserE();   userE.setUserType(UserE.UserEmun.childe);12从实现方式和逻辑上看,方案二枚举确实能解决方案一存在的漏洞,但是这里有一点需要注意:Enum因为其相比方案一的常量来说,占用内存相对大很多而受到曾经被Google列为不建议使用。
既然枚举也有它相关的缺陷,那如何完美解决这样的需求呢,以下完美实现-就是Android中用注解来替换java的枚举;
完美方案Android中新引入的替代枚举的注解有IntDef和StringDef,他们唯一的区别一个是int类型,一个是string类型,下面我们就以IntDef为例讲解如何使用
构建定义注解public class UserInter {    public static final int childe = 0x1;    public static final int man = 0x2;    public static final int girl = 0x3;    public static final int other = 0x4;
    @IntDef({childe, man, girl})    @Retention(RetentionPolicy.SOURCE)    public @interface UserInters{}
}表示注解类型的存活时长和@interface定义一个注解,具体详细用法可查看上章Java注解
注意:这里定义的other 并没有用IntDef修饰
使用:设置变量,设置get和set方法
  public class UserInter {    public static final int childe = 0x1;    public static final int man = 0x2;    public static final int girl = 0x3;    public static final int other = 0x4;
    @IntDef({childe, man, girl})    @Retention(RetentionPolicy.SOURCE)    public @interface UserInters{}
    private int userType;
    @UserInters    public int getUserType() {        return userType;    }
    public void setUserType(@UserInters int userType) {        this.userType = userType;    }}123456789101112131415161718192021使用我们自定义的注解类UserInters,在get方法上定义返回类型,和set方法中设置传入参数的类型,这样在调用get和set方法时,编译器会自动帮我们检测是否合法!
  UserInter userInter=new UserInter();  userInter.setUserType(UserInter.childe);
  userInter.setUserType(UserInter.other);1234在调用的地方我们先正确的设置一个IntDef定义的childe,再设置一个没有用IntDef修饰的other对象:
结果:

从结果图片中可以发现,当设置一个没有被IntDef定义的对象时,编译器直接抛出了异常,到此我们就完美解决了第一种方案的泄露和第二中方案中的占用内存相对大的问题;
资源类型注解常用的资源注解:
nameexpAnimRes动画AnimatorResanimator资源类型AnyRes任何资源类型ArrayRes数组资源类型AttrRes属性资源类型BoolResbool类型资源类型ColorRes颜色资源类型DimenRes长度资源类型DrawableRes图片资源类型IdRes资源idInterpolatorRes动画插值器LayoutReslayout资源MenuResmenu资源RawResraw资源StringRes字符串资源StyleResstyle资源StyleableResStyleable资源类型TransitionRestransition资源类型XmlResxml资源资源注解是为了防止我们在使用程序资源的时候,错误传递资源类型给函数,导致程序错误!
@StringRes 如例讲解:public class MainActivity extends AppCompatActivity {
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);
        testDo(R.style.AppTheme);        testDo(R.string.app_name);    }

    private void testDo(@StringRes int str){        Log.e("tag","-------->"+getString(str));    }
}1234567891011121314151617我们定义testDo方法传入类型用@StringRes修饰,当方法被调用后编译器会自动匹配和检测,错误会及时的抛出错误异常,所以当调用testDo(R.style.AppTheme);系统抛出异常如图


Null注解null注解对应的有两个详细的注解:
@NonNull:不能为空
@Nullable:可以为空
使用@NonNull注解修饰的参数不能为null。在下面的代码例子中,我们有一个取值为null的msg变量,它被作为参数传递给testDo函数,而该函数要求这个参数是非null的String类型
public class MainActivity extends AppCompatActivity {
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);
        String msg=null;        testDo(msg);    }
    private void testDo(@NonNull String s){        Log.e("tag","-------->"+s);    }}123456789101112131415结果:


编译器提示警告
@Nullable和@NonNull是相反的,使用一样,不啰嗦了!
注意:在使用的过程中发现在最新版本的AndroidStudio中不添加@Nullable和@NonNull,编译器也同样会提示响应的警告,所以这个注解可以忽略使用


其他注解除了上述的主要功能注解外,还有一些实用的注解
Threading 注解thread注解是帮助我们指定方法在特定线程中执行处理,如果和指定的线程不一致,抛出异常;Threading 注解类型:
@UiThread: UI线程
@MainThread :主线程
@WorkerThread: 子线程
@BinderThread : 绑定线程
下面以@WorkerThread为例:public class MainActivity extends AppCompatActivity {
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);
        testDo(R.string.app_name);    }
    @WorkerThread    private void testDo(@StringRes int str){        Log.e("tag","-------->"+getString(str));    }
}12345678910111213141516错误提示结果:


我们指定testDo在子线程中处理,但是当前调用确实在主线程中,所以抛出异常
正确使用:
public class MainActivity extends AppCompatActivity {
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {            @Override            public void run() {                testDo(R.string.app_name);            }        });    }
    @WorkerThread    private void testDo(@StringRes int str){        Log.e("tag","-------->"+getString(str));    }
}123456789101112131415161718192021结果:


Value Constraints注解主要类型:
@Size
@IntRange
@FloatRange
@size使用定义长度大小,可选择最小和最大长度使用
public class MainActivity extends AppCompatActivity {
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        testDo("");
        testDo("111");
        testDo("1");    }
    private void testDo(@Size(min = 1,max = 2)String s){        Log.e("tag","-------->"+s);    }}1234567891011121314151617错误提示结果:


这里size定了一个最小和最大长度,所以只有testDo("1")符合条件,其他调用都抛出了异常
@IntRange使用IntRange是用来指定int类型范围的注解
public class MainActivity extends AppCompatActivity {
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);
        testDo(0);
        testDo(2);
    }
    private void testDo(@IntRange(from = 1,to = 100)int s){        Log.e("tag","-------->"+s);    }
}123456789101112131415161718用IntRange定义了一个从1到100的s类型,所以在调用testDo方法时testDo(0)不符合要求,会抛出异常
错误提示结果:


@FloatRange使用FloatRange和IntRange用法一样,不过是指定的是float类型的数据对象,不重复阐述了
使用:
public class MainActivity extends AppCompatActivity {
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);
        testDo(0);
        testDo(2);    }

    private void testDo(@FloatRange(from = 1.0,to = 100.0)float s){        Log.e("tag","-------->"+s);    }
}123456789101112131415161718错误提示结果:


@CallSuper注解@CallSuper注解主要是用来强调在覆盖父类方法时,需要实现父类的方法,及时调用对应的super.***方法,当用@CallSuper修饰了该方法,如果子类覆盖的后没有实现对呀的super方法会抛出异常
正常使用:
public class CallSuperT {
    @CallSuper    protected  void init(){
    }
    class T extends CallSuperT{
        @Override        protected void init() {            super.init();        }    }}123456789101112131415首先定义一个CallSuperT父类,然后生成一个T继承CallSuperT覆盖其init()方法,父类中的init()采用@CallSuper定义,如果我们去掉子类覆盖的super.init();方法,AS在编译时会抛出错误信息
修改:

public class CallSuperT {
    @CallSuper    protected  void init(){
    }
    class T extends CallSuperT{
        @Override        protected void init() {        }    }}123456789101112131415错误提示结果:


@CheckResult注解假设你定义了一个方法返回一个值,你期望调用者用这个值做些事情,那么你可以使用@CheckResult注解标注这个方法,强制用户定义一个相应的返回值,使用它!
使用:首先修改上面的CallSuperT ,定义一个retrunI方法返回一个int类型
public class CallSuperT {
    @CheckResult    public int retrunI(){        return 1;    }}1234567正确调用:
public class MainActivity extends AppCompatActivity {
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);
        CallSuperT callSuperT = new CallSuperT();        int returns = callSuperT.retrunI();    }}1234567891011如果这里去掉返回类型的定义对象:int returns则会抛出异常
public class MainActivity extends AppCompatActivity {
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);
        CallSuperT callSuperT = new CallSuperT();        callSuperT.retrunI();    }}1234567891011错误提示结果:


总结:注解的作用:提高我们的开发效率
更早的发现程序的问题或者错误
更好的增加代码的描述能力
更加利于我们的一些规范约束
提供解决问题的更优解
使用Java注解和Android注解,能大幅度的提高我们的开发效率已经开发过程中的及时校验,避免一些因开发者疏忽而导致的问题,非常的实用!————————————————版权声明:本文为CSDN博主「wzgiceman」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/wzgiceman/java/article/details/53483665

相关推荐