Android Studio comes with a built-in code inspection tool called Lint, which can be accessed by selecting Analyze > Inspect Code from the menu bar. Code inspection can identify potential issues and help developers discover code problems caused by supervisors during the development phase. The Android official provides an annotation library called support-annotations to help developers detect problems early. Here are some commonly used annotations:
- Nullness annotations
- Resource annotations
- Thread annotations
- Value constraint annotations
- Permission annotations
- Return value annotations
- CallSuper annotations
- Typedef annotations
- Accessibility annotations
Nullness annotations#
Nullness annotations can check whether a given variable, parameter, or return value allows null values. Specifically:
- @Nullable: Indicates a variable, parameter, or return value that can be null.
- @NonNull: Indicates a variable, parameter, or return value that cannot be null.
@NonNull
@Override
public View onCreateView(String name, @NonNull Context context,@NonNull AttributeSet attrs) {
//...
}
Resource annotations#
Resource annotations can be used to check for non-standard code in the source code and optimize code structure to some extent. Here are some common resource annotations:
- @StringRes: Checks for references to R.string.
- @ColorRes: Checks for references to R.color.
- @ColorInt: Checks for integer representations of colors.
- @DrawableRes: Checks for references to R.drawable.
- @DimenRes: Checks for references to R.dimen.
- @InterpolatorRes: Checks for references to interpolators.
Thread annotations#
Thread annotations can check whether a method is called from a specific type of thread. The following thread annotations are supported:
- @MainThread: Represents the main thread.
- @UiThread: Represents the UI thread.
- @WorkerThread: Represents a worker thread.
- @BinderThread: Represents a Binder thread.
- @AnyThread: Represents any thread.
In most cases, @MainThread and @UiThread represent the same thread. If an application has multiple views, the UI thread may be different from the main thread. Therefore, @UIThread can be used to annotate methods associated with the application's view hierarchy, and @MainThread can be used to annotate methods associated with the application's lifecycle. The most common use of thread annotations is to replace methods in AsyncTask, as AsyncTask performs background operations and publishes results to the UI thread.
Value constraint annotations#
Value constraint annotations can validate the legality of the values passed as parameters and specify the range of settings for the parameters. This can reduce subjective errors in the code to some extent. Here are some common value constraint annotations:
- @IntRange: Validates whether an integer parameter is within a specified range.
- @FloatRange: Validates whether a floating-point parameter is within a specified range.
- @Size: Validates whether a collection, array, or string parameter is within a specified range, and can specify maximum and minimum values as well as exact values.
The above annotations have some parameters that can be used, such as from, to, min, etc. The specific definitions can be found in the annotation body.
Permission annotations#
The permission annotation @RequiresPermission can validate the permissions of the method caller. When a method with a permission annotation is used, it checks whether the specified permission is declared in the AndroidManifest.xml file. If it is a dangerous permission, it also requires dynamic permission request. The usage is as follows:
/**
* Single permission check
* @param message
*/
@RequiresPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
public void setMessage(String message) {
}
/**
* All permissions check
* @param message
*/
@RequiresPermission(allOf = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE})
public void setMesage(String message) {
}
/**
* Any permission check
* @param message
*/
@RequiresPermission(anyOf = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE})
public void setMesage(String message) {
}
Return value annotations#
The return value annotation @CheckResult checks whether the return value of a method is used. If it is not used, it suggests using another method without a return value based on the suggest configuration. If the return value is used, it behaves the same as a method without this annotation. The usage is as follows:
@CheckResult(suggest="#enforcePermission(String,int,int,String)")
public int checkPermission(@NonNull String permission, int pid, int uid){
return 0;
}
If the return value is not used, the following prompt is displayed:
If the return value is not used, it suggests using another method without a return value. In short, the return value annotation @CheckResult can indicate whether a method is used for its own processing or for the final result.
CallSuper annotation#
Using the @CallSuper annotation verifies whether a subclass's overridden method calls the parent class's implementation. The benefit of this constraint is that it ensures that the parent class's implementation is not modified. Of course, if this annotation is not used, the subclass can override the parent class's method without calling the parent class's default implementation. Here is an example:
/**
* Parent class
* Use of @CallSuper annotation
*/
public class Test {
// Use @CallSuper annotation, the subclass must call this method when overriding it
@CallSuper
protected void onCreate(){
}
}
Here is an implementation class of the Test class:
/**
* Subclass
* Use of @CallSuper annotation
*/
public class TestImpl extends Test{
@Override
protected void onCreate() {
super.onCreate();
/**
* If the parent class's method is not called, the following prompt will be displayed:
* Some methods, such as View#onDetachedFromWindow, require that you also call the super implementation as part of your method.
*/
}
}
Typedef annotation#
Using the @IntDef and @StringDef annotations, you can create annotations for enumerating integers and strings to validate certain integers and strings used in other code. This ensures that certain constant integers or constant strings in the code are part of a specific set of constants. These two annotations can only be placed in annotations.
Enums are often used in development to some extent to make the code structure clearer. However, using enums can increase memory overhead. Typedef annotations can be used as an alternative to enums. Here is an example of using the Typedef annotation:
/**
* Definition of Typedef annotation
*/
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{
}
}
Here is an example of using the above Typedef annotation:
/**
* Use of Typedef annotation
* @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: // Cannot use undefined integers
// break;
}
}
As can be seen, the Typedef annotation constrains the use of certain integers. It can also be used for strings, achieving the same effect as enums.
Accessibility annotations#
Accessibility annotations, such as @VisibleForTesting and @Keep, can indicate the accessibility of methods, fields, and classes. Specifically:
- @VisibleForTesting: Indicates that the visibility of the annotated code block is higher than the level required for testing.
- @Keep: Indicates that the annotated code block will not be obfuscated.
The most commonly used annotation may be resource annotations, such as @StringRes, @ColorRes, @ColorInt, etc. There is also the Typedef annotation, which can replace the performance impact of enums in Android development. If you pay attention to these annotations in the Android source code, you will find that they are often used. Therefore, it is worth trying to use these annotations for necessary code inspections during development.