Android如何使用註解進行程式碼檢查
阿新 • • 發佈:2020-09-24
> 原文首發於微信公眾號:躬行之(jzman-blog),歡迎關注交流!
Android Studio 內建了程式碼檢查工具 Lint,可在選單欄選擇 Analyze > Inspect Code 執行相應的程式碼檢查,程式碼檢查能夠根據推斷一些不合法的潛在問題,有助於在開發階段發現開發者因為主管原因導致的一下程式碼問題,Android 官方提供了註解庫 support-annotations 來幫助開發者及早發現問題,下面是常用的一些註解,主要內容如下:
1. Nullness註解
2. 資源註解
3. 執行緒註解
4. 值約束註解
5. 許可權註解
6. 返回值註解
7. CallSuper註解
8. Typedef註解
9. 可訪問性註解
#### Nullness註解
使用 Nullness 註解可以檢查給定變數、引數和返回值是否允許 null 值,具體如下:
- **@Nullable** :表示可以為 null 的變數、引數或返回值,
- **@NonNull** :表示不可為 null 的變數、引數或返回值。
```java
@NonNull
@Override
public View onCreateView(String name, @NonNull Context context,@NonNull AttributeSet attrs) {
//...
}
```
#### 資源註解
資源註解的使用可使得在原始碼階段讓編輯器檢查書寫的不規範,也可在一定程度上優化程式碼結構,下面是常見的資源註解如下:
- **@StringRes**: 表示檢查是否包含R.string引用
- **@ColorRes**: 表示檢查是否包含R.color引用
- **@ColorInt**: 表示檢查是否包含表示顏色的整型
- **@DrawableRes**: 表示檢查是否包含R.drawable引用
- **@DimenRes**: 表示檢查是否包含R.dimen引用
- **@InterpolatorRes**:表示檢查是否包含插值器引用
#### 執行緒註解
執行緒註解可以檢查某個方法是否從某個特定型別的執行緒中呼叫,支援一下執行緒註解,具體如下:
- **@MainThread**:表示主執行緒
- **@UiThread**:表示 UI 執行緒
- **@WorkerThread**:表示工作執行緒
- **@BinderThread**:表示Binder執行緒
- **@AnyThread**:表示任何一個執行緒
上述註解中 **@MainThread** 和 **@UiThread** 在大多時候表示的都是同一執行緒,如果應用中帶有多個試圖,UI 執行緒可與主執行緒不同,故可使用 **@UIThread** 標註與應用的檢視層次相關聯的方法,使用 **@MainThread** 僅標註與應用生命週期相關聯的方法。執行緒註解最常用的一個用途是 AsyncTask 使用中的方法替換,因為 AsyncTask 會執行後臺操作並將結果釋出到 UI 執行緒。
#### 值約束註解
使用值約束註解可驗證傳遞的引數的值的合法性,可以藉此指定引數的設定範圍,可在一定程度上減少程式碼在主觀程度上出現的錯誤,常見的值約束註解如下:
- **@IntRange**:表示可以驗證整型引數是否在指定範圍內
- **@FloatRange**:表示可以驗證浮點型引數是否在指定範圍內
- **@Size**:表示可以驗證集合、陣列、字串引數是否在指定範圍內,可指定最大值、最小值以及確切值
上面的註解有一些可使用的引數,如 from、to、min 等,使用時具體在某個註解體重檢視定義即可。
#### 許可權註解
許可權註解 **@RequiresPermission** 可以驗證方法呼叫方的許可權,即當使用了許可權註解的方法時會檢查有沒有指定的許可權,如果沒有則會提示要在 AndroidManifest.xml 檔案中申明許可權,如果是危險許可權還有進行許可權動態申請,使用方式參考如下:
```java
/**
* 單個許可權檢查
* @param message
*/
@RequiresPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
public void setMessage(String message) {
}
/**
* 全部許可權檢查
* @param message
*/
@RequiresPermission(allOf = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE})
public void setMesage(String message) {
}
/**
* 某個許可權檢查
* @param message
*/
@RequiresPermission(anyOf = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE})
public void setMesage(String message) {
}
```
#### 返回值註解
返回值註解 **@CheckResult** 會檢查某個方法的返回值是否被使用,如果沒有被使用,則會根據 suggest 配置建議使用相同公民沒有返回值的另一個方法,如果返回值使用了,則和未加該註解的方法一樣,使用方式參考如下:
```java
@CheckResult(suggest="#enforcePermission(String,int,int,String)")
public int checkPermission(@NonNull String permission, int pid, int uid){
return 0;
}
```
如果沒有使用返回值提示如下:
![jzman-blog](https://user-gold-cdn.xitu.io/2019/9/15/16d3590105eaa642?w=771&h=76&f=jpeg&s=10249)
當返回值沒有被使用,則會建議使用相同功能沒有返回值的另一個方法,簡而言之,返回值註解 **@CheckResult** 能夠表示某個方法實際使用的時方法本身的處理還是方法最終的處理結果。
#### CallSuper註解
使用 **@CallSuper** 註解會驗證子類的重寫方法是否呼叫父類的實現,這樣約束的好處是可保證父類的實現不會修改,當然,如果不使用該註解,子類重寫父類的方法可以不呼叫弗父類的預設實現,具體參考如下:
```java
/**
* 父類
* @CallSuper註解的使用
*/
public class Test {
//使用@CallSuper註解,子類重寫該方法時必須呼叫該方法
@CallSuper
protected void onCreate(){
}
}
```
下面是 Test 類的實現類:
```java
/**
* 子類
* @CallSuper註解的使用
*/
public class TestImpl extends Test{
@Override
protected void onCreate() {
super.onCreate();
/**
* 如果不呼叫父類的方法,則會提示
* Some methods, such as View#onDetachedFromWindow, require that you also call the super implementation as part of your method.
*/
}
}
```
#### Typedef註解
使用 **@IntDef** 和 **@StringDef 註解** 可以建立整型和字串的列舉註解來驗證其他程式碼中使用的某些整型和字串,可以保證程式碼中的某些常量整型或常量字串是某些具體定義的常量集,這兩個註解的位置只能是註解。
開發中總會使用到列舉,列舉在一定程度上可使得程式碼結構更清晰,但列舉的使用會增加記憶體的開銷,這裡可以用 Typedef 註解的方式來代替列舉,下面是 Tyoedef 註解的使用,參考如下:
```java
/**
* Typedef 註解的定義
*/
public class ActionType {
public static final int ACTION_TYPE_0 = 0;
public static final int ACTION_TYPE_1 = 1;
public static final int ACTION_TYPE_2 = 2;
@Retention(RetentionPolicy.SOURCE)
@IntDef({ACTION_TYPE_0,ACTION_TYPE_1,ACTION_TYPE_2})
public @interface ActionTypeDef{
}
}
```
下面是上述 Typedef 註解的使用方式,參考如下:
```java
/**
* Typedef註解的使用
* @param value
*/
private void setValue(@ActionType.ActionTypeDef int value) {
switch (value) {
case ActionType.ACTION_TYPE_0:
break;
case ActionType.ACTION_TYPE_1:
break;
case ActionType.ACTION_TYPE_2:
break;
// case 100://不能使用未定義的整型
// break;
}
}
```
可見 Typedef 註解約束了使用到的某些整型,當然還可以是字串,這樣也能達到列舉的作用。
#### 可訪問性註解
可訪問性註解是 **@VisibleForTesting** 和 **@Keep** 可以表示方法、欄位、類的可訪問性。具體如下:
- **@VisibleForTesting**:表示註解的某個程式碼塊的可見性高於能夠測試時需要的水平
- **@Keep**:表示被註解的程式碼塊將不會被混淆。
最常用的可能就是資源註解,如 @StringRes、@ColorRes、@ColorInt等,還有Typeof 註解,該註解可以在替換列舉在 Android 開發中帶來的效能影響,如果平時留意這些註解在 Android 原始碼中也經常使用,所以可在開發過程中嘗試去使用這些註解以進行必要的程式碼檢查。
可以關注公眾號:躬行之(jzman-blog),一起交流學習。
![](https://user-gold-cdn.xitu.io/2020/7/10/1733467a93fbeaec?w=600&h=400&f=png&s