这里为了加深对编译时注解框架的使用同时为了完善之前Android模块间通信参数的获取过程,决定自己写一个简单的编译时注解框架,主要是替代
getIntent().getExtras().getSerializable
获取参数代码的编写。感兴趣的朋友可以先浏览之前的Android模块化学习和Android编译时注解项目学习。详细代码路径点这里。
回顾一下编译时注解框架的大致流程:
(1)编写注解代码
(2)编写注解处理器,用于生成java文件实现getIntent的获取过程
(3)编写API用于调用注解处理器生成的java文件
在我看来,编译时注解框架的核心在于处理一些重复的代码,解放我们的时间,所以最重要在于如何生成java文件来实现上面的getIntent过程,而其余的代码其实只要套用模板即可。
一、编写注解
(1)新建了一个ioc-annotation模块,同时增加了BindParameter这个注解,具体代码如下:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindParameter {
String value();
}
这个注解代码很简单,就设置了作用域和注解类型
二、编写注解处理器
(1)新建ioc-compiler模块,这个模块必须是java模块,否则一些方法无法调用
(2)只实现具体生成的java代码,其他皆可套用模板来开发。
public String generateJavaCode()
{
StringBuilder builder = new StringBuilder();
builder.append("// Generated code. Do not modify!\n");
builder.append("package ").append(packageName).append(";\n\n");
builder.append("import com.example.francis.bmodule.*;\n");
builder.append("import com.example.francis.commonmodule.inject.ParameterInject;\n");
builder.append('\n');
builder.append("public class ").append(proxyClassName).append(" implements " + ParameterProxyInfo.PROXY + "<" + typeElement.getQualifiedName() + ">");
builder.append(" {\n");
generateMethods(builder);
builder.append('\n');
builder.append("}\n");
return builder.toString();
}
private void generateMethods(StringBuilder builder)
{
builder.append("@Override\n ");
builder.append("public void inject(" + typeElement.getQualifiedName() + " host) {\n");
for (String id : injectVariables.keySet())
{
VariableElement element = injectVariables.get(id);
String name = element.getSimpleName().toString();
String type = element.asType().toString();
builder.append("if(host.getIntent() != null){\n");
builder.append("host." + name).append(" = ");
builder.append("(" + type + ")(host.getIntent().getExtras().getSerializable( \"" + id + "\"));\n");
builder.append("}\n");
}
builder.append(" }\n");
}
我觉得可以这样调试,这块代码不一定要一次性就写对,毕竟没有真正的运行过,可以等build项目生成具体java文件后再修改,如果有报错,编译器会直接提示,通过修改编译后的java文件在反过来修改这里的方法,也更直观有效。编译后生成的代码如下:
package com.example.francis.bmodule;
import com.example.francis.bmodule.*;
import com.example.francis.commonmodule.inject.ParameterInject;
public class BmoduleMainActivity$$ParameterInject implements ParameterInject<com.example.francis.bmodule.BmoduleMainActivity> {
@Override
public void inject(com.example.francis.bmodule.BmoduleMainActivity host) {
if(host.getIntent() != null){
host.user = (com.example.francis.commonmodule.User)(host.getIntent().getExtras().getSerializable( "user"));
}
}
}
三、编写API
编写API的代码也是套用模板,直接使用即可,它的原理在于调用刚才生成的Java文件实现具体的赋值。代码这里就不再贴出来,详细可以直接查看上面提供的项目demo。
四、具体使用
(1)在使用项目中添加依赖,包括注解,api和注解处理器模块的依赖
implementation project(':commonModule')
implementation project(':ioc-annotation')
annotationProcessor project(':ioc-compiler')
(2)代码中使用注解并调用API接口
public class BmoduleMainActivity extends AppCompatActivity {
@BindParameter("user")
User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bmodule_main);
ParameterInjector.injectParameter(this);
Toast.makeText(this, "user: " + user.getName() + " age: " + user.getAge(), Toast.LENGTH_LONG).show();
}
}