Android apt 遇到问题的例子
Posted danfengw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android apt 遇到问题的例子相关的知识,希望对你有一定的参考价值。
这篇apt 的Demo 是为了解释我在编写apt 代码时遇到的一个问题。问题链接,简单写一下步骤,解释下遇到的问题。 as 版本:4.1.1 (版本不同配置上可能会有所不同)
前言
这里就简单仿照ButterKnife写个BindV的注解。先看下最终的使用
class MainActivity : AppCompatActivity()
@BindV(R.id.tv) // 1 注解
lateinit var tv: TextView;
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Binding.bind(this) // 2 调用相关绑定逻辑,前期不需要关注
tv.setText("哈哈哈111")
这里个点需要区分,如果你使用过ButterKnife 可以知道ButterKnife会自动给我门生成对应的Binding的文件,然后我们调用Bind方法就自动绑定了。这里我们也分成2个部分: 1 代码自动生成 2绑定逻辑
代码自动生成
1 创建3个lib
android Library: aptlib
Java or Kotlin Library: aptlib-anno (专门放我们编写的注解)
Java or Kotlin Library :aptlib-processor (编写动态生成文件的逻辑)
ps:必须是java library 有2个原因 apt 本来java 提供的,另外 Android库中不允许继承AbstractProcessor。
2 注解
aptlib-anno 库下面创建
BindV.kt
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.SOURCE)
annotation class BindV(val value: Int)
3 注解动态生成关联文件
aptlib-processor 库
步骤一
创建相关目录,如下,在main目录下创建resources/META-INF/services
然后创建文件:javax.annotation.processing.Processor
步骤二
添加 上面2 注解 中创建的aptlib-anno 库 以及 javapoet的库
implementation project(path: ':aptlib-anno')
implementation 'com.squareup:javapoet:1.13.0'
aptlib-anno 库:因为生成代码的时候有个getSupportedAnnotationTypes方法需要返回需要支持的注解类型
javapoet 库: 自动生成代码需要借助的库
步骤三
编写自动生成文件的代码
BindingProcessor.kt
class BindingProcessor : AbstractProcessor()
var filer: Filer? = null
override fun init(processorEnvironment: ProcessingEnvironment?)
super.init(processorEnvironment)
filer = processorEnvironment?.filer
override fun process(p0: MutableSet<out TypeElement>?, roundEnvironment: RoundEnvironment?): Boolean
roundEnvironment?.rootElements?.forEach element ->
val packageStr = element.enclosingElement.toString()
val classStr = element.simpleName.toString()
generateBindingFile(packageStr, classStr, element)
return false
private fun generateBindingFile(packageStr: String, classStr: String, element: Element)
// 下面代码都是依赖的javapoet 库完成的,具体用法参考github上javapoet 链接
val className = ClassName.get(packageStr, classStr + "_Binding")
val constructBuilder = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ClassName.get(packageStr, classStr), "activity")
var hasBinding = false
element.enclosedElements?.forEach enclosedElement ->
if (enclosedElement.kind == ElementKind.FIELD)
val bindV = enclosedElement.getAnnotation(BindV::class.java)
bindV?.let
hasBinding = true
constructBuilder.addStatement(
"activity.\\$N = activity.findViewById(\\$L)",
enclosedElement.simpleName,
bindV.value
)
val classBuilder = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC)
.addMethod(constructBuilder.build())
.build()
if (hasBinding)
JavaFile.builder(packageStr, classBuilder)
.build()
.writeTo(filer)
override fun getSupportedAnnotationTypes(): MutableSet<String>
return mutableSetOf(BindV::class.java.canonicalName)
步骤四
在步骤一:javax.annotation.processing.Processor 文件中添加我们自己编写的BindingProcessor.kt 的关联
com.example.aptlib_process.BindingProcessor
步骤五:测试代码自动生成使用
主项目中添加依赖
implementation project(path: ':aptlib-anno')
annotationProcessor project(path: ':aptlib-process')
public class MainActivity2 extends AppCompatActivity
@BindV(R.id.tv)
TextView tv;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
运行后可以看到自动生成的文件
public class MainActivity2_Binding
public MainActivity2_Binding(MainActivity2 activity)
activity.tv = activity.findViewById(2131231003);
从文件可以看出当我们调用MainActivity2_Binding的构造器的时候,findViewById的方法就可以被调用,因此接下来我们要做代码绑定相关的逻辑了
代码绑定
aptlib
步骤一
添加依赖
api project(path: ':aptlib-anno')
api: 是为了后面让上层依赖 ,之前主目录也依赖了aptlib-anno
,后面可以通过依赖aptlib
替换掉aptlib-anno
步骤二
特别简单:
public class Binding
companion object
fun bind(activity: AppCompatActivity)
// 通过反射调用自动生成类的构造器
try
val bindClass = Class.forName(activity.javaClass.canonicalName + "_Binding");
val constructor = bindClass.getDeclaredConstructor(activity.javaClass)
constructor.newInstance(activity)
catch (e: Exception)
e.printStackTrace()
步骤三:测试代码绑定
替换依赖
// implementation project(path: ':aptlib-anno')
implementation project(path: ':aptlib')
annotationProcessor project(path: ':aptlib-process')
如果绑定成功,tv.setText("哈哈哈222");
代码就不会报空指针
public class MainActivity2 extends AppCompatActivity
@BindV(R.id.tv)
TextView tv;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Binding.Companion.bind(this);
tv.setText("哈哈哈222");
出现的问题
上面写完之后,会发现,如果我主目录中使用的是kotlin代码编写的kt文件,就不会自动编译生成对应的bingding文件,这是为啥呢?就比如我写的下面的代码,不会生成对应的MainActivity_Binding,运行起来会直接崩溃
class MainActivity : AppCompatActivity()
@BindV(R.id.tv)
lateinit var tv: TextView;
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Binding.bind(this)
tv.setText("哈哈哈111")
以上是关于Android apt 遇到问题的例子的主要内容,如果未能解决你的问题,请参考以下文章