安卓开发者一般都知道ButterKnife,他实现了findViewById,onClick这些无脑操作,释放了程序员的双手,使我们有更多时间去处理逻辑代码。
今天我要手写ButterKnife核心代码实现,简单介绍下ButterKnife的核心原理,在编译时(java文件转class文件过程)使用注解处理器,帮我们写findViewById,通过反射获取这个生成的类,调用这个类的父类接口方法,实现调用子类实现方法。可能现在有点蒙,那撸码后就会清晰不少。
//首先我们建个Java的lib库管理所有注解,命名为annotations,里面建个BindView自定义注解类;
//再建个Java的lib库,注解处理器,对所有注解的处理,
这里需要在build.grald 依赖几个库实现注解处理器的注解注册,和Log的打印处理,配置编码格式,依赖注解管理库
dependencies { //注解处理器 annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4' //自动生成service,省去了配置resources/META-INF/services,配合@AutoService(Processor.class)使用 compileOnly 'com.google.auto.service:auto-service:1.0-rc4' //是square推出的开源java代码生成框架,提供Java Api生成.java源文件配置才能看到日志 implementation 'com.squareup:javapoet:1.11.1' //依赖注解管理库 implementation project(path: ':annotations')
//指定编译的编码
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
}
//指定编译的编码
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
//这个库建个AnnotationCompiler类 核心代码,这个类继承AbstactProcessor;
//实现process方法和重新三个方法
//核心代码在process方法里面处理,其他三个重新方法,都很简单。
//现在分析下核心方法
/** * 生成代码 的核心逻辑 * * @param roundEnvironment */ private void setAnnotation(RoundEnvironment roundEnvironment) { Map<String, List<VariableElement>> map = new HashMap<>(); //获取使用BindView注解的元素集合 Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class); //元素分类保存到map for (Element element : elementsAnnotatedWith) { //获取元素所在类的类名 VariableElement variableElement = (VariableElement) element; String activityName = variableElement.getEnclosingElement().getSimpleName().toString(); //获取类里面的注解元素集合 List<VariableElement> list = map.get(activityName); if (list == null) { list = new ArrayList<>(); map.put(activityName, list); } list.add(variableElement); } //------------------代码执行到这元素分类结束------------ //写文件 if (map.size() > 0) { //需要对每个activity单独写一个文件 Iterator<String> iterator = map.keySet().iterator(); //写对象 Writer writer = null; while (iterator.hasNext()) { //获取类名 String activityName = iterator.next(); //获取类名对应 元素集合 List<VariableElement> list = map.get(activityName); //获取元素的包名 //获取元数包裹成-->类元素 TypeElement typeElement = (TypeElement) list.get(0).getEnclosingElement(); String packageName = processingEnv.getElementUtils().getPackageOf(typeElement).toString(); //创建文件 try { JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + activityName + "_ViewBinding"); //获取写对象 writer = sourceFile.openWriter(); // package cn.gxy.mybutterknife; writer.write("package " + packageName + ";\n"); // import cn.gxy.mybutterknife.IBinder; writer.write("import " + packageName + ".IBinder;\n"); // public class MainActivity_ViewBinding implements IBinder<cn.gxy.mybutterknife.MainActivity>{ writer.write("public class " + activityName + "_ViewBinding implements IBinder<" + packageName + "." + activityName + ">{\n"); // @Override writer.write("@Override\n"); // public void bind(cn.gxy.mybutterknife.MainActivity target){ //public void bind(final cn.gxy.mybutterknife.MainActivity target){ writer.write("public void bind(" + packageName + "." + activityName + " target){\n"); // target.tv_text=(android.widget.TextView)target.findViewById(2131165325); //这段代码有几个地方是变的需要获取变动的数据才可以写 ---》tv_text TextView 2131165325 for (VariableElement element : list) { //获取元素名称 --->tv_text String name = element.getSimpleName().toString(); //获取元素类型 TypeMirror typeMirror = element.asType(); //获取元素注解 BindView annotation = element.getAnnotation(BindView.class); //通过注解获取id int id = annotation.value(); //获取变的数据就可以写了 writer.write("target." + name + "=(" + typeMirror + ")target.findViewById(" + id + ");\n"); } // }} writer.write("}\n}"); //代码执行到这文件就写完了 } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }
//我们主APP使用依赖注解库和注解管理库;
在buil.grald里面配
dependencies {
//注意注册处理器使用 这个导包annotationProcessor
annotationProcessor project(path: ':annotationCompiler')
//注解管理类
implementation project(path: ':annotations')
}
//新建个接口IBinder,代码如下和简单,主要是人注解处理器生成的类实现这接口
//暴露使用者调用的API类,代码也很简单,反射获取生成的类,使用多态调用方法
public class ButterKnife { public static void init(Activity activity) { //获取注解处理器生成类的 全类名 String packageName = activity.getClass().getCanonicalName() + "_ViewBinding"; //反射获取注解处理器 生成 try { Class<?> aClass = Class.forName(packageName); IBinder iBinder = (IBinder) aClass.newInstance(); //多态 调用子类实现方法 iBinder.bind(activity); } catch (Exception e) { e.printStackTrace(); } } }
//在MainActivity代码
public class MainActivity extends AppCompatActivity { @BindView(R.id.tv_text) TextView tv_text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.init(this); tv_text.setText("butterKnife成功"); } // @OnClick(R.id.tv_text) // public void onViewClick(View view) { // Toast.makeText(this, "测试点击", Toast.LENGTH_SHORT).show(); // // } }
//build下工程自动生成如下图类,说明注解处理器没有问题,就可以运行项目了
//如果想实现点击其实就回调MainActivity的方法实现点击,掌握上面实现点击就不难了
源码地址https://github.com/zhudaihao/MyButterknife