1. 程式人生 > >Android新增的註解

Android新增的註解

父類 參數 內存 信息 studio 重復 添加 tag threading

環境

使用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中新引入的替代枚舉的註解有IntDefStringDef,他們唯一的區別一個是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方法中設置傳入參數的類型,這樣在調用getset方法時,編譯器會自動幫我們檢測是否合法!

  
1 UserInter userInter=new UserInter();
2   userInter.setUserType(UserInter.childe);
3 
4   userInter.setUserType(UserInter.other);

在調用的地方我們先正確的設置一個IntDef定義的childe,再設置一個沒有用IntDef修飾的other對象:

結果:
技術分享圖片

從結果圖片中可以發現,當設置一個沒有被IntDef定義的對象時,編譯器直接拋出了異常,到此我們就完美解決了第一種方案的泄露和第二中方案中的占用內存相對大的問題;


資源類型註解

常用的資源註解:

nameexp
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新增的註解