好,我们接着parseBindView的步骤3 ,忘记了在哪里了,咦。
可以看下上一篇,哈哈。
步骤3
BindingClass bindingClass = targetClassMap.get(enclosingElement); if (bindingClass != null) { ViewBindings viewBindings = bindingClass.getViewBinding(getId(id)); if (viewBindings != null && viewBindings.getFieldBinding() != null) { FieldViewBinding existingBinding = viewBindings.getFieldBinding(); error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)", BindView.class.getSimpleName(), id, existingBinding.getName(), enclosingElement.getQualifiedName(), element.getSimpleName()); return; } } else { bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement); }
如果map(缓存)中已经有了就直接到到这个BindingClass实例。BindingClass这个我们后面还会再说。
1.如果不为空,则根据id获取保存在bindingClass中的ViewBindings实例,
如果viewBindings不为空且viewBindings.getFieldBinding不为空则抛出异常,什么意思呢?就是说一个id不能bind多次。
2.如果为空,则bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement);
获取并返回,参数是最初的那个map和父节点。
private BindingClass getOrCreateTargetClass(Map<TypeElement, BindingClass> targetClassMap, TypeElement enclosingElement) { BindingClass bindingClass = targetClassMap.get(enclosingElement); //再次判断 if (bindingClass == null) { //获取父节点的类型名字,这个不用太关系 TypeName targetType = TypeName.get(enclosingElement.asType()); if (targetType instanceof ParameterizedTypeName) { targetType = ((ParameterizedTypeName) targetType).rawType; } //获取该enclosingElement就是父节点所在的包名称 String packageName = getPackageName(enclosingElement); //类名字 String className = getClassName(enclosingElement, packageName); //根据包名称和类名称获取bindingClassName实体 //并且加入了_ViewBinding 哈哈,有点意思是了。不是吗?? ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding"); //是否是final 类 boolean isFinal = enclosingElement.getModifiers().contains(Modifier.FINAL); //创建了一个BindingClass实例 bindingClass = new BindingClass(targetType, bindingClassName, isFinal); //加入集合,缓存 targetClassMap.put(enclosingElement, bindingClass); } return bindingClass; }
到此我们的parseBindView的步骤3就完了。
步骤4:parseBindView步骤4
//@BindView(R.id.word) // TextView word; //name就是word String name = element.getSimpleName().toString(); //类型的名字 TypeName type = TypeName.get(elementType); //是否要求可为空 boolean required = isFieldRequired(element); //生成FieldViewBinding实体 FieldViewBinding binding = new FieldViewBinding(name, type, required);
步骤5.
添加到bindingClass中的成员变量的实体的集合中,方便生成java源文件也就是xxxxx_ViewBinding文件的成员变量的初始化存在 bindingClass.addField(getId(id), binding);
其它的注解都是一样的。至此查找并解析成员变量的流程就完了。
接下来是处理控件事件的监听的流程。
注解事件源码流程分析(OnClick,OnItemClick等)
我们回到findAndParseTargets方法。
//... 省略成员变量的注解 // Process each annotation that corresponds to a listener. //处理方法的比如一些OnClick,OnItemClick等 for (Class<? extends Annotation> listener : LISTENERS) { findAndParseListener(env, listener, targetClassMap, erasedTargetNames); } // Try to find a parent binder for each. for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) { TypeElement parentType = findParentType(entry.getKey(), erasedTargetNames); if (parentType != null) { BindingClass bindingClass = entry.getValue(); BindingClass parentBindingClass = targetClassMap.get(parentType); bindingClass.setParent(parentBindingClass); } } return targetClassMap; }
处理注解事件同样也分为查找和解析2个大步骤。
LISTENERS是butterknife支持的注解集合
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 void findAndParseListener(RoundEnvironment env, Class<? extends Annotation> annotationClass, Map<TypeElement, BindingClass> targetClassMap, Set<TypeElement> erasedTargetNames) { for (Element element : env.getElementsAnnotatedWith(annotationClass)) { //检查合法性问题 if (!SuperficialValidation.validateElement(element)) continue; try { //解析注解 parseListenerAnnotation(annotationClass, element, targetClassMap, erasedTargetNames); } catch (Exception e) { StringWriter stackTrace = new StringWriter(); e.printStackTrace(new PrintWriter(stackTrace)); error(element, "Unable to generate view binder for @%s.\n\n%s", annotationClass.getSimpleName(), stackTrace.toString()); } } }我们看一下parseListenerAnnotation方法,传入了注解类annotationClass,该节点element,最初的那个集合targetClassMap。
比较长,我在方法里注释效果会比较好,哈哈
private void parseListenerAnnotation(Class<? extends Annotation> annotationClass, Element element, Map<TypeElement, BindingClass> targetClassMap, Set<TypeElement> erasedTargetNames) throws Exception { // This should be guarded by the annotation's @Target but it's worth a check for safe casting. //必需是方法类型的,节点元素为ExecutableElement if (!(element instanceof ExecutableElement) || element.getKind() != METHOD) { throw new IllegalStateException( String.format("@%s annotation must be on a method.", annotationClass.getSimpleName())); } //方法对应的是ExecutableElement,前文我们已经简单的说明了一下 ExecutableElement executableElement = (ExecutableElement) element; //获取父节点 TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); // Assemble information on the method. // 获取注解信息 Annotation annotation = element.getAnnotation(annotationClass); //该注解value方法,每一个注解都有(butterknife提供的都是数组) //为什么是数组?因为支持下面这种 @OnClick({R.id.hello,R.id.hello2}) void sayHello() { } //反射注解方法value Method annotationValue = annotationClass.getDeclaredMethod("value"); //不是数组抛出异常 if (annotationValue.getReturnType() != int[].class) { throw new IllegalStateException( String.format("@%s annotation value() type not int[].", annotationClass)); } //反射调用 int[] ids = (int[]) annotationValue.invoke(annotation); //方法名字 String name = executableElement.getSimpleName().toString(); boolean required = isListenerRequired(executableElement); // Verify that the method and its containing class are accessible via generated code. //检查方法的修饰符,和成员变量一样,这里就不写了,嘻嘻 boolean hasError = isInaccessibleViaGeneratedCode(annotationClass, "methods", element); hasError |= isBindingInWrongPackage(annotationClass, element); //一个注解的方法不能有形同的id,or抛出异常 Integer duplicateId = findDuplicate(ids); if (duplicateId != null) { error(element, "@%s annotation for method contains duplicate ID %d. (%s.%s)", annotationClass.getSimpleName(), duplicateId, enclosingElement.getQualifiedName(), element.getSimpleName()); hasError = true; } //获取该注解ListenerClass.class注解,什么意思呢?就是 //butterknife提供的方法注解 包含了另外一个注解 //可以跳过代码看下面的文字说明。 ListenerClass listener = annotationClass.getAnnotation(ListenerClass.class); if (listener == null) { throw new IllegalStateException( String.format("No @%s defined on @%s.", ListenerClass.class.getSimpleName(), annotationClass.getSimpleName())); } //检查id的合法性,里面有个Optional注解 for (int id : ids) { //id 为 -1 ,不合法 if (id == NO_ID.value) { if (ids.length == 1) { //一个参数情况,且方法的参数适用了Optional注解,则抛出异常 if (!required) { error(element, "ID-free binding must not be annotated with @Optional. (%s.%s)", enclosingElement.getQualifiedName(), element.getSimpleName()); hasError = true; } } else { error(element, "@%s annotation contains invalid ID %d. (%s.%s)", annotationClass.getSimpleName(), id, enclosingElement.getQualifiedName(), element.getSimpleName()); hasError = true; } } } //获取实现的方法 ListenerMethod method; ListenerMethod[] methods = listener.method(); // methods就是OnItemClick 注解的ListenerClass注解的初始化值, 比如下面这种,肯定是个 // method = @ListenerMethod( // name = "onItemClick", // parameters = { // "android.widget.AdapterView<?>", // "android.view.View", // "int", // "long" // } // ) if (methods.length > 1) { //抛异常,不可能走到这因为butterknife提供的都是1个默认的,能骗到我,我可是上过小学的人,哈哈 throw new IllegalStateException(String.format("Multiple listener methods specified on @%s.", annotationClass.getSimpleName())); } else if (methods.length == 1) { //如果有method属性值即这种onItemClick,则callbacks必须为空,也就是2者不能同时使用 if (listener.callbacks() != ListenerClass.NONE.class) { throw new IllegalStateException( String.format("Both method() and callback() defined on @%s.", annotationClass.getSimpleName())); } method = methods[0]; } else { // 否则使用callback //反射ListenerClass注解中的callback方法 Method annotationCallback = annotationClass.getDeclaredMethod("callback"); Enum<?> callback = (Enum<?>) annotationCallback.invoke(annotation); Field callbackField = callback.getDeclaringClass().getField(callback.name()); method = callbackField.getAnnotation(ListenerMethod.class); //如果没有ListenerMethod.class注解 抛出异常,也就是说你使用了callback,则必须提供ListenerMethod.class注解 if (method == null) { throw new IllegalStateException( String.format("No @%s defined on @%s's %s.%s.", ListenerMethod.class.getSimpleName(), annotationClass.getSimpleName(), callback.getDeclaringClass().getSimpleName(), callback.name())); } } //检查方法的合法性,就是你使用的注解的方法的参数不能butterknife的参数的个数(也就是android系统的那种) // Verify that the method has equal to or less than the number of parameters as the listener. List<? extends VariableElement> methodParameters = executableElement.getParameters(); if (methodParameters.size() > method.parameters().length) { error(element, "@%s methods can have at most %s parameter(s). (%s.%s)", annotationClass.getSimpleName(), method.parameters().length, enclosingElement.getQualifiedName(), element.getSimpleName()); hasError = true; } //检查返回值,就是你使用的注解的方法的参数不能butterknife的参数的个数(也就是android系统的那种) // Verify method return type matches the listener. TypeMirror returnType = executableElement.getReturnType(); if (returnType instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable) returnType; returnType = typeVariable.getUpperBound(); } if (!returnType.toString().equals(method.returnType())) { error(element, "@%s methods must have a '%s' return type. (%s.%s)", annotationClass.getSimpleName(), method.returnType(), enclosingElement.getQualifiedName(), element.getSimpleName()); hasError = true; } if (hasError) { return; } //下面是方法参数的检查,不做分析了,太细了。记住一点就行了,你写的不和系统的实现方法一样就抛出异常 Parameter[] parameters = Parameter.NONE; if (!methodParameters.isEmpty()) { parameters = new Parameter[methodParameters.size()]; BitSet methodParameterUsed = new BitSet(methodParameters.size()); String[] parameterTypes = method.parameters(); for (int i = 0; i < methodParameters.size(); i++) { VariableElement methodParameter = methodParameters.get(i); TypeMirror methodParameterType = methodParameter.asType(); if (methodParameterType instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable) methodParameterType; methodParameterType = typeVariable.getUpperBound(); } for (int j = 0; j < parameterTypes.length; j++) { if (methodParameterUsed.get(j)) { continue; } if (isSubtypeOfType(methodParameterType, parameterTypes[j]) || isInterface(methodParameterType)) { parameters[i] = new Parameter(j, TypeName.get(methodParameterType)); methodParameterUsed.set(j); break; } } if (parameters[i] == null) { StringBuilder builder = new StringBuilder(); builder.append("Unable to match @") .append(annotationClass.getSimpleName()) .append(" method arguments. (") .append(enclosingElement.getQualifiedName()) .append('.') .append(element.getSimpleName()) .append(')'); for (int j = 0; j < parameters.length; j++) { Parameter parameter = parameters[j]; builder.append("\n\n Parameter #") .append(j + 1) .append(": ") .append(methodParameters.get(j).asType().toString()) .append("\n "); if (parameter == null) { builder.append("did not match any listener parameters"); } else { builder.append("matched listener parameter #") .append(parameter.getListenerPosition() + 1) .append(": ") .append(parameter.getType()); } } builder.append("\n\nMethods may have up to ") .append(method.parameters().length) .append(" parameter(s):\n"); for (String parameterType : method.parameters()) { builder.append("\n ").append(parameterType); } builder.append( "\n\nThese may be listed in any order but will be searched for from top to bottom."); error(executableElement, builder.toString()); return; } } } //最后构造MethodViewBinding实体,形成方法的实体 MethodViewBinding binding = new MethodViewBinding(name, Arrays.asList(parameters), required); //构造BindingClass BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement); for (int id : ids) { //将生成的方法加入到bindingClass的方法集合中,一切都是为了生存java代码而准备。 if (!bindingClass.addMethod(getId(id), listener, method, binding)) { error(element, "Multiple listener methods with return value specified for ID %d. (%s.%s)", id, enclosingElement.getQualifiedName(), element.getSimpleName()); return; } } // Add the type-erased version to the valid binding targets set. erasedTargetNames.add(enclosingElement); }
ListenerClass/ListenerMethod 注解说明
@Target(METHOD) @Retention(CLASS) @ListenerClass( targetType = "android.widget.AdapterView<?>", setter = "setOnItemClickListener", type = "android.widget.AdapterView.OnItemClickListener", method = @ListenerMethod( name = "onItemClick", parameters = { "android.widget.AdapterView<?>", "android.view.View", "int", "long" } ) ) public @interface OnItemClick { /** View IDs to which the method will be bound. */ @IdRes int[] value() default { View.NO_ID }; }
import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Retention(RUNTIME) @Target(ANNOTATION_TYPE) public @interface ListenerClass { String targetType(); /** Name of the setter method on the {@linkplain #targetType() target type} for the listener. */ String setter(); /** * Name of the method on the {@linkplain #targetType() target type} to remove the listener. If * empty {@link #setter()} will be used by default. */ String remover() default ""; /** Fully-qualified class name of the listener type. */ String type(); /** Enum which declares the listener callback methods. Mutually exclusive to {@link #method()}. */ Class<? extends Enum<?>> callbacks() default NONE.class; /** * Method data for single-method listener callbacks. Mutually exclusive with {@link #callbacks()} * and an error to specify more than one value. */ ListenerMethod[] method() default { }; /** Default value for {@link #callbacks()}. */ enum NONE { } }
package butterknife.internal; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Retention(RUNTIME) @Target(FIELD) public @interface ListenerMethod { /** Name of the listener method for which this annotation applies. */ String name(); /** List of method parameters. If the type is not a primitive it must be fully-qualified. */ String[] parameters() default { }; /** Primitive or fully-qualified return type of the listener method. May also be {@code void}. */ String returnType() default "void"; /** If {@link #returnType()} is not {@code void} this value is returned when no binding exists. */ String defaultReturn() default "null"; }
可以把这3个整体来看。ListenerMethod 这个注解包含了方法的返回值,名字,参数,是实现的那些方法;ListenerClass是set的那些方法属性,包含setter等,我们看到了OnItemClick设置的值就是我们平常写的那种,嘻嘻。
至此,我们的findAndParseTargets方法算是走完了。里面有很多细节。
为什么要分析有关细节呢?可以学习下大神的方法和理解有关处理的细节啊,哈哈。