关于Adnroid processor

LovelyInject

项目地址:https://github.com/xiejinlong/LovelyInject
这个是一个基于https://github.com/enbandari/TieGuanYin库实现的一个简易版的intent注入框架。

使用流程

使用注解

可以使用的有3个注解,BuilderActivity,BuilderFragment和BuilderModel,这三个注解是用来修饰类的,三个注解的retention都是编译期间,targetType都是ElementType.TYPE,也就是用来修饰Class。
其中,BuilderActivity用来修饰Activity,可以指定默认的跳转Scheme,会生成一个通过scheme跳转的静态方法。
BuilderFragme用来修饰Fragment。
BuilderModel用来修饰普通的model类。

@Retention(RetentionPolicy.CLASS)@Target(ElementType.TYPE)
  • 修饰activity
@BuilderActivity(routerValue = "to_test_scheme")    class TestActivity: Activity() {    @Fields    var name: String? = null    @Fields    var age: Int = 0    @Fields    var msg: String? = null    }
  • 修饰fragment
@BuilderFragment    class TestFragment: Fragment() {    @Fields    var name: String? = null    @Fields    var age: Int = 0    @Fields    var msg: String? = null    }
  • 修饰model类
@BuilderModel    class TestModel {    @Fields    var name: String? = null    @Fields    var age: Int = 0    @Fields    var msg: String? = null    }

编译build

  • 生成的ActivityBuilder
public final class TestActivityBuilder {  public static final String FIELD_NAME = "name";  public static final String FIELD_AGE = "age";  public static final String FIELD_MSG = "msg";  private String name;  private int age;  private String msg;  public static TestActivityBuilder builder() {    TestActivityBuilder builder = new TestActivityBuilder();    return builder;  }  public TestActivityBuilder name(String name) {    this.name = name;    return this;  }  public TestActivityBuilder age(int age) {    this.age = age;    return this;  }  public TestActivityBuilder msg(String msg) {    this.msg = msg;    return this;  }  private void fillIntent(Intent intent) {    intent.putExtra("name", name);    intent.putExtra("age", age);    intent.putExtra("msg", msg);  }  public void start(Context context) {    Intent intent = new Intent(context, TestActivity.class);    fillIntent(intent);    context.startActivity(intent);  }
  • 生成的fragmentBuilder
public final class TestFragmentBuilder {  public static final String FIELD_NAME = "name";  public static final String FIELD_AGE = "age";  public static final String FIELD_MSG = "msg";  private String name;  private int age;  private String msg;  public static TestFragmentBuilder builder() {    TestFragmentBuilder builder = new TestFragmentBuilder();    return builder;  }  public TestFragmentBuilder name(String name) {    this.name = name;    return this;  }  public TestFragmentBuilder age(int age) {    this.age = age;    return this;  }  public TestFragmentBuilder msg(String msg) {    this.msg = msg;    return this;  }  private void fillIntent(Intent intent) {    intent.putExtra("name", name);    intent.putExtra("age", age);    intent.putExtra("msg", msg);  }  public TestFragment build() {    TestFragment fragment = new TestFragment();    Intent intent = new Intent();    fillIntent(intent);    fragment.setArguments(intent.getExtras());    return fragment;  }}

-生成的modelBuilder

public final class TestModelBuilder {  public static final String FIELD_NAME = "name";  public static final String FIELD_AGE = "age";  public static final String FIELD_MSG = "msg";  private String name;  private int age;  private String msg;  public static TestModelBuilder builder() {    TestModelBuilder builder = new TestModelBuilder();    return builder;  }  public TestModelBuilder name(String name) {    this.name = name;    return this;  }  public TestModelBuilder age(int age) {    this.age = age;    return this;  }  public TestModelBuilder msg(String msg) {    this.msg = msg;    return this;  }  public TestModel build() {    TestModel model = new TestModel();    return model;  }}

这三个builder文件基本类似,每个@Fields修饰的成员变量都将生成一个对应的builder方法。activityBuilder会多一个fillIntent方法和start方法,用来填充intent和开启新页面。而fragmentBuilder会多一个fillIntent方法和build方法,fillIntent也是用来填充intent,而build方法是用来返回fragment实例的。

使用Builder

  • in ActivityBuilder
//调用TestActivityBuilder.builder().age(12)                    .name("xie")                    .msg("我是从mainAc过来的参数")                    .start(this)//使用变量,in TestActivity Toast.makeText(this,                "我是 $name,今年 $age, $msg", Toast.LENGTH_LONG).show()
  • in fragmentBuilder
//调用fragmentManager.beginTransaction()                    .replace(R.id.mainLayout,                            TestFragmentBuilder.builder()                                    .name("lovely")                                    .age(13)                                .msg("我是从testAc过来的参数").build())                    .commitAllowingStateLoss()//使用变量,in TestFragment Toast.makeText(context,                "我是 $name,今年 $age, $msg", Toast.LENGTH_LONG).show()

Prossor生成代码原理

上面就是通过builder生成的代码来给我简化使用流程,每一个activity和fragment都会相对应的生成一个Builder类来供我们使用。下面我们来详细了解一下这个框架的原理实现。

基本元素

Element

Element是基类,可在不同的情况下转化成不同的子类,具体的类型可以通过getKind方法获得

  • TypeElement: 表示类或者接口
  • VariableElement: 表示字段参数
  • PackageElement: 表示一个包
  • ExecutableElement: 表示方法
  • TypeParameterElement: 表示范型参数

TypeMirror

Element表示的是元素,而TypeMirror表示的是参数类型。可以通过getkind来获取参数类型。

TypeSpec

是javapoet库用来生成文件的主要的类。

ProcessingEnvironment

ProcessingEnvironment中提供了4个工具接口

  • lateinit var types: Types //java类型工具
  • lateinit var elements: Elements //注解获取出来的元素
  • lateinit var messager: Messager//消息输出
  • lateinit var filer: Filer//文件写入

实现流程

  1. 首先我们需要先创建出Annotation库,创建出对应的注解。
@Retention(RetentionPolicy.CLASS)@Target(ElementType.TYPE)public @interface BuilderActivity {    String routerValue() default "";}
  1. 创建出Processor库,并且自定义Processor
class KKProcessor : AbstractProcessor() {}

在这里需要注意的是,我们必须手动的建立Processor索引,不然编译期间不会执行到这个Processor。
需要在和java同级目下创建出resources/META-INF/services/javax.annotation.processing.Processor文件,然后在内部添加processor的引用。

com.inject.xie.processor.KKProcessor

而且,需要在app的gradle中添加该processor的编译。

kapt project(":Processor")
  1. 开始编译,解析注解
    在编译时,会调用到processor的process方法
override fun process(p0: MutableSet?, p1: RoundEnvironment?): Boolean {        LogUtils.warn("KKProcessor process")        return true    }

在这个方法中,我们需要解析出我们要的注解

//解析classenv.getElementsAnnotatedWith(BuilderActivity::class.java)                .asSequence()                .filter(SuperficialValidation::validateElement)                .filter { it.kind.isClass }                .toList()                .forEach { element ->                    LogUtils.warn("KKProcessor parasClass ${element.simpleName} is Activity~")                    if (ProcessorEnv.types.isSubtype(element.asType(), ClassType.KKACTIVITY.typeMirror)) {                        classMap[element] = KKActivityBuilder(element as TypeElement)                    }                }

通过getElementsAnnotatedWith方法来获取被BuilderActivity修饰的所有的类。其他几个注解类似,然后存储在classMap,这里需要注意一点,process方法可能会执行多次,所以需要将解析的产物放在map中或者每次解析都将list清空。
然后解析field

private fun parasFiled(env: RoundEnvironment) {        env.getElementsAnnotatedWith(Fields::class.java)                .asSequence()                .filter(SuperficialValidation::validateElement)                .filter { it.kind.isField }                .toList()                .forEach { element ->                    LogUtils.warn("KKProcessor parasFiled ${element.simpleName}")                    classMap[element.enclosingElement]?.addFiled(element)                }    }

将解析生成的fields存储到上一步生成的class产物中,这样,就拿到了被注解的类和其中的被注解的成员变量。
4. 生成代码

  • 创建class
        val classFileBuilder = TypeSpec.classBuilder(builderClassName)                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
  • 创建成员及成员的builder方法
fields.forEach { field ->            LogUtils.warn("fieldBuilder ${field.name}")            //构造临时变量            classFileBuilder.addField(FieldSpec.builder(field.asTypeName(), field.name, Modifier.PRIVATE).build())            //构造变量相关的静态变量            classFileBuilder.addField(FieldSpec.builder(String::class.java, KKActivityBuilder.CONST_POSIX + field.name.toUpperCase())                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)                    .initializer("\$S", field.name)                    .build())            //构造相关变量的builder方法            classFileBuilder.addMethod(MethodSpec.methodBuilder(field.name)                    .addModifiers(Modifier.PUBLIC)                    .addParameter(field.asTypeName(), field.name)                    .addStatement("this.${field.name} = ${field.name}")                    .addStatement("return this")                    .returns(builderClassTypeName)                    .build())        }
  • 创建方法
    首先需要静态的builder方法
//构造主builder        classFileBuilder.addMethod(MethodSpec.methodBuilder("builder")                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)                .returns(builderClassTypeName)                .addStatement("\$T builder = new \$T()", builderClassTypeName, builderClassTypeName)                .addStatement("return builder").build())

对于Activity,需要创建fillIntent和start方法

 //对于Activity,需要创建fillIntent和start方法        val intentMethod = MethodSpec.methodBuilder("fillIntent")                .addModifiers(Modifier.PRIVATE)                .addParameter(INTENT.java, "intent")        fields.forEach { field ->            //给fillIntent方法添加元素            intentMethod.addStatement("intent.putExtra(\$S, \$L)", field.name, field.name)        }        typeBuilder.addMethod(intentMethod.build())        //start        typeBuilder.addMethod(MethodSpec.methodBuilder("start")                .addModifiers(Modifier.PUBLIC)                .addParameter(CONTEXT.java, "context")                .addStatement("Intent intent = new Intent(context, \$L.class)", simpleName)                .addStatement("fillIntent(intent)")                .addStatement("context.startActivity(intent)")                .build())    }

对于fragment需要创建fillIntent和build方法

   //fragment也需要fillIntent        val intentMethod = MethodSpec.methodBuilder("fillIntent")                .addModifiers(Modifier.PRIVATE)                .addParameter(ClassType.INTENT.java, "intent")        fields.forEach { field ->            //给fillIntent方法添加元素            intentMethod.addStatement("intent.putExtra(\$S, \$L)", field.name, field.name)        }        typeBuilder.addMethod(intentMethod.build())        val originClassName = ClassName.get(packageName, simpleName.toString())        //通过builder方法返回实例        typeBuilder.addMethod(MethodSpec.methodBuilder("build")                .returns(originClassName)                .addModifiers(Modifier.PUBLIC)                .addStatement("\$T fragment = new \$T()", originClassName, originClassName)                .addStatement("Intent intent = new Intent()")                .addStatement("fillIntent(intent)")                .addStatement("fragment.setArguments(intent.getExtras())")                .addStatement("return fragment")                .build())    }
  • 写入文件
    当构建好了TypeSpec,通过Filer进行文件写入
 private fun writeJavaToFile(typeSpec: TypeSpec) {        try {            val file = JavaFile.builder(packageName, typeSpec).build()            file.writeTo(ProcessorEnv.filer)        } catch (e: IOException) {            e.printStackTrace()        }    }

这样,对应的生成文件就创建出来了。

  1. 依赖注入
    在文件生成之后,我们通过对应的Builder类来启动activity或者创建fragment实例,那我们如何直接在activity或者fragment中直接使用被注解的成员变量呢?这个其实也比较简单。
  • 对于activity
    在application中注册activity监听,然后通过onActivityCreate的回调方法中进行inject,这个方法会在oncreate之前调用。
 override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {                if (activity == null) {                    return                }                if (!activity.javaClass.isAnnotationPresent(BuilderActivity::class.java)) {                    //该activity没有被Builder标注,跳过                    return                }                Log.d("KKActivityBuilder", "onActivityCreated~")                val intent = activity.intent ?: return                var fields = activity.javaClass.declaredFields                inject(activity, fields, intent.extras)            }fun inject(activity: Activity?, fields: Array?, extras: Bundle?) {        if (fields == null) {            Log.d("KKActivityBuilder", "declaredFields is null, should return~")            return        }        fields.forEach { field ->            if (field.isAnnotationPresent(Fields::class.java)) {                val name = field.name                try {                    val access = field.isAccessible                    if (!access) field.isAccessible = true                    val value = getIntentExtra(extras, name)                    if (value != null) {                        field.set(activity, getIntentExtra(extras, name))                    } else {                        Log.d("KKActivityBuilder", "get value is null, continue~")                    }                    if (!access) field.isAccessible = false                } catch (e: Exception) {                    Log.e("KKActivityBuilder", "error in -> ${e.message}")                }            }        }    }    fun  getIntentExtra(extras: Bundle?, name: String): Any? {        return extras?.get(name)    }
  1. 判断当前的activity是否被BuilderActivity修饰过
  2. 如果被BuilderActivity修饰过,遍历fields,判断是否被Fields修饰过
  3. 如果被Fields修饰过,从intent中获取field的name对应的value,以object的形式取出即可
  4. 通过反射,给field赋值为上一步取出的值。
  5. 完成
  • 对于fragment
    fragment的注入其实与activity基本一致,只是fragment没有相对应的生命周期的监听,不过我们可以在统一的基类的onCreateView方法中调用inject方法进行注入。实际的注入流程完全一样。不过activity是从intent中取值,fragment是从argument中取值。

更多相关文章

  1. Android群英传知识点回顾——第五章:Android(安卓)Scroll分析
  2. Android屏蔽Home键,适配所有版本
  3. 深入android数据库操作
  4. android 常用Bitmap处理方法收集:普通裁剪,缩放,圆形裁剪
  5. 将TaintDroid编译进Android(安卓)2.3
  6. Android(安卓)Studio lint工具所提示的需要注意的内容简要记录
  7. Android(安卓)Notification.Builder通知案例分享
  8. Android中调用System.exit(0)
  9. Android(安卓)图片处理方法大全

随机推荐

  1. 如何将dict转换为spark map输出
  2. 当使用一个传送到另一个的python文件时,我
  3. matplotlib绘制符合论文要求的图片
  4. classmethod,staticmethod,还有类里面一般
  5. Python文件操作大全,随机删除文件夹内的任
  6. 获取错误“ValueError:int()的无效文字,基数
  7. C++各大有名库的介绍——准标准库Boost
  8. 从Python中的列表元素中删除URL
  9. python传递列表作为函数参数
  10. Python读取修改ini配置文件[ConfigParser