Android中利用APT生成代码
来源:互联网 发布:数据汇集平台 编辑:程序博客网 时间:2024/06/05 15:32
APT已经不新鲜了,虽然我们都知道这是个什么东西:
APT(Annotation Processing Tool 的简称),可以在代码编译期解析注解,并且生成新的 Java 文件。
但是为了能自己动手采用APT写一个框架那才能说是真的了解它、所以本文模仿butterknife自己写一个方便加深印象。
首先我们先看一段小代码
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 }});
很熟悉吧、然后很烦心吧。
每个界面都要这么写表示心累..
那要不换个姿势
@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(); }
这才对嘛,这样子我们才能高效的愉快的开发代码…
这是怎么实现
定义注解
BindView、Intent、OnClick因为有这些东西那么肯定是注解了,撩起袖子马上定义这几个注解了、这就不一一写出来随便举个做解释一下:
@Retention(RetentionPolicy.CLASS)// 表示我们用于编译注解@Target(ElementType.FIELD)// 表示我们是用于属性上public @interface BindView { int value();}
AbstractProcessor来生成代码
肯定是要顶一个类来实现AbstractProcessor这里给一个初始化的模板
@AutoService(Processor.class)public class NeacyProcesser extends AbstractProcessor { private Filer mFiler; //文件相关的辅助类 private Elements mElementUtils; //元素相关的辅助类 private Messager mMessager; //日志相关的辅助类 @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); mFiler = processingEnvironment.getFiler(); mElementUtils = processingEnvironment.getElementUtils(); mMessager = processingEnvironment.getMessager(); } @Override public Set<String> getSupportedAnnotationTypes() {// 要处理的相关注解类 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; }}
上面的”模板”代码写好了之后其中的process就是用于生成代码用的,所以在这里我们才要真正的实现APT的难点,同样我们拿Intent来做解释:
for (Element element : roundEnvironment.getElementsAnnotatedWith(Intent.class)) { // ...some codes}
通过getElementsAnnotatedWith我们可以根据注解来获取Element对象、这个时候我们就要来解释一些Element相关的copy网上一个例子(文章末尾一起给出例子的出处)。
package com.example;public class Foo { // TypeElement如果你的注解是用于处理类的时候 private int a; // VariableElement如果你的注解是用于处理属性的时候 private Foo other; // VariableElement public Foo() {} // ExecuteableElement如果你的注解是用于处理方法的时候 public void setA( // ExecuteableElement int newA // TypeElement ) { }}
看了一下上面我添加的注释然后结合代码就能明白这几个Element是干嘛用的了~
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; }
这里是采用神奇的Square公司开源的JavaPoet来生成代码,当然如果要用字符串拼接也是可以的。
这里推荐一篇JavaPoet的文章看了你就能懂得上面的代码是什么一下了JavaPoet
最后在process方法中
try { javaFile.writeTo(mFiler); } catch (IOException e) { return true; }
那么很快就能生成一个java文件了,这里把我项目里面生成的代码复制出来
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); }}
其实主要弄懂了这些Elemet是什么意思、怎么用然后就是利用JavaPoet来拼接代码即可了。
最后我们再跟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(); } }
这样我们就能想butterknife那样子在Activity中一行代码之后所以有标记注解的都帮我们完成了。
NeacyFinder.inject(this);
感谢
http://brucezz.itscoder.com/use-apt-in-android
http://blog.csdn.net/crazy1235/article/details/51876192
项目地址
https://github.com/Neacy/NeacyFinder
- Android中利用APT生成代码
- Android 利用 APT 技术在编译期生成代码
- Android 利用 APT 技术在编译期生成代码
- Android 利用 APT 技术在编译期生成代码
- Android注解-编译时生成代码 (APT)
- android apt编译时期自动生成代码
- Android 利用annotationProcessor生成Java代码
- Android中利用Random生成随机数
- 在Android中利用iText生成PDF
- android中利用javah生成头文件
- Android中利用Random生成随机数
- 1.Android注解-编译时生成代码 APT(Annotation Processing Tool ) Poet 说明
- 2.Android注解-编译时生成代码 APT(Annotation Processing Tool ) 实例说明
- android 使用apt(编译时注解) 自动生成第三方的狗皮膏药代码
- 利用APT实现Android编译时注解
- 利用APT实现Android编译时注解
- Android中利用代码去除标题栏
- 利用CodeSmith在自己的工程中动态生成代码
- Map四种获取key和value值的方法,以及对map中的元素排序
- NestedScrolling详解
- CAS详解
- map containsKey与get方法区别经典总结
- 树莓派控制数字舵机转动
- Android中利用APT生成代码
- Java网络编程重点总结
- mongo索引创建和索引分析
- 搜索中的记录步数
- Java高级部分--线程重点总结
- 手把手教你Tiny4412 Android5.0 定制 按下开发板上的按键通过广播发送到应用层
- Android 高级控件(三)
- Tomcat 7.0多端口配置
- 奇偶剪枝