环境
使用android注解前需要导入相关的包
compile ‘com.android.support:support-annotations:latest.integration‘
注意:如果我们已经引入了appcompat则没有必要再次引用support-annotations,因为appcompat默认包含了对其引用
使用
Android注解给我们提供了三种主要和其他注释供我们使用:
-
IntDef和StringDef注解;
-
资源类型注解;
-
Null注解;
-
其他实用注解
IntDef和StringDef注解替代枚举
这里我们采用假设一个问题然后一步步解决学习:假设有一个User对象,我们需要记录user类型的变量,如何实现呢?
方案一
1 public class UserI { 2 public static int childe=0x1; 3 public static int man=0x2; 4 public static int girl=0x3; 5 6 7 private int userType; 8 9 public int getUserType() { 10 return userType; 11 } 12 13 public void setUserType(int userType) { 14 this.userType = userType; 15 } 16 }
估计大家常用的就是这样的方式去解决这样的需求,但是上述实现存在一个问题:setUserType
设置的是一个int
类型的变量,既然如此我们可以如此调用:
1 UserI userI=new UserI(); 2 /*正确调用*/ 3 userI.setUserType(userI.childe); 4 5 /*错误调用*/ 6 userI.setUserType(100);
错误方式下的调用也不会抛出异常,所以这样的实现方式存在逻辑泄漏的危险!
方案二
既然如此:想必这个时候大家想到的解决办法就是枚举了,下面研究下枚举的实现
1 public class UserE { 2 3 private UserEmun userType; 4 5 public UserEmun getUserType() { 6 return userType; 7 } 8 9 public void setUserType(UserEmun userType) { 10 this.userType = userType; 11 } 12 13 public static enum UserEmun { 14 childe, 15 man, 16 girl 17 } 18 } 19 20 21 调用: 22 23 UserE userE=new UserE(); 24 userE.setUserType(UserE.UserEmun.childe);
从实现方式和逻辑上看,方案二枚举确实能解决方案一存在的漏洞,但是这里有一点需要注意:Enum
因为其相比方案一的常量来说,占用内存相对大很多而受到曾经被Google
列为不建议使用。
既然枚举也有它相关的缺陷,那如何完美解决这样的需求呢,以下完美实现-就是Android
中用注解来替换java
的枚举;
完美方案
Android中
新引入的替代枚举的注解有IntDef
和StringDef
,他们唯一的区别一个是int
类型,一个是string
类型,下面我们就以IntDef
为例讲解如何使用
构建定义注解
1 public class UserInter { 2 public static final int childe = 0x1; 3 public static final int man = 0x2; 4 public static final int girl = 0x3; 5 public static final int other = 0x4; 6 7 @IntDef({childe, man, girl}) 8 @Retention(RetentionPolicy.SOURCE) 9 public @interface UserInters{} 10 11 }
@Retention
表示注解类型的存活时长和@interface
定义一个注解,具体详细用法可查看上章Java注解
注意:这里定义的other
并没有用IntDef
修饰
使用:
设置变量,设置get和set方法
1 public class UserInter { 2 public static final int childe = 0x1; 3 public static final int man = 0x2; 4 public static final int girl = 0x3; 5 public static final int other = 0x4; 6 7 @IntDef({childe, man, girl}) 8 @Retention(RetentionPolicy.SOURCE) 9 public @interface UserInters{} 10 11 private int userType; 12 13 @UserInters 14 public int getUserType() { 15 return userType; 16 } 17 18 public void setUserType(@UserInters int userType) { 19 this.userType = userType; 20 } 21 }
使用我们自定义的注解类UserInters
,在get
方法上定义返回类型,和set
方法中设置传入参数的类型,这样在调用get
和set
方法时,编译器会自动帮我们检测是否合法!
1 UserInter userInter=new UserInter(); 2 userInter.setUserType(UserInter.childe); 3 4 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 如例讲解:
1 public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 testDo(R.style.AppTheme); 9 testDo(R.string.app_name); 10 } 11 12 13 private void testDo(@StringRes int str){ 14 Log.e("tag","-------->"+getString(str)); 15 } 16 17 }
我们定义testDo
方法传入类型用@StringRes修饰,当方法被调用后编译器会自动匹配和检测,错误会及时的抛出错误异常,所以当调用testDo(R.style.AppTheme);
系统抛出异常如图
Null注解
null注解对应的有两个详细的注解:
-
@NonNull:不能为空
-
@Nullable:可以为空
使用@NonNull注解修饰的参数不能为null。在下面的代码例子中,我们有一个取值为null的msg变量,它被作为参数传递给testDo函数,而该函数要求这个参数是非null的String类型
1 public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 String msg=null; 9 testDo(msg); 10 } 11 12 private void testDo(@NonNull String s){ 13 Log.e("tag","-------->"+s); 14 } 15 }
结果:
编译器提示警告
@Nullable和@NonNull是相反的,使用一样,不啰嗦了!
注意:在使用的过程中发现在最新版本的AndroidStudio中不添加@Nullable和@NonNull,编译器也同样会提示响应的警告,所以这个注解可以忽略使用
其他注解
除了上述的主要功能注解外,还有一些实用的注解
Threading 注解
thread注解是帮助我们指定方法在特定线程中执行处理,如果和指定的线程不一致,抛出异常;Threading 注解类型:
-
@UiThread: UI线程
-
@MainThread :主线程
-
@WorkerThread: 子线程
-
@BinderThread : 绑定线程
下面以@WorkerThread为例:
1 public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 testDo(R.string.app_name); 9 } 10 11 @WorkerThread 12 private void testDo(@StringRes int str){ 13 Log.e("tag","-------->"+getString(str)); 14 } 15 16 }
错误提示结果:
我们指定testDo在子线程中处理,但是当前调用确实在主线程中,所以抛出异常
正确使用:
1 public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 new Thread(new Runnable() { 9 @Override 10 public void run() { 11 testDo(R.string.app_name); 12 } 13 }); 14 } 15 16 @WorkerThread 17 private void testDo(@StringRes int str){ 18 Log.e("tag","-------->"+getString(str)); 19 } 20 21 }
结果:
Value Constraints注解
主要类型:
-
@Size
-
@IntRange
-
@FloatRange
@size使用
定义长度大小,可选择最小和最大长度使用
1 public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 testDo(""); 8 9 testDo("111"); 10 11 testDo("1"); 12 } 13 14 private void testDo(@Size(min = 1,max = 2)String s){ 15 Log.e("tag","-------->"+s); 16 } 17 }
错误提示结果:
这里size定了一个最小和最大长度,所以只有testDo("1")
符合条件,其他调用都抛出了异常
@IntRange使用
IntRange是用来指定int类型范围的注解
1 public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 testDo(0); 9 10 testDo(2); 11 12 } 13 14 private void testDo(@IntRange(from = 1,to = 100)int s){ 15 Log.e("tag","-------->"+s); 16 } 17 18 }
用IntRange定义了一个从1到100的s类型,所以在调用testDo
方法时testDo(0)
不符合要求,会抛出异常
错误提示结果:
@FloatRange使用
FloatRange和IntRange用法一样,不过是指定的是float类型的数据对象,不重复阐述了
使用:
1 public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 testDo(0); 9 10 testDo(2); 11 } 12 13 14 private void testDo(@FloatRange(from = 1.0,to = 100.0)float s){ 15 Log.e("tag","-------->"+s); 16 } 17 18 }
错误提示结果:
@CallSuper注解
@CallSuper
注解主要是用来强调在覆盖父类方法时,需要实现父类的方法,及时调用对应的super.***
方法,当用@CallSuper
修饰了该方法,如果子类覆盖的后没有实现对呀的super
方法会抛出异常
正常使用:
1 public class CallSuperT { 2 3 @CallSuper 4 protected void init(){ 5 6 } 7 8 class T extends CallSuperT{ 9 10 @Override 11 protected void init() { 12 super.init(); 13 } 14 } 15 }
首先定义一个CallSuperT
父类,然后生成一个T
继承CallSuperT
覆盖其init()
方法,父类中的init()
采用@CallSuper定义,如果我们去掉子类覆盖的super.init();
方法,AS在编译时会抛出错误信息
修改:
1 public class CallSuperT { 2 3 @CallSuper 4 protected void init(){ 5 6 } 7 8 class T extends CallSuperT{ 9 10 @Override 11 protected void init() { 12 } 13 } 14 }
错误提示结果:
@CheckResult注解
假设你定义了一个方法返回一个值,你期望调用者用这个值做些事情,那么你可以使用@CheckResult注解标注这个方法,强制用户定义一个相应的返回值,使用它!
使用:
首先修改上面的CallSuperT
,定义一个retrunI
方法返回一个int
类型
1 public class CallSuperT { 2 3 @CheckResult 4 public int retrunI(){ 5 return 1; 6 } 7 }
正确调用:
1 public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 CallSuperT callSuperT = new CallSuperT(); 9 int returns = callSuperT.retrunI(); 10 } 11 }
如果这里去掉返回类型的定义对象:int returns
则会抛出异常
1 public class MainActivity extends AppCompatActivity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 CallSuperT callSuperT = new CallSuperT(); 9 callSuperT.retrunI(); 10 } 11 }
错误提示结果:
总结:
注解的作用:
-
提高我们的开发效率
-
更早的发现程序的问题或者错误
-
更好的增加代码的描述能力
-
更加利于我们的一些规范约束
-
提供解决问题的更优解
使用Java注解和Android注解,能大幅度的提高我们的开发效率已经开发过程中的及时校验,避免一些因开发者疏忽而导致的问题,非常的实用!