Android自定義注解


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);
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM