ButterKnife(8.4.0版本)原理分析
来源:互联网 发布:自动打印软件 编辑:程序博客网 时间:2024/05/18 21:07
ButterKnife是鼎鼎大名的JakeWharton写的注解框架, 将你从findViewById这样无聊的体力活解脱出来。 github地址: https://github.com/JakeWharton/butterknife , 已超过1万颗星了, 很屌。
JakeWharton是square公司的大咖, 是Piccaso(图片开源框架)、RxJava/RxAndroid(响应式编程)、OkHttp(Http通讯开源框架)的主要开发者。 不夸张的说, 对于一个做互联网app的码农来说, 如果不知道他就是少见识了。
ButterKnife的集成方式和使用方法已在github上描述, 就不多说了。 8.4.0版本做了一些优化:
1、 删除了ButterKnife类的unbind方法。 改为保存ButterKnife.bind函数返回的Unbinder引用, 在onDestroy函数里调用Unbinder的unbind方法。
2、如下图所示,在编译过程会生成*_ViewBinding.java, 如SimpleActivity_ViewBinding.java。
3、ButterKnife对性能没影响, 一些人说用了注解和反射影响性能, 这个锅ButterKnife不背。 增加Java方法数量和apk体积到是真的, 毕竟生成了_ViewBinding.java文件。
基础知识:APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定), APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
生成*_ViewBinding.java文件的原理:在编译时期,javac会调用java注解处理器(APT)进行处理,通过自定义注解处理器来实现想要的功能, 例如ButterKnife在编译期间生成Java文件。
下面看ButterKnife的核心代码ButterKnifeProcessor.java, 在Android编译期间
@AutoService(Processor.class) //注册处理器,这样在编译时才会调用ButterKnifeProcessorpublic final class ButterKnifeProcessor extends AbstractProcessor { ... //支持的监听器 private static final List<Class<? extends Annotation>> LISTENERS = Arrays.asList(// OnCheckedChanged.class, // OnClick.class, // OnEditorAction.class, // OnFocusChange.class, // OnItemClick.class, // OnItemLongClick.class, // OnItemSelected.class, // OnLongClick.class, // OnPageChange.class, // OnTextChanged.class, // OnTouch.class // ); //支持注解的资源类型 private static final List<String> SUPPORTED_TYPES = Arrays.asList( "array", "attr", "bool", "color", "dimen", "drawable", "id", "integer", "string" ); ... //指定使用的Java版本 @Override public synchronized void init(ProcessingEnvironment env) { super.init(env); ... } //返回stirng类型的set集合,集合里包含了需要处理的注解</span></code>类型 @Override public Set<String> getSupportedAnnotationTypes() { Set<String> types = new LinkedHashSet<>(); for (Class<? extends Annotation> annotation : getSupportedAnnotations()) { types.add(annotation.getCanonicalName()); } return types; } //支持的所有注解类型 private Set<Class<? extends Annotation>> getSupportedAnnotations() { Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>(); annotations.add(BindArray.class); annotations.add(BindBitmap.class); annotations.add(BindBool.class); annotations.add(BindColor.class); annotations.add(BindDimen.class); annotations.add(BindDrawable.class); annotations.add(BindFloat.class); annotations.add(BindInt.class); annotations.add(BindString.class); annotations.add(BindView.class); annotations.add(BindViews.class); annotations.addAll(LISTENERS); return annotations; } //核心函数, 在这个函数里生成*_ViewBinding.java @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) { //查找所有的注解信息,并形成BindingClass保存到 map中 Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env); //遍历bindingMap生成类名_ViewBinding的java文件 for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) { TypeElement typeElement = entry.getKey(); BindingSet binding = entry.getValue(); JavaFile javaFile = binding.brewJava(sdk); try { javaFile.writeTo(filer); } catch (IOException e) { error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage()); } } return true; } //解析所有的注解 private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) { Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>(); Set<TypeElement> erasedTargetNames = new LinkedHashSet<>(); scanForRClasses(env); // Process each @BindArray element. for (Element element : env.getElementsAnnotatedWith(BindArray.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceArray(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindArray.class, e); } } // Process each @BindBitmap element. for (Element element : env.getElementsAnnotatedWith(BindBitmap.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceBitmap(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindBitmap.class, e); } } // Process each @BindBool element. for (Element element : env.getElementsAnnotatedWith(BindBool.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceBool(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindBool.class, e); } } // Process each @BindColor element. for (Element element : env.getElementsAnnotatedWith(BindColor.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceColor(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindColor.class, e); } } // Process each @BindDimen element. for (Element element : env.getElementsAnnotatedWith(BindDimen.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceDimen(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindDimen.class, e); } } // Process each @BindDrawable element. for (Element element : env.getElementsAnnotatedWith(BindDrawable.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceDrawable(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindDrawable.class, e); } } // Process each @BindFloat element. for (Element element : env.getElementsAnnotatedWith(BindFloat.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceFloat(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindFloat.class, e); } } // Process each @BindInt element. for (Element element : env.getElementsAnnotatedWith(BindInt.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceInt(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindInt.class, e); } } // Process each @BindString element. for (Element element : env.getElementsAnnotatedWith(BindString.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceString(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindString.class, e); } } // Process each @BindView element. for (Element element : env.getElementsAnnotatedWith(BindView.class)) { // we don't SuperficialValidation.validateElement(element) // so that an unresolved View type can be generated by later processing rounds try { parseBindView(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindView.class, e); } } // Process each @BindViews element. for (Element element : env.getElementsAnnotatedWith(BindViews.class)) { // we don't SuperficialValidation.validateElement(element) // so that an unresolved View type can be generated by later processing rounds try { parseBindViews(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindViews.class, e); } } // Process each annotation that corresponds to a listener. for (Class<? extends Annotation> listener : LISTENERS) { findAndParseListener(env, listener, builderMap, erasedTargetNames); } // Associate superclass binders with their subclass binders. This is a queue-based tree walk // which starts at the roots (superclasses) and walks to the leafs (subclasses). Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries = new ArrayDeque<>(builderMap.entrySet()); Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>(); while (!entries.isEmpty()) { Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst(); TypeElement type = entry.getKey(); BindingSet.Builder builder = entry.getValue(); TypeElement parentType = findParentType(type, erasedTargetNames); if (parentType == null) { bindingMap.put(type, builder.build()); } else { BindingSet parentBinding = bindingMap.get(parentType); if (parentBinding != null) { builder.setParent(parentBinding); bindingMap.put(type, builder.build()); } else { // Has a superclass binding but we haven't built it yet. Re-enqueue for later. entries.addLast(entry); } } } return bindingMap; } ...}
下面看Java文件的生成方法:
JavaFile brewJava() { //参数1:包名 //参数2:TypeSpec,这个可以生成class ,interface 等java文件 //注意addFileComment的参数,已经说明是生成的代码了 return JavaFile.builder(bindingClassName.packageName(), createBindingClass()) .addFileComment("<span style="color:#FF0000;">Generated code from Butter Knife. Do not modify!</span>") .build(); }
bind:
如何才能生成的Java文件呢?
答案是: ButterKnife.bind(this);方法。
unbind:
在新版的8.4.0中去除了 unbind方法。
<span style="font-size:18px;">ButterKnife.unbind //已经删除了</span>
并采用了接口的形式,让生成的类来实现释放引用。 例如:
<span style="font-size:18px;"> public final class SimpleAdapter$ViewHolder_ViewBinding implements Unbinder { @UiThread public SimpleAdapter$ViewHolder_ViewBinding(SimpleAdapter.ViewHolder target, View ) { //... } //... @Override public void unbind() { //... } }</span>
那如何unbind呢?ButterKnife.bind(this)返回值是一个Unbinder引用。
所以可以这样:
<span style="font-size:18px;"> Unbinder mUnbinder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); mUnbinder=ButterKnife.bind(this); //保存引用 } @Override protected void onDestroy() { super.onDestroy(); mUnbinder.unbind(); //释放所有绑定的view }</span>
综上, ButterKnife是个好东西,节省开发时间而且不影响性能。 是Android开发居家必备的良品。
- ButterKnife(8.4.0版本)原理分析
- ButterKnife 最新版本8.4.0详细使用
- Butterknife 8.4.0版本的使用:
- Android 源码分析 ButterKnife框架原理
- ButterKnife原理分析(一)设计思想
- Android Butterknife 框架源码解析(3)——Butterknife 8.7.0源码分析
- Butterknife原理
- ButterKnife源码分析(一)
- ButterKnife源码分析(二)
- 集成butterknife最新版本8.6.0
- Android ButterKnife 注解框架的使用详解和原理分析
- Android UI注解框架 ButterKnife源码及原理分析
- butterknife 8.4.0
- 学习ButterKnife 8.4.0
- ButterKnife 8.4.0
- Android Butterknife 8.4.0
- 关于ButterKnife 8.4.0以后版本报空指针的使用注意事项:
- butterKnife原理学习
- exec族函数解析
- 350_MeasureSpec使用
- Jdbc
- C++ 内联函数的使用
- 国嵌实时监控系统代码笔记(三)采集端 net.c
- ButterKnife(8.4.0版本)原理分析
- 相对路径的使用
- 从此不再更新博客,新的博客地址我会在以后公布
- UCenter 1.6.0 安装过程
- Android 内存泄漏
- Atitit 为什么网络会有延时 电路交换与分组交换的区别
- NRV优化详解
- 模版
- 文章标题