APT is not new anymore, although we all know what it is:
APT (short for Annotation Processing Tool) parses annotations at code compilation time and generates new Java files.
But to be able to write a framework using APT by yourself is to really understand it, so this article imitates butterknife Write your own to enhance your impression.
First let's look at a little bit of code
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
String key = getIntent().getStringExtra("key");
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// on click
}
});
Get familiar, then get upset.
Every interface should be written like this to express tiredness.
Do you want to change your position?
@BindView(R.id.fab) FloatingActionButton fab;
@Intent("key") String key;
@OnClick({R.id.fab}) public void fabClick() { Toast.makeText(this, "Neacy", Toast.LENGTH_LONG).show(); }
That's right, so we can develop code efficiently and happily...
How does this work
Definition Notes
BindView, Intent, OnClick because of these things are so definitely annotations, roll up your sleeve to define these annotations immediately, which do not need to be written out to explain it casually:
@Retention(RetentionPolicy.CLASS)// Indicates that we are used to compile annotations
@Target(ElementType.FIELD)// Indicates that we are using attributes
public @interface BindView {
int value();
}
AbstractProcessor to generate code
Must be the top class to implement AbstractProcessor Here's an initialized template
@AutoService(Processor.class)
public class NeacyProcesser extends AbstractProcessor {
private Filer mFiler; //File-related auxiliary classes
private Elements mElementUtils; //Element-related auxiliary classes
private Messager mMessager; //Log-related auxiliary classes
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
mElementUtils = processingEnvironment.getElementUtils();
mMessager = processingEnvironment.getMessager();
}
@Override
public Set<String> getSupportedAnnotationTypes() {// Related annotation classes to process
Set<String> types = new LinkedHashSet<>();
types.add(BindView.class.getCanonicalName());
types.add(OnClick.class.getCanonicalName());
types.add(Intent.class.getCanonicalName());
return types;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return true;
}
}
Once the "template" code above is written, the process in it is used to generate code, so here's the real difficulty in implementing APT. Let's also explain it with Intent:
for (Element element : roundEnvironment.getElementsAnnotatedWith(Intent.class)) {
// ...some codes
}
With getElementsAnnotatedWith we can get Element objects from annotations, and at this point we'll explain some of the Element-related copy net examples (with examples at the end of the article).
package com.example;
public class Foo { // TypeElement If your comment is for working with classes
private int a; // VariableElement If your comment is for processing attributes
private Foo other; // VariableElement
public Foo() {} // ExecuteableElement If your comment is for processing
public void setA( // ExecuteableElement
int newA // TypeElement
) {
}
}
Take a look at the comments I added above and combine them with the code to understand why these elements are used.
public JavaFile generateFinder() {
// method inject(final T host, Object source, Provider provider)
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder("inject")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(TypeName.get(mClassElement.asType()), "host", Modifier.FINAL)
.addParameter(TypeName.OBJECT, "source")
.addParameter(TypeUtil.PROVIDER, "provider");
for (IntentField field : mIntents) {
injectMethodBuilder.addStatement("host.$N = host.getIntent().getStringExtra($S)", field.getFieldName(), field.getKey());
}
// generate whole class
TypeSpec finderClass = TypeSpec.classBuilder(mClassElement.getSimpleName() + "$$Finder")
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(ParameterizedTypeName.get(TypeUtil.FINDER, TypeName.get(mClassElement.asType())))
.addMethod(injectMethodBuilder.build())
.build();
String packageName = mElementUtils.getPackageOf(mClassElement).getQualifiedName().toString();
JavaFile javaFile = JavaFile.builder(packageName, finderClass).build();
return javaFile;
}
This is using the magical Square company's open source JavaPoet to generate code, although string splicing is also possible.
Here's a JavaPoet article that you can read to understand what the above code is JavaPoet
Finally, in the process method
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
return true;
}
A java file will be generated soon, so here's a copy of the code generated in my project
public class MainActivity$$Finder implements Finder<MainActivity> {
@Override
public void inject(final MainActivity host, Object source, Provider provider) {
host.fab = (FloatingActionButton)(provider.findView(source, 2131558523));
View.OnClickListener listener;
host.key = host.getIntent().getStringExtra("key");
listener = new View.OnClickListener() {
@Override
public void onClick(View view) {
host.fabClick();
}
} ;
provider.findView(source, 2131558523).setOnClickListener(listener);
}
}
In fact, the main idea is to understand what these Elemet s mean, how to use them, and then splice the code using JavaPoet.
Finally, we'll add an intermediate layer of encapsulation similar to butterknife
public static void inject(Object host, Object source, Provider provider) {
String className = host.getClass().getName();
try {
Class<?> finderClass = Class.forName(className + FINDER_SUFFIX);
Finder finder = mFinderArrayMap.get(className);
if (finder == null) {
finder = (Finder) finderClass.newInstance();
mFinderArrayMap.put(className, finder);
}
finder.inject(host, source, provider);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
This way we can think of butterknife as having one line of code in the Activity, so all the tagged annotations are done for us.
NeacyFinder.inject(this);
Thank
http://brucezz.itscoder.com/use-apt-in-android
http://blog.csdn.net/crazy1235/article/details/51876192