Android-注解详解

Android-注解详解

背景

上一章,我们研究了 Java注解 的用法;紧跟着这章研究一下Android相关的注解的使用,比如我们熟悉的ButterKnife就是使用注解的方式实现对其使用,掌握注解的使用方法极大的方便了我们的开发效率


环境

使用Android注解前需要导入相关的包

 compile 'com.android.support:support-annotations:latest.integration'

注意:如果我们已经引入了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;
    }
}

估计大家常用的就是这样的方式去解决这样的需求,但是上述实现存在一个问题:setUserType设置的是一个int类型的变量,既然如此我们可以如此调用:

    UserI userI=new UserI();
    /*正确调用*/
    userI.setUserType(userI.childe);

    /*错误调用*/
    userI.setUserType(100);

错误方式下的调用也不会抛出异常,所以这样的实现方式存在逻辑泄漏的危险!


方案二

既然如此:想必这个时候大家想到的解决办法就是枚举了,下面研究下枚举的实现

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
    }
}

调用:

   UserE userE=new UserE();
   userE.setUserType(UserE.UserEmun.childe);

从实现方式和逻辑上看,方案二枚举确实能解决方案一存在的漏洞,但是这里有一点需要注意:Enum因为其相比方案一的常量来说,占用内存相对大很多而受到曾经被Google列为不建议使用。

既然枚举也有它相关的缺陷,那如何完美解决这样的需求呢,以下完美实现-就是Android中用注解来替换java的枚举;


完美方案

Android中新引入的替代枚举的注解有IntDefStringDef,他们唯一的区别一个是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{}

}

@Retention表示注解类型的存活时长和@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;
    }
}

使用我们自定义的注解类UserInters,在get方法上定义返回类型,和set方法中设置传入参数的类型,这样在调用getset方法时,编译器会自动帮我们检测是否合法!

  UserInter userInter=new UserInter();
  userInter.setUserType(UserInter.childe);

  userInter.setUserType(UserInter.other);

在调用的地方我们先正确的设置一个IntDef定义的childe,再设置一个没有用IntDef修饰的other对象:

结果:
这里写图片描述

从结果图片中可以发现,当设置一个没有被IntDef定义的对象时,编译器直接抛出了异常,到此我们就完美解决了第一种方案的泄露和第二中方案中的占用内存相对大的问题;


资源类型注解

常用的资源注解:

name exp
AnimRes 动画
AnimatorRes animator资源类型
AnyRes 任何资源类型
ArrayRes 数组资源类型
AttrRes 属性资源类型
BoolRes bool类型资源类型
ColorRes 颜色资源类型
DimenRes 长度资源类型
DrawableRes 图片资源类型
IdRes 资源id
InterpolatorRes 动画插值器
LayoutRes layout资源
MenuRes menu资源
RawRes raw资源
StringRes 字符串资源
StyleRes style资源
StyleableRes Styleable资源类型
TransitionRes transition资源类型
XmlRes xml资源

资源注解是为了防止我们在使用程序资源的时候,错误传递资源类型给函数,导致程序错误!

@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));
    }

}

我们定义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);
    }
}

结果:

这里写图片描述

编译器提示警告

@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));
    }

}

错误提示结果:

这里写图片描述

我们指定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));
    }

}

结果:

这里写图片描述


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);
    }
}

错误提示结果:

这里写图片描述

这里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);
    }

}

用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);
    }

}

错误提示结果:

这里写图片描述


@CallSuper注解

@CallSuper注解主要是用来强调在覆盖父类方法时,需要实现父类的方法,及时调用对应的super.***方法,当用@CallSuper修饰了该方法,如果子类覆盖的后没有实现对呀的super方法会抛出异常

正常使用:

public class CallSuperT {

    @CallSuper
    protected  void init(){

    }

    class T extends CallSuperT{

        @Override
        protected void init() {
            super.init();
        }
    }
}

首先定义一个CallSuperT父类,然后生成一个T继承CallSuperT覆盖其init()方法,父类中的init()采用@CallSuper定义,如果我们去掉子类覆盖的super.init();方法,AS在编译时会抛出错误信息

修改:


public class CallSuperT {

    @CallSuper
    protected  void init(){

    }

    class T extends CallSuperT{

        @Override
        protected void init() {
        }
    }
}

错误提示结果:

这里写图片描述


@CheckResult注解

假设你定义了一个方法返回一个值,你期望调用者用这个值做些事情,那么你可以使用@CheckResult注解标注这个方法,强制用户定义一个相应的返回值,使用它!

使用:
首先修改上面的CallSuperT ,定义一个retrunI方法返回一个int类型

public class CallSuperT {

    @CheckResult
    public int retrunI(){
        return 1;
    }
}

正确调用:

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();
    }
}

如果这里去掉返回类型的定义对象: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();
    }
}

错误提示结果:

这里写图片描述


总结:

注解的作用:

  • 提高我们的开发效率

  • 更早的发现程序的问题或者错误

  • 更好的增加代码的描述能力

  • 更加利于我们的一些规范约束

  • 提供解决问题的更优解

使用Java注解和Android注解,能大幅度的提高我们的开发效率已经开发过程中的及时校验,避免一些因开发者疏忽而导致的问题,非常的实用!


建议

如果你有任何的问题和建议欢迎加入QQ群告诉我!

发布了70 篇原创文章 · 获赞 206 · 访问量 149万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览