Use of annotations in Android

preface

Android Support Library has introduced a new annotation library since version 19.1, which contains a lot of meta annotations. Using them to modify our code can enable us to improve the development efficiency of the program and find problems earlier. And standardize the code to make the code more readable. In this article, let's briefly understand these annotations and their use. If there are errors and omissions, please leave a message and add ~

Note: now we directly rely on support for new projects Appcompat package, which already relies on the annotations package. If you write the following comments in your project and report an error, you can add a comment package:

 

dependencies {
    compile 'com.android.support:support-annotations:22.2.0'
}

@IntDef & @StringDef

Instead of enumerating annotations in Java, take @ IntDef as an example. The definition and use are as follows:

 

@IntDef({RED, BLUE, YELLOW})
@Retention(RetentionPolicy.SOURCE)
public @interface LightColors{};

public static final int RED = 1;
public static final int BLUE = 2;
public static final int YELLOW = 3;

public void setColor(@LightColors int color){
}
  • @interface: declare a new enumeration annotation type.
  • @Retention(RetentionPolicy.SOURCE): tells the compiler not to store the enumerated annotation data in class file.

If constants are allowed to be combined with flags (such as: |, & and ^ and so on), we can use the flag attribute, such as:

 

@IntDef(flag = true, value = {RED, BLUE, YELLOW})

use:

 

setColor(RED | BLUE);

@Nullable & @NonNull

  • @Nullable: the element of the annotation can be null.
  • @NonNull: the element of the annotation cannot be null.

The above annotation can modify the following elements:
1. Method parameters. For example:

 

@Nullable
private String data;

2. Return value of the method. For example:

 

@Nullable
public String getData(){
    return data;
}

3. Member properties. For example:

 

public void setData(@Nullable String data){
}

When an empty parameter is passed to the method of the method parameter modified by @ NonNull, the following warning prompt will be given (no error will be reported during compilation):

 

passing "null" argument to parameter annotated as @NotNull

@FloatRange & @IntRange

@FloatRange and @ IntRange are annotations used to limit the scope. Where @ FloatRange is limited to float type and @ IntRange is limited to int type. Like annotations, they can modify method parameters, method return values, and member properties.

Taking @ IntRange as an example, the modification method parameters are defined as follows:

 

public void setAge(@IntRange(from = 1, to = 180) int age){
}

If the parameters passed by calling this method are not in the range of 1 - 180, such as setAge(0), the compilation will directly report the following error:

 

value must be ≥ 1 and ≤ 180 (was 0)

@Size

@The Size annotation is used to limit the length. Like the annotation above, it can modify method parameters, method return values and member properties.

  • Length of qualified string:

 

public void setData(@Size(4) String data){
}

When the length of the incoming string is not equal to 4, the compiler will directly report an error:

 

Length must be exactly 4 
  • Limit the length of the array:

 

public void setData(@Size(4) int[] data){
}
  • Special restrictions, such as a multiple of 2:

 

public void setData(@Size(multiple = 2) int[] data){
}

Limit the minimum length:

 

@Size(min = 2)

Limit maximum length:

 

@Size(max = 2)

Equivalent to @ Size(2):

 

@Size(value = 2)

@RequiresPermission

This annotation is used to indicate that the content executed by the method requires permission. If a single permission is required:

 

@RequiresPermission(Manifest.permission.CALL_PHONE)
private void callPhone(String phone){
}

A set of permissions is required:

 

@RequiresPermission(allOf = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE})
public static final void copyFile(String dest, String source) {
...
}

For intent permission, we can define it on the string of intent operation name:

 

@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE =
        "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";

For the permissions of content providers that need separate read-write permissions, we can use @ requirespermission Read or @ requirespermission The write annotation contains each permission requirement:

 

@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");

If the permission depends on the specific value provided to the method parameter, you can use @ RequiresPermission on the parameter itself without listing the specific permission, such as the startActivity(intent) method:

 

public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle) {...}

When we use this approach (indirect permissions), the build tool will perform data flow analysis to check whether the parameters passed to the method have any @ RequiresPermission annotations. For example:

 

Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:1234567890"));
startActivity(intent);

The startActivity(intent) here directly reports an error:

 

call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`

Because intent ACTION_ Permission comments are marked in call:

 

@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(Manifest.permission.CALL_PHONE)
public static final String ACTION_CALL = "android.intent.action.CALL";

@CheckResult

@The CheckResult annotation is used on the method to check whether the return value is processed. If the return value is not processed, an error will be reported.

 

@CheckResult
public String getData(String data) {
    return data.trim();
}

Thread annotation

Thread annotations can check whether a method is called from a particular type of thread. The following thread annotations are supported:

  • @MainThread: indicates that the marked method should only be called in the main thread. If a class is marked, all methods in the class should be called in the main thread. Example: (usually, the main thread of the application is also Ui thread. However, in special cases, the main thread of the application may not be its Ui thread)

 

@MainThread
public void deliverResult(D data) { ... }
  • @Uitthread: indicates that the marked method or constructor should only be called on the Ui thread. If a class is marked, all methods in the class should be called in the Ui thread. Example:

 

@UiThread
public abstract void setText(@NonNull String text) {...}
  • @WorkerThread: indicates that the marked method should only be called on the worker thread. If you are marking a class, all methods in that class should be called on a worker thread. Example:

 

@WorkerThread
protected abstract FilterResults performFiltering(CharSequence constraint);
  • @BinderThread: indicates that the marked method should only be called on the binding thread. If a class is marked, all methods in the class should be called in the binding thread. Example:

 

@BinderThread
public BeamShareData createBeamShareData() { ... }
  • @AnyThread: indicates that marked methods can be called from any thread. If you are marking a class, all methods in that class can be called from any thread. Example:

 

@AnyThread
public void deliverResult(D data) { ... }

The builder will treat the @ MainThread and @ uitthread annotations as interchangeable, so we can call the @ uitthread method from the @ MainThread method and vice versa. However, if the system application has multiple attempts on different threads, the Ui thread can be different from the main thread. Therefore, we should use @ uitthread to label the method of applying view hierarchy association, and use @ MainThread to label only the method of applying life cycle Association.

Resource annotation

In Andro id, almost all resources have their IDs. We can use them directly, such as:

 

textView.setText(getResources().getText(R.string.app_name));

But in this way, if the specified resource annotation is not written, there will be risks. For example, if a 0 is passed casually, the corresponding resource will not be found.
In order to avoid errors caused by our carelessness, we can use resource annotations, such as:

 

public int getText(@StringRes int id){
}

In this way, when we call this method, if the parameter passed is not a resource id of String type, the compiler will give an error prompt.

In addition to the @ StringRes resource annotation, there are:

  • @IntegerRes: resource of type R.integer.
  • @AnimatorRes: R.animator type resource.
  • @AnimRes: R.anim type resource.
  • @ArrayRes: resource of type R.array.
  • @AttrRes: resource of type R.attr.
  • @BoolRes: R.bool type resource.
  • @ColorRes: resource of type R.color.
  • @DimenRes: r.dimension type resource.
  • @DrawableRes: resource of type R.drawable.
  • @FractionRes: R.fraction type resource. (percentage)
  • @IdRes: R.id Type resource.
  • @InterpolatorRes: resource of type R.interpolator. (interpolator)
  • @LayoutRes: R.layout type resource.
  • @MenuRes: R.menu Type resource.
  • @PluralsRes: R.plurals type resource. (plural)
  • @RawRes: R.raw type resource.
  • @StyleableRes: R.styleable type resource.
  • @StyleRes: R.style type resource.
  • @TransitionRes: R.transition type resource.
  • @XmlRes: R.xml type resource.
  • @AnyRes: unknown resource. (indicates that you don't know what type of resource it is. For example, it may be R.drawable or R.string.)

@ColorInt

@ColorInt annotation is used to limit the color value. (ARGB: 0xAARRGGBB)

 

public void setColor(@ColorInt int color) {

}

If the resource id is used directly, an error will be reported as follows:

 

setColor(R.color.colorAccent)// report errors

The correct use is:

 

setColor(0xFFFF00FF);

If you want to use the resource id, you can use contextcompat Getcolor() method to:

 

setColor(ContextCompat.getColor(context, R.color.colorAccent));

@CallSuper

This annotation is used to modify a method, indicating that the super method must be called when overriding the method. For example, onCreate() method:

 

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

When overriding the onCreate() method, you must call the super method:

 

super.onCreate(savedInstanceState);

Otherwise, an error is reported.

@VisibleForTesting & @Keep

Use the @ VisibleForTesting and @ Keep annotations to indicate the accessibility of a method, class, or field.

  • @VisibleForTesting: this annotation only serves as an annotation to tell other developers why the marked code is so visible (for the convenience of testing). Therefore, it is often used to modify public or protected. It will not report an error when it is used to modify private, but it is meaningless.

  • @Keep: the specified code of the tag will not be confused when confused.

reference resources:


 

Tags: Android

Posted by needphphelp on Sat, 07 May 2022 05:41:38 +0300