Preface:

The previous study summarizes the use of Java annotations, blog address see Java learning Annotation Annotation implementation principle, essentially understand what annotations, and how to use annotations? Don’t assume that reflection affects performance when you use Annotations. Today we’ll take a look at Android Support Annotations to optimize our code, increase readability, and nip more bugs in the bud.

Support Annotations

The Android Support Library has introduced a new annotation library starting with version 19.1, which contains many useful meta-annotations that you can use to decorate your code and help you find bugs. The Support Library itself uses these annotations, so as a Support Library user, Android Studio has validated your code against these annotations and flagged potential problems.

Support Annotations

Annotations are not included by default; It is wrapped as a separate library. If the AppCompat library is used, Support Annotations are automatically introduced, because AppCompat uses Support Annotations. If not, you need to add the following configuration to build.gradle:

Dependencies {the compile 'com. Android. Support: support - annotations: 23.4.0'}Copy the code

Annotations: Support Annotations

1) Nullness

The @nullable annotation can be used to indicate that a particular parameter or return value can be null.

The @nonNULL annotation can be used to indicate that a parameter cannot be NULL.

    

2.) Resource Type annotation

Resources are passed as integer values in Android. This means that code that expects a drawable as an argument can easily be passed a string resource because their resource ids are integers, making it difficult for the compiler to distinguish them. Resource Type annotations can provide Type checking in this case, for example:

If the type is specified incorrectly, the compilation will not pass.

Common Resource Type annotations are used to specify an integer parameter, member variable, or method to check the corresponding Resource Type.

  • AnimatorRes :animatorThe resource type
  • AnimRes:animThe resource type
  • AnyRes: indicates any resource type
  • ArrayRes:arrayThe resource type
  • AttrRes:attrThe resource type
  • BoolRes:booleanThe resource type
  • ColorRes:colorThe resource type
  • DimenRes:dimenResource type.
  • DrawableRes:drawableResource type.
  • FractionRes:fractionThe resource type
  • IdRes:Id Resource type
  • IntegerRes:Integer Resource type
  • InterpolatorRes:Interpolator Resource type
  • LayoutRes:Layout Resource type
  • MenuRes:Menu Resource type
  • PluralsRes:Plurals resource type
  • RawRes:Raw Resource type
  • StringRes:String Resource type
  • StyleableRes:Styleable Resource type
  • StyleRes:Style resource type
  • TransitionRes:Transition resource type
  • XmlRes:XML resource type

This covers almost all resource types, but sometimes you need to use RGB color integers to set the color value. In this case, you can use @colorInt to indicate that you are expecting an integer value that represents the color

3. His Threading was very difficult

For example, if you were working on a project that needed to execute a time-consuming operation in a worker thread, you could use the Threading annotation. If you didn’t execute the operation in the worker thread, you would fail to compile

Several Threading annotations

  • @ UiThread UI thread
  • @ MainThread main thread
  • @ WorkerThread child thread
  • @binderThread Binds threads

Typedefs: IntDef/StringDef

In addition to being a reference to a resource, integers can also be used as an “enumeration” type.

@intdef and typedef work very much the same way. You can create another annotation and specify a list of values you want for integer constants with @intdef. Then you can use the defined annotation to modify your API.

For example, you need to customize the network type

    private final static int GET=0;
    private final static int POST=1;
    private final static int DELETE=2;
    private final static int PUT=3;
    @IntDef({GET, POST, DELETE,PUT})
    @Retention(RetentionPolicy.SOURCE)
    public @interface ReqType{}Copy the code

Only the specified type can be passed in the call. If the type is not correct, the compilation fails

Constraints: @size, @intrange, @floatrange

In real development, we may sometimes need to set a value range, which can be constrained by value range annotations.

Let’s say we set a percentage that ranges from 0 to 100,

 

For data, collections, and strings, you can use the @size annotation argument to limit the Size of collections (or the length of strings if the argument is a string).

Just a couple of examples

  • Set cannot be empty: @size (min=1)
  • A string can contain a maximum of 23 characters: @size (Max =23)
  • Arrays can only have 2 elements: @size (2)
  • The Size of the array must be a multiple of 2 (for example, the x/ Y array in the graphics API for fetching positions: @size (multiple=2)
Permissions: @requiresperMission

Sometimes our method calls require the caller to have specified permissions, so we can use the @requiresperMission annotation,

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap) throws IOException;Copy the code

In addition to the above single use mode, the official also provides the following use scenarios

(1) If you need at least one of the permissions set, you can use the anyOf attribute

@RequiresPermission(anyOf = {
    Manifest.permission.ACCESS_COARSE_LOCATION,
    Manifest.permission.ACCESS_FINE_LOCATION})
public abstract Location getLastKnownLocation(String provider);Copy the code

(2) If you need multiple permissions at the same time, you can use the allOf attribute

@RequiresPermission(allOf = {
    Manifest.permission.READ_HISTORY_BOOKMARKS, 
    Manifest.permission.WRITE_HISTORY_BOOKMARKS})
public static final void updateVisitedHistory(ContentResolver cr, String url, boolean real) ;Copy the code

(3) For intents permissions, you can specify permission requirements directly on the defined Intent constant string field (they are usually marked by the @sdkconstant annotation).

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

For content providers, you may need separate Read and Write permissions, so use @read or @write for each request

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

7.) Ancient Methods (@callsuper

If your API allows users to override your method, but you need your own method (parent method) to be called when you override it, you can use the @callsuper annotation

For example, the onCreate function of the Activity

@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) Copy the code

With this, the tool will prompt you when the overridden method does not call the parent method

8.) Return Values note: @checkResult

If your method returns a value and you expect the caller to do something with that value, you can annotate the method with the @checkResult annotation.

This function is rarely used in specific use, except in special cases, such as processing a data in the project, which is time-consuming. We hope that the caller who calls this function will prompt that it is not used when the result is not needed and delete the call as appropriate.

9.) Other notes

Keep: Indicates that a method should be retained when confused.

VisibleForTesting: A class, method, or variable can be annotated to indicate looser visibility so that it can have wider visibility so that code can be tested.

10.) IntelliJ notes

IntelliJ, on which Android Studio is based, has its own set of annotations; The IntDef analysis actually reuses the MagicConstant analysis code, whereas the IntelliJ NULL analysis actually uses a set of configured NULL annotations. Infer Nullity if you perform Analyze > Infer Nullity… It will try to find all the NULL constraints and add them. This check sometimes inserts IntelliJ annotations. You can search for and replace annotations with Android annotations library, or you can use IntelliJ annotations directly. You can add the following Dependencies in build.gradle or in the Dependencies panel of the Project Structure dialog:

Dependencies {the compile 'com. Intellij: annotations: 12.0'}Copy the code

Summary:

After consulting materials, I systematically learned Support Annotations and put them into practice. Through this Support Annotations, code readability can be improved and errors can be checked during class loading without any impact on performance. Because the lifetime of Annotations in Support Annotations is all retentionPolicy.class. The next step is to use it extensively in projects.


Preface:

Having recently studied EventBus, BufferKinfe, GreenDao, Retrofit and other excellent open source frameworks, none of them use annotations in their new versions, and we have experienced many benefits when using annotations. Based on this idea, I feel it is necessary to have a deeper understanding of annotations. I finished the company’s project at noon today, and worked overtime to learn and summarize Java annotations in the evening.

What are annotations?

That’s a question that many first-time developers have, right? Annontation is a new feature introduced in Java5, called annotations in Chinese. It provides a secure annotation-like mechanism for associating any information or metadata with program elements (classes, methods, member variables, and so on). Add more intuitive and explicit descriptions of application elements (classes, methods, member variables) that are independent of the application’s business logic and are used by the specified tool or framework. Annontation is used like a modifier in declarations of packages, types, constructors, methods, member variables, parameters, and local variables.

The use of annotations:

1. Generate documentation. This is the most common and the earliest annotation provided in Java. Common ones are @param@return, etc

2. Track code dependencies to replace configuration files. For example, Dagger 2 dependency injection, which will be very useful for future Java development with a lot of annotation configuration;

3. Format checking at compile time. If @Override comes before a method, it will be checked at compile time if your method does not override a superclass method.

Yuan comments:

Java.lang. Annotation provides four meta-annotations that specialize in other annotations:

   @Documented- Whether annotations will be included in JavaDoc

   @Retention-- When to use this annotation

   @Target-- Where are annotations used

   @Inherited- Whether subclasses are allowed to inherit this annotation

1.)@Retention– Define the life cycle of the annotation
  • Retentionpolicy. SOURCE: discarded at compile time. These annotations are no longer meaningful after compilation, so they are not written to bytecode. @Override and @SuppressWarnings fall into this category.
  • Retentionpolicy. CLASS: Discarded when the CLASS is loaded. Useful in the processing of bytecode files. This is the default for annotations
  • Retentionpolicy.runtime: The annotation is never discarded and is retained at RUNTIME, so it can be read using reflection. Our custom annotations usually use this approach.

Example: In bufferKnife 8.0, the @bindView life cycle is CLASS

@Retention(CLASS) @Target(FIELD)
public @interface BindView {
  /** View ID to which the field will be bound. */
  @IdRes int value();
}Copy the code

2.)Target– Indicates where the annotation is used. The default value is any element, indicating where the annotation is used. The ElementType parameters available include
  • Elementtype. CONSTRUCTOR: Used to describe the CONSTRUCTOR
  • Elementtype. FIELD: Member variables, objects, attributes (including enum instances)
  • Elementtype. LOCAL_VARIABLE: Used to describe local variables
  • Elementtype. METHOD: Used to describe methods
  • Elementtype. PACKAGE: Used to describe packages
  • Elementtype. PARAMETER: Used to describe parameters
  • Elementtype. TYPE: Used to describe classes, interfaces (including annotation types), or enum declarations

For example, Retrofit 2 uses the @field scope as a parameter

@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {
  String value();

  /** Specifies whether the {@linkplain #value() name} and value are already URL encoded. */
  boolean encoded() default false;
}Copy the code

 3.)@DocumentedAnnotations — A simple Annotations, which indicate whether annotation information is added to a Java document.
 4.)@Inherited– Define the relationship between the annotation and subclasses

An @Inherited meta-annotation is a markup annotation. @Inherited indicates that an annotated type is Inherited. If an annotation type with the @Inherited annotation is applied to a class, the annotation will be applied to a subclass of that class.

Common standard annotations:

1.) the Override

Java.lang.Override is a tag type annotation that is used as an annotation method. It indicates that the method being tagged overrides the parent class’s method, acting as an assertion. If we use this annotation in a method that does not override a superclass method, the Java compiler will alert us with a compilation error.

2.) Deprecated

Deprecated is also a tag type annotation. When a type or a type member uses the @deprecated modifier, the compiler discourages the use of the annotated program element. So there is some “continuity” in the use of this modifier: if we use an obsolete type or member in our code by inheritance or overwriting, the compiler still alerts us, even though the inherited or overwritten type or member is not declared as @deprecated.

3.) SuppressWarnings

SuppressWarning is not a tag type annotation. It has a member of type String[] whose value is the forbidden warning name. For the Javac compiler, warning names that are valid with the -xLint option are also valid with @Suppresswarings, and the compiler ignores unrecognized warning names.

@SuppressWarnings("unchecked") Copy the code

Custom annotations:

Here simulates a network request interface, and how to obtain the interface’s annotation function, parameters to perform the request.

1.) Define notes:

@reqType Request type

@documented @target (METHOD) @Retention(RUNTIME) public @interface ReqType {@documented @Target(METHOD) @Retention(RUNTIME) public @interface ReqType { GET,POST,DELETE,PUT}; /** * Return */ ReqTypeEnum reqType() default ReqTypeEnum.POST; }Copy the code

@requrl Request address

@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface ReqUrl {
    String reqUrl() default "";
}Copy the code

@reqparam Request parameters

@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface ReqParam {
    String value() default "";
}Copy the code

As you can see from the above, the supported data types for annotation parameters are as follows:

1. All basic data types (int, float, Boolean, byte, double, char, long, short) 2. The type String (3) type Class 4. 5. Enum type Annotation types 6. All of the above types of arrays

And it’s not hard to see that @Interface is used to declare an annotation, where each method is actually declaring a configuration parameter. The name of the method is the name of the parameter, and the return value type is the type of the parameter (the return value type can only be basic type, Class, String, or enum). You can declare default values for parameters with default.

2.) How to use custom annotations
Public interface IReqApi {@reqType (ReqType = reqtype.reqTypeenum.POST)// Declare that the POST request @reqURL (ReqUrl = "Www.xxx.com/openApi/login") / / request Url String login (@ ReqParam (" userId ") String userId, @ ReqParam (" PWD ") String PWD). // Parameter username password}Copy the code

3.) How do I get annotation parameters

So again, an Annotation is passive metadata, it never has active behavior, and every time an Annotation works there’s an execution mechanism/caller that gets that metadata through reflection and then acts on it.

Get function annotation information through reflection mechanism

      Method[] declaredMethods = IReqApi.class.getDeclaredMethods();
        for (Method method : declaredMethods) {
            Annotation[]  methodAnnotations = method.getAnnotations();
            Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();
        }Copy the code

You can also get the specified annotation

ReqType reqType =method.getAnnotation(ReqType.class);Copy the code

4.) Implement annotation interface call concretely

The Java dynamic proxy mechanism is used to separate the definition of the interface from the implementation, which will be summarized later.

private void testApi() { IReqApi api = create(IReqApi.class); api.login("whoislcj", "123456"); } public T create(final Class service) { return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable {// Annotation[] methodAnnotations = method.getAnnotations(); // Get the array of function annotations ReqType ReqType = method.getannotation (reqtype.class); Log.e(TAG, "IReqApi---reqType->" + (reqType.reqType() == ReqType.ReqTypeEnum.POST ? "POST" : "OTHER")); ReqUrl reqUrl = method.getAnnotation(ReqUrl.class); Log.e(TAG, "IReqApi---reqUrl->" + reqUrl.reqUrl()); Type[] parameterTypes = method.getGenericParameterTypes(); Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations(); For (int I = 0; i < parameterAnnotationsArray.length; i++) { Annotation[] annotations = parameterAnnotationsArray[i]; if (annotations ! = null) { ReqParam reqParam = (ReqParam) annotations[0]; Log.e(TAG, "reqParam---reqParam->" + reqParam.value() + "==" + args[i]); Return String result = ""; Return result; }}); }Copy the code

Print result:

This mimics the basic networking principles of Retrofit 2 by defining parameters through annotations and executing functions through dynamic proxies.