1、元注解
概念:用來定義其他注解的注解,自定義注解的時候,需要使用它來定義我們的注解。
在jdk 1.5之后提供了 java.lang.annotation 來支持注解功能
常見的四種元注解有 :
@Target (目標, 注解可以使用的地方,參數是一個ElementType 枚舉)
@Retention (保持性, 描述注解的生命周期 )
@Inherited ( 可繼承的, 參數true or false ,表示是否允許子類繼承該注解,默認false)
@Document (文檔化,表明注解可以被javadoc 此類工具文檔化)
1.1 @Target
@Target ElementType 枚舉類型
ElementType.Type
|
接口、類、注解、枚舉
|
ElementType.FIELD
|
字段、枚舉常量
|
ElementType.METHOD
|
方法 |
ElementType.PARAMETER
|
方法參數
|
ElementType.CONSTRUCOTOR
|
構造函數
|
ElementType.LOCAL_VARIABLE
|
局部變量
|
ElementType.ANNOTATION_TYPE
|
注解
|
Element.PACKAGE
|
包
|
1.2 @Retention
用於描述注解的生命周期, 注解在什么地方使用有效
參數 RetentionPolicy 枚舉對象
RetentionPolicy.SOURCE
|
源文件,當java文件被編譯成class文件時,注解失效
|
RetentionPolicy.CLASS
|
注解存在class 文件,當jvm 加載class文件時,注解生效,默認指定的參數
|
RetentionPolicy.RUNTIME
|
注解保存到class文件,jvm加載class文件后,依然有效
|
周期有效性, RUNTIME > CLASS > SOURCE
1.3 @Document
標記自定義注解可被javadoc 此類文檔化
1.4 @Inherited
@Inherited 表明我們標記的注解是被繼承的,如果一個父類使用@Inherited 修飾的注解,則允許子類繼承該父類的注解
二、自定義注解
步驟:
1、申明注解,確定注解的運行生命周期、目標、參數
2、注解解析:找到被注解類的方法、屬性。添加自定義注解的一些操作
案例1、注解創建對象
2.1申明注解AutoCreateObject
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * author: rexkell * explain: */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoCreateObject { }
2.2解析注解
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; /** * author: rexkell * explain: */ public class AutoCreateProcess { public static void bind(final Object object){ Class parentClass=object.getClass(); Field[] fields= parentClass.getFields(); for (Field field: fields){ AutoCreateObject autoCreateObject= field.getAnnotation(AutoCreateObject.class); if (autoCreateObject!=null){ field.setAccessible(true); try { Class<?> autoCreateClass= field.getType(); Constructor autoCreateConstructor= autoCreateClass.getConstructor(); field.set(object,autoCreateConstructor.newInstance()); } catch (NoSuchMethodException e) { e.printStackTrace(); }catch (IllegalAccessException e){ e.printStackTrace(); }catch (InvocationTargetException e){ e.printStackTrace(); }catch (InstantiationException e){ e.printStackTrace(); } } } } }
@AutoCreateObject
Students students;
//創建對象
AutoCreateProcess.bind(this);
3、模擬bindViewId
3.1、創建一個java Module
implementation 'com.squareup:javapoet:1.9.0'
implementation 'com.google.auto.service:auto-service:1.0-rc2'
3.2 申明注解
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * author: rexkell * explain: */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.CLASS) public @interface BindView { int value() default -1; }
3.3 解析注解
import android.app.Activity; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * author: rexkell * explain: */ public class MyBindView { private static Map<Class, Method> classMethodMap=new HashMap<>(); public static void bind(Activity target){ if (target!=null){ Method method = classMethodMap.get(target.getClass()); try { if (method==null){ //獲取編譯生成的注解類 String bindClassName= target.getPackageName()+".Bind"+target.getClass().getSimpleName(); Class bindClass=Class.forName(bindClassName); method=bindClass.getMethod("bindView",target.getClass()); classMethodMap.put(target.getClass(),method); } method.invoke(null,target); } catch (Exception e) { e.printStackTrace(); } } } }
由於是編譯時產生的注解,需要通過 extends AbstractProcessor 來實現
import com.google.auto.service.AutoService; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; /** * author: rexkell * explain: */ @AutoService(Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_7) public class BindProcess extends AbstractProcessor { private Elements mElementsUtil; private Map<TypeElement,Set<Element>> mBindViewElems; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mElementsUtil=processingEnv.getElementUtils(); mBindViewElems=new HashMap<>(); } @Override public Set<String> getSupportedAnnotationTypes() { //添加需要解析的自定義注解類 Set<String> types=new HashSet<>(); types.add(BindView.class.getCanonicalName()); types.add(BindLayout.class.getCanonicalName()); return types; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { System.out.println("Process start!"); initBindElems(roundEnv.getElementsAnnotatedWith(BindView.class)); generateJavaClass(); System.out.println("Process finish!"); return true; } //初始化綁定的控件 private void initBindElems(Set<? extends Element> bindElems){ for (Element bindElem : bindElems){ TypeElement enclosedElem=(TypeElement) bindElem.getEnclosingElement(); Set<Element> elems=mBindViewElems.get(enclosedElem); if (elems==null){ elems=new HashSet<>(); mBindViewElems.put(enclosedElem,elems); System.out.println(enclosedElem.getSimpleName()); } elems.add(bindElem); System.out.println("Add bind elem "+bindElem.getSimpleName()); } } private void generateJavaClass(){ //生成Bind+ClassName+.class 文件,文件內容實現findViewById for (TypeElement enclosedElem: mBindViewElems.keySet()){ MethodSpec.Builder methodSpesBuilder = MethodSpec.methodBuilder("bindView") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(ClassName.get(enclosedElem.asType()),"activity") .returns(TypeName.VOID); BindLayout bindLayoutAnno =enclosedElem.getAnnotation(BindLayout.class); if (bindLayoutAnno!=null){ methodSpesBuilder.addStatement(String.format(Locale.US,"activity.setContentView(%d)",bindLayoutAnno.value())); } for (Element bindElem : mBindViewElems.get(enclosedElem)){ methodSpesBuilder.addStatement(String.format(Locale.US,"activity.%s=(%s)activity.findViewById(%d)", bindElem.getSimpleName(),bindElem.asType(),bindElem.getAnnotation(BindView.class).value())); } TypeSpec typeSpec=TypeSpec.classBuilder("Bind"+enclosedElem.getSimpleName()) .superclass(TypeName.get(enclosedElem.asType())) .addModifiers(Modifier.FINAL,Modifier.PUBLIC) .addMethod(methodSpesBuilder.build()) .build(); JavaFile file = JavaFile.builder(getPackageName(enclosedElem),typeSpec).build(); try { file.writeTo(processingEnv.getFiler()); } catch (IOException e) { e.printStackTrace(); } } } private String getPackageName(TypeElement typeElement){ return mElementsUtil.getPackageOf(typeElement).getQualifiedName().toString(); } }
3.4 在需要使用bindViewId 注解中引入模塊。
@BindView(R.id.edt_longitude) EditText edtLongitude; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedPreferences sharedPreferences= this.getSharedPreferences("theme",MODE_PRIVATE); int themeId=sharedPreferences.getInt("themeId",2); if (themeId==1){ setTheme(R.style.BaseAppThemeNight); }else if (themeId==0){ setTheme(R.style.AppTheme); } setContentView(R.layout.activity_main); MyBindView.bind(this); }