注解可以為Java代碼中添加信息,然后在某個時刻去運用它。Java內置了三種注解,分別為:
@Override,(表示當前方法覆蓋超類的方法)
@Deprecated,(如果使用了含有此注解的元素,編譯器將會警告)
@SuppressWarnings.(關閉編譯器的警告信息)
一,定義注解
Java提供了四種注解來負責新注解的創建,分別是
@Tartget:
表示該注解用在什么地方,參數包括:
CONSTRUCTOR:構造器的聲明
FIELD:域聲明
LOCAL_VARIABLE:局部變量聲明
METHOD:方法聲明
PACKIAGE:包聲明
TYPE:類,接口或enum聲明
@Retention:
表示在哪個級別保存注解信息,參數包括:
SOURCE::注解會被編譯器丟棄
CLASS:注解在class文件中可以用
RUNTIME:運行期保留注解,可以通過反射機制讀取注解的信息
@Documented?
將此注解包含在Javadoc中
@Inherited
允許子類繼承父類的注解
例子:要定義在方法上一個運行時注解
@Tartget(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test{
}
使用時如下:
class H{
@Test
void someMethod(){
}
}
在運行時,可以通過反射獲取@Test的信息
二,注解處理
標記在代碼的注解可以在運行時通過反射獲取其信息
for(mothod m : H.getDeclaredMethods()){
Test test=m.getAnnotation(Test.class)
if(test != null){
//找到注解信息,進行處理
? ? }
}
三,注解在Android中的應用
在Android開發中,經常會根據控件id獲取控件實例,例如獲取一個按鈕實例,并設置點擊事件
Button btn=(Button)findviewbyid(R.id.xxxx);
btn.setOnClickListener(xxx);
大部分情況下,編寫一個Activity頁面時,都會設置大量的按鈕,那么都要寫大量同樣樣式的代碼,為什么不交給程序去完成這種繁復的勞動呢?
我們可以省掉寫這些代碼的時間,可以利用注解來解決。
首先,定義注解,按鈕有兩個基本作用,一是獲取實例便于稍后做其它操作,二是設置點擊事件,由此可以這樣定義:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ButtonHandle {
public ?int ?id();
public ?boolean ?click() ?default ? false;
}
@Target標識該注解用于變量上,@Retention說明該注解用于運行時,注解名稱為ButtonHandle,有兩個屬性,int類型屬性id對于布局中的R.id.xxx
boolean類型屬性click標識是否設置點擊事件。使用如下:
@ButtonHandle(id = R.id.xxx, click =true)
private? Button ?btn;
這個注解的作用是把id為R.id.xxx的控件實例賦值給btn,并設置btn的點擊事件。
下面來實現這個注解的功能,需要寫一個注解處理器,先寫關鍵代碼:
public ?static ?void ? ButtonHandle(Object activity, View sourceView) {
//?通過反射獲取到全部屬性
Field[] fields = activity.getClass().getDeclaredFields();
if(fields?!=null&&?fields.length?>0)?{
for(Field?field?:?fields)?{
// 返回ButtonHandle類型的注解內容
BindView bindView = field.getAnnotation(ButtonHandle.class);
if(bindView?!=null)?{
//獲取標記在注解上的id
intviewId?=?bindView.id();
//是否設置點擊事件
boolean ?clickLis?=?bindView.click();
try{
field.setAccessible(true);
if(clickLis)?{
//注解幫我們完成的事,設置點擊事件
sourceView.findViewById(viewId).setOnClickListener(
(OnClickListener) activity);
}
//注解幫我們完成的事,賦值
field.set(activity, sourceView.findViewById(viewId));
}catch(Exception e) {
e.printStackTrace();
}
}
}
}
}
上面的代碼完成獲取實例并給按鈕設置點擊事件, activity表示當前activity, sourceView表示activity的根view,即DecorView,在onCreate()里
調用以上方法即可完成注解的處理,給Btton設置點擊事件。
四,編譯時注解
運行時注解的處理用到了反射,假如在意反射損耗的性能,可以考慮使用編譯時注解,可以利用編譯時動態生成想要源代碼,實現相關功能。
在Android studio 中新建一個java Library的Module,在里面實現一個注解處理器,該處理器繼承自AbstractProcessor,重寫getSupportedAnnotationTypes()和process()方法:
public classMyProcessorextendsAbstractProcessor {
privateFilerfiler;
@Override
public synchronized voidinit(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
// Filer
filer= processingEnv.getFiler();
}
@Override
public booleanprocess(Set annotations,RoundEnvironment roundEnv) {
for(TypeElement element : annotations) {
if(element.getQualifiedName().toString().equals(MyAnnotation.class.getCanonicalName())) {
MethodSpec main = MethodSpec.methodBuilder("main")
//? ? ? ? ? ? ? ? ? ? ? ? .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class,"args")
.addStatement("$T.out.println($S)",System.class,"Hello, JavaPoet!")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
//? ? ? ? ? ? ? ? ? ? ? ? .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld",helloWorld)
.build();
try{
javaFile.writeTo(newFile(filer);
}catch(IOException e) {
e.printStackTrace();
}
}
}
return true;
}
@Override
publicSetgetSupportedAnnotationTypes() {
Set annotataions =newLinkedHashSet();
annotataions.add(MyAnnotation.class.getCanonicalName());
returnannotataions;
}
@Override
publicSourceVersiongetSupportedSourceVersion() {
returnSourceVersion.latestSupported();
}
新建一個注解的module,定義注解:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public@interfaceMyAnnotation{
Stringvalue();
}
寫完后好需要配置,在main文件夾下創建一個resources.META-INF.services文件夾,創建文件
javax.annotation.processing.Processor,文件內容是Process類的包名+類名。
然后在要使用注解的module依賴包含注解處理器的module和包含注解定義的module,并使用:
然后Build該Module。完成后,打開build文件夾,生成的代碼在該文件夾下,可以直接利用其功能。