Java Annotation
Recently, I am learning Spring framework. Spring framework is based on annotation-driven development. I used to use annotation not often, just like override. For better understanding spring framework, I think I need to understand annotation development first.
Annotation is a note for the function or varibale. If you search on Google about annotation, there are many tutorials about the definition of annotation in Java. Easy words, hard to understand how the annotation works. Here I will explain annotation development with an example in Android development of NetworkListener ( switch to Annotation-Network-Listener branch).
In our application, we want to catch the changes in network, network switching between wifi and mobile, or between connected and disconnected. The first thing to do is to define an enum type NetType
Define an Annotation
1 | public enum NetType { |
There are 4 states in network, WIFI, MOBILE and NONE are very straightforward. Why we have this AUTO? NetType is not only used for network type for the network connection, also used for which network type changes you want to listen. So AUTO means to listen all types of network change.
Next we need to define the annotation.1
2
3
4
5 (ElementType.METHOD)
(RetentionPolicy.RUNTIME)
public Network {
NetType netType() default NetType.AUTO;
}
There are two annotations for our annotation. This is called meta-annotation. Meta-annotation is like a key word in Java, such as int, string, enum. It is how you define your annotation.
- @Target(ElementType.METHOD) : our annotation should be defined on methods.
- @Retention(RetentionPolicy.RUNTIME) : annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
I am not going to explain all the meta-annotation here. From the definition, it uses @interface to define an annotation, so we can see that annotation is kind of interface, which extends java.lang.annotation.Annotation
. netType()
is called attribute in annotation, while you can give it a default value. Java will generate an instance that implements this interface by dynamic proxy and then assigns the value to the attribute.
Use the Annotation
After we defined the annotation Network, now we need to use it. In our application, the MainActivity want to listen to the network changes. So we use annotation like this1
2
3
4
5
6
7
8
9
10
11
12
13
14 (netType = NetType.AUTO)
public void network(NetType netType) {
switch (netType) {
case WIFI:
Log.e(Constants.LOG_TAG, "WIFI");
break;
case MOBILE:
Log.e(Constants.LOG_TAG, "MOBILE");
break;
case NONE:
Log.e(Constants.LOG_TAG, "Network Disconnected");
break;
}
}
There are two netType here.
public void network(NetType netType)
netType is what kind of network connection type is for the device.@Network(netType = NetType.AUTO)
netType is what kind of network changes you want to listen. If you change it to NetType.WIFI which means this function only listeners to WIFI changes.
Track the Annotation
For network changes, we will define a broadcast receiver to catch the network changed broadcasting. Also Receiver needs to know which function in which activity want to know about this. First we need to pass the MainActivity to the receiver. We have a registerObserver function to handle this.
1 | public void registerObserver(Object register) { |
networkList
is a map which key is the actiivty and value is a list of methods that has the annotation. findAnnotationMethod
is the function to get all satisfied function that has the network annotation.
1 | private List<MethodManager> findAnnotationMethod( Object register ) { |
This is the most improtant part in our application of annotation. We use Java refection to do this.
- Get all function in that activity/class and filter the function has the network annotation.
- Filter the voide return type function
- Filter the one parameter function
- Store the function in the list. ParameterType(netType in the parameter, current network connection type), network.netType(netType in our annotation, what kind of network changes we want to catch), method
Now after we registered all the satisfied methods in our application, how to trigger it? In receiver, when the network changes, the receiver should receive the broadcasting message. Then trigger a post function with the current network connection type1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29private void post(NetType netType) {
Set<Object> set = networkList.keySet();
for ( final Object getter : set ) {
List<MethodManager> methodeList = networkList.get(getter);
if( methodeList != null ) {
for( final MethodManager method : methodeList ) {
if( method.getType().isAssignableFrom(netType.getClass() ) ) {
switch ( method.getNetType() ) {
case AUTO:
invoke( method, getter, netType );
break;
case WIFI:
if( netType == NetType.WIFI || netType == NetType.NONE ) {
invoke( method, getter, netType );
}
break;
case MOBILE:
if( netType == NetType.MOBILE || netType == NetType.NONE ) {
invoke( method, getter, netType );
}
break;
default:
break;
}
}
}
}
}
}
We give the network attribute netType as NetType.AUTO. so method.getNetType()
will give what kind of network changes we want to catch. In our case, it is AUTO, so it will invoke the function with the annotation as long as the network changes. If it WIFI, it will only invoke the function when WIFI changes. This is how we use annotation. If you are interested in the source code, you can check my NetworkListener repo with Annnotation-Network-Listener branch.