【Android】APT(컴 파일 시 코드 생 성)
APT(Annotation Processing Tool)는 주 해 를 처리 하 는 도구 입 니 다.정확히 말 하면 자바 c 의 도구 입 니 다.컴 파일 할 때 주 해 를 스 캔 하고 처리 하 는 데 사 용 됩 니 다.주해 처리 기 는 자바 코드(또는 컴 파일 된 바이트 코드)를 입력 하여**자바 파일 을 출력 으로 생 성 합 니 다.쉽게 말 하면 컴 파일 기간 에 주 해 를 통 해 자바**파일 을 생 성 하 는 것 입 니 다.
역할.
APT 를 사용 하 는 장점 은 편리 하고 간단 하 며 중복 되 는 코드 를 적 게 사용 할 수 있다 는 것 이다.
ButterKnife,Dagger,EventBus 등 주해 프레임 워 크 를 사용 한 학생 들 은 이 프레임 워 크 를 이용 하면 코드 가 적 고 주 해 를 조금 만 쓰 면 된다 는 것 을 느 낄 수 있다.사실 그들 은 주 해 를 통 해 코드 를 만 들 었 을 뿐이다.APT 에 대한 학습 을 통 해 그들 은 매우 강하 다 는 것 을 알 게 될 것 입 니 다~~
이루어지다
이렇게 많아
목 표 는 APT 를 통 해 하나의 기능 을 실현 하고
View
변수 에 대한 주 해 를 통 해View
의 바 인 딩ButterKnife
중의@BindView
을 실현 한다.(여기 참고)
프로젝트 생 성 Android Module 이름 app 생 성 Java library Module 이름 apt-annotation 생 성 Java library Module 이름 apt-processor 의존 apt-annotation 생 성 Android library Module 이름 apt-library 의존 apt-annotation,auto-service
구 조 는 다음 과 같다.
기능 은 주로 세 부분 으로 나 뉜 다.
apt-annotation
:사용자 정의 주석,저장@BindViewapt-processor
:주해 프로세서,apt-annotation
의 주해 에 따라 컴 파일 기간 에 코드 생 성xxxActivity_ViewBinding.java
apt-library
:도구 류,호출xxxActivity_ViewBinding.java
중의 방법 으로View
의 연결 을 실현 합 니 다.관 계 는 다음 과 같다.
app?app 은 기능 코드 가 아니 라 기능 을 검증 하 는 데 사 용 됩 니 다~~
1.apt-annotation(사용자 정의 주석)
주석 클래스 만 들 기
BindView
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
@Retention(RetentionPolicy.CLASS)
:실행 시 주해@Target(ElementType.FIELD)
를 나타 낸다.주해 범 위 는 클래스 구성원(구조 방법,방법,구성원 변수)임 을 나타 낸다.@Retention:보 존 된 시간의 길 이 를 정의 합 니 다.RetentionPoicy.SOURCE,RetentionPoicy.CLASS,RetentionPoicy.RUNTIME@Target:수 정 된 대상 범 위 를 정의 합 니 다 TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCALVARIABLE 등 상세 내용
실행 시 주석
BindView
을 정의 합 니 다.그 중에서value()
은 대응 하 는View
id
을 가 져 오 는 데 사 용 됩 니 다.2.apt-processor(주해 프로세서)
(중점 부분)
Module
에 의존 도 를 추가 합 니 다.dependencies {
implementation 'com.google.auto.service:auto-service:1.0-rc2'
implementation project(':apt-annotation')
}
Android Studio 가 3.0 으로 업그레이드 되면 Gradle 도 3.0 으로 업그레이드 된다.
implementation
이전compile
을 대체 했다.창설
BindViewProcessor
@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
private Messager mMessager;
private Elements mElementUtils;
private Map mProxyMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mMessager = processingEnv.getMessager();
mElementUtils = processingEnv.getElementUtils();
}
@Override
public Set getSupportedAnnotationTypes() {
HashSet supportTypes = new LinkedHashSet<>();
supportTypes.add(BindView.class.getCanonicalName());
return supportTypes;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnv) {
// Java
return false;
}
}
init
:초기 화.ProcessingEnviroment
,ProcessingEnviroment
유용 한 도구 류Elements
,Types
및Filer
getSupportedAnnotationTypes
:이 주해 처리 기 를 어느 주해 에 등 록 했 는 지 지정 합 니 다.여기 설명 은 주해BindView
getSupportedSourceVersion
:지정 한 자바 버 전 입 니 다.보통 여 기 를 되 돌려 줍 니 다SourceVersion.latestSupported()
process
:여기 서 주 해 를 스 캔,평가,처리 하 는 코드 를 쓰 고 자바 파일(process 의 코드 아래 상세 한 설명)을 생 성 할 수 있 습 니 다@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
private Messager mMessager;
private Elements mElementUtils;
private Map mProxyMap = new HashMap<>();
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
mMessager.printMessage(Diagnostic.Kind.NOTE, "processing...");
mProxyMap.clear();
//
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
for (Element element : elements) {
VariableElement variableElement = (VariableElement) element;
TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
String fullClassName = classElement.getQualifiedName().toString();
ClassCreatorProxy proxy = mProxyMap.get(fullClassName);
if (proxy == null) {
proxy = new ClassCreatorProxy(mElementUtils, classElement);
mProxyMap.put(fullClassName, proxy);
}
BindView bindAnnotation = variableElement.getAnnotation(BindView.class);
int id = bindAnnotation.value();
proxy.putElement(id, variableElement);
}
// mProxyMap, java
for (String key : mProxyMap.keySet()) {
ClassCreatorProxy proxyInfo = mProxyMap.get(key);
try {
mMessager.printMessage(Diagnostic.Kind.NOTE, " --> create " + proxyInfo.getProxyClassFullName());
JavaFileObject jfo = processingEnv.getFiler().createSourceFile(proxyInfo.getProxyClassFullName(), proxyInfo.getTypeElement());
Writer writer = jfo.openWriter();
writer.write(proxyInfo.generateJavaCode());
writer.flush();
writer.close();
} catch (IOException e) {
mMessager.printMessage(Diagnostic.Kind.NOTE, " --> create " + proxyInfo.getProxyClassFullName() + "error");
}
}
mMessager.printMessage(Diagnostic.Kind.NOTE, "process finish ...");
return true;
}
}
roundEnvironment.getElementsAnnotatedWith(BindView.class)
를 통 해 모든 주석elements
을 얻 은 다음elements
의 정 보 를mProxyMap
에 저장 하고 마지막 으로mProxyMap
을 통 해 대응 하 는 자바 파일 을 만 듭 니 다.그 중에서mProxyMap
은ClassCreatorProxy
의Map
집합 입 니 다.ClassCreatorProxy
자바 코드 를 만 드 는 프 록 시 클래스 입 니 다.다음 과 같 습 니 다.public class ClassCreatorProxy {
private String mBindingClassName;
private String mPackageName;
private TypeElement mTypeElement;
private Map mVariableElementMap = new HashMap<>();
public ClassCreatorProxy(Elements elementUtils, TypeElement classElement) {
this.mTypeElement = classElement;
PackageElement packageElement = elementUtils.getPackageOf(mTypeElement);
String packageName = packageElement.getQualifiedName().toString();
String className = mTypeElement.getSimpleName().toString();
this.mPackageName = packageName;
this.mBindingClassName = className + "_ViewBinding";
}
public void putElement(int id, VariableElement element) {
mVariableElementMap.put(id, element);
}
/**
* Java
* @return
*/
public String generateJavaCode() {
StringBuilder builder = new StringBuilder();
builder.append("package ").append(mPackageName).append(";
");
builder.append("import com.example.gavin.apt_library.*;
");
builder.append('
');
builder.append("public class ").append(mBindingClassName);
builder.append(" {
");
generateMethods(builder);
builder.append('
');
builder.append("}
");
return builder.toString();
}
/**
* Method
* @param builder
*/
private void generateMethods(StringBuilder builder) {
builder.append("public void bind(" + mTypeElement.getQualifiedName() + " host ) {
");
for (int id : mVariableElementMap.keySet()) {
VariableElement element = mVariableElementMap.get(id);
String name = element.getSimpleName().toString();
String type = element.asType().toString();
builder.append("host." + name).append(" = ");
builder.append("(" + type + ")(((android.app.Activity)host).findViewById( " + id + "));
");
}
builder.append(" }
");
}
public String getProxyClassFullName()
{
return mPackageName + "." + mBindingClassName;
}
public TypeElement getTypeElement()
{
return mTypeElement;
}
}
위의 코드 는 주로
Elements
,TypeElement
에서 원 하 는 정 보 를 얻 는 것 이다.예 를 들 어 package name,Activity 명,변수 유형,id 등 을 통 해StringBuilder
코드 를 조금씩 맞 추고 각 대상 은 각각 대응 하 는Java
파일 을 대표 한다.생각 지도 못 했 지!자바 코드 는 이렇게 쓸 수 있 습 니 다~생 성 된 코드 를 미리 보 세 요(정렬 되 지 않 고 포맷 되 었 습 니 다)
public class MainActivity_ViewBinding {
public void bind(com.example.gavin.apttest.MainActivity host) {
host.mButton = (android.widget.Button) (((android.app.Activity) host).findViewById(2131165218));
host.mTextView = (android.widget.TextView) (((android.app.Activity) host).findViewById(2131165321));
}
}
결함
.java
을 통 해 자바 코드 를 조금씩 맞 추 는 것 이 번 거 로 울 뿐만 아니 라 잘못 쓰기 도 쉽 습 니 다~~더 좋 은 방안 은
StringBuilder
을 통 해 이러한 자바 코드 를 더욱 간단하게 생 성 할 수 있다.(나중에 얘 기 할 게 요.라 이브 러 리 auto-service 에 의존 하여 주석 처리 기 를 사용 할 때 먼저 설명 해 야 합 니 다.절차:1.processors 라 이브 러 리 의 main 디 렉 터 리 에서 resources 자원 폴 더 를 새로 만들어 야 합 니 다.2.resources 폴 더 에 META-INF/services 디 렉 터 리 폴 더 를 만 듭 니 다.3.META-INF/services 디 렉 터 리 폴 더 에 javax.annotation.processing.Processor 파일 을 만 듭 니 다.4.javax.annotation.processing.Processor 파일 에 주석 프로세서 의 전 칭 을 기록 합 니 다.패키지 경 로 를 포함 합 니 다.)이렇게 성명 하 는 것 도 너무 번 거 로 운 데?이것 이 바로 auto-service 를 도입 하 는 이유 다.auto-service 의@AutoService 를 통 해 AutoService 주석 처리 장 치 를 자동 으로 생 성 할 수 있 습 니 다.Google 에서 개발 한 것 으로 META-INF/services/javax.annotation.processing.Processor 파일 을 생 성 하 는 데 사 용 됩 니 다.
3.apt-library 도구 류
Processor 부분 을 완성 하여 거의 큰 성 과 를 거 두 었 습 니 다.
javapoet
에서 대응 하 는BindViewProcessor
을 만 들 었 습 니 다.우 리 는 어떻게 호출 합 니까?당연히 반사 지!!!Module
xxxActivity_ViewBinding.java
에 의존 도 를 추가 합 니 다.dependencies {
implementation project(':apt-annotation')
}
주석 도구 클래스 만 들 기
build.gradle
public class BindViewTools {
public static void bind(Activity activity) {
Class clazz = activity.getClass();
try {
Class bindViewClass = Class.forName(clazz.getName() + "_ViewBinding");
Method method = bindViewClass.getMethod("bind", activity.getClass());
method.invoke(bindViewClass.newInstance(), activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
BindViewTools
의 부분 은 비교적 간단 하 다.반 사 를 통 해 대응 하 는apt-library
종 류 를 찾 은 다음 에 그 중의ViewBinding
방법 으로bind()
의 연결 을 완성 한다.지금까지 모든 관련 코드 를 다 써 서 마침내 꺼 내 서 미 끄 러 질 수 있 게 되 었 다.
4、app
Module 의
View
에 의존(Gradle>=2.2)dependencies {
implementation project(':apt-annotation')
implementation project(':apt-library')
annotationProcessor project(':apt-processor')
}
Android Gradle 플러그 인 2.2 버 전의 출시,Android Gradle 플러그 인 은
build.gradle
라 는 기능 을 제공 하여 완전히 대체 합 니 다annotationProcessor
(Gradle<2.2)Projectandroid-apt
에서:buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
Module 의
build.gradle
에서:apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
apt project(':apt-processor')
}
buile.gradle
에 사용 하고MainActivity
의 앞 에View
주 해 를 붙 여BindView
를 전달 하면 된다.public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv)
TextView mTextView;
@BindView(R.id.btn)
Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BindViewTools.bind(this);
mTextView.setText("bind TextView success");
mButton.setText("bind Button success");
}
}
운행 결 과 는 모두 가 알 고 있 을 것 이다.이것
id
의 기능 이 완성 되 었 다 는 것 을 증명 하기 위해 서 나 는 그림 을 붙 였 다.생 성 된 코드
위의 기능 은 자바 코드 를 만 드 는 것 입 니 다.그러면 생 성 된 코드 는 어디 에 있 습 니까?app/build/generated/source/apt 에서 생 성 된 자바 파일 에 대응 하 는 코드 를 찾 을 수 있 습 니 다.
public class MainActivity_ViewBinding {
public void bind(com.example.gavin.apttest.MainActivity host) {
host.mButton = (android.widget.Button) (((android.app.Activity) host).findViewById(2131165218));
host.mTextView = (android.widget.TextView) (((android.app.Activity) host).findViewById(2131165321));
}
}
javapoet 을 통 해 코드 생 성
위 는
BindView
에서ClassCreatorProxy
를 통 해 대응 하 는 자바 코드 를 생 성 한다.이런 방법 은 비교적 번 거 롭 고 더 우아 한 방법 이 있 는데 그것 이 바로 자바 poet 이다.우선 의존 도 추가
dependencies {
implementation 'com.squareup:javapoet:1.10.0'
}
그리고
StringBuilder
에서public class ClassCreatorProxy {
// ...
/**
* Java
* @return
*/
public TypeSpec generateJavaCode2() {
TypeSpec bindingClass = TypeSpec.classBuilder(mBindingClassName)
.addModifiers(Modifier.PUBLIC)
.addMethod(generateMethods2())
.build();
return bindingClass;
}
/**
* Method
*/
private MethodSpec generateMethods2() {
ClassName host = ClassName.bestGuess(mTypeElement.getQualifiedName().toString());
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind")
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addParameter(host, "host");
for (int id : mVariableElementMap.keySet()) {
VariableElement element = mVariableElementMap.get(id);
String name = element.getSimpleName().toString();
String type = element.asType().toString();
methodBuilder.addCode("host." + name + " = " + "(" + type + ")(((android.app.Activity)host).findViewById( " + id + "));");
}
return methodBuilder.build();
}
public String getPackageName() {
return mPackageName;
}
}
마지막 으로
ClassCreatorProxy
에서 @Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
// ...
// javapoet
for (String key : mProxyMap.keySet()) {
ClassCreatorProxy proxyInfo = mProxyMap.get(key);
JavaFile javaFile = JavaFile.builder(proxyInfo.getPackageName(), proxyInfo.generateJavaCode2()).build();
try {
//
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
mMessager.printMessage(Diagnostic.Kind.NOTE, "process finish ...");
return true;
}
자바 코드 를
BindViewProcessor
맞 추 는 것 보다 훨씬 간결 하고 많다.마지막 으로 생 성 된 코드 는 이전 과 같 아서 붙 이지 않 습 니 다.javapoet 상세 용법
소스 코드
GitHub
레 퍼 런 스
컴 파 일 러 주해 의 APT 컴 파 일 러 주해 의 사용법 을 상세히 소개 합 니 다.Android 컴 파 일 러 주해-Android APT 향상 및 APT 기반 간단 한 응용 Android 컴 파일 시 주석 해석 프레임 워 크 를 만 드 는 것 은 처음에 알 아야 할 APT,annotationProcessor,android-apt,provide,사용자 정의 주석 일 뿐 입 니 다.
이상 잘못된 점 이 있 습 니 다.지적 해 주 셔 서 감사합니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.