Android新增的註解
環境
使用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) { 14this.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註解,能大幅度的提高我們的開發效率已經開發過程中的及時校驗,避免一些因開發者疏忽而導致的問題,非常的實用!
Android新增的註解