ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

IOC架构设计之ButterKnife源码&原理(二)中篇,android开发艺术探索pdf百度云

2021-11-19 16:58:01  阅读:161  来源: 互联网

标签:架构设计 class getSimpleName element ButterKnife 源码 annotationClass method append


其它的注解都是一样的。至此查找并解析成员变量的流程就完了。
接下来是处理控件事件的监听的流程。

注解事件源码流程分析(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 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 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()

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

开源分享完整内容戳这里

;
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 };

标签:架构设计,class,getSimpleName,element,ButterKnife,源码,annotationClass,method,append
来源: https://blog.csdn.net/m0_64319455/article/details/121426644

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有