Android杂谈(21)+Java随笔(4):注解(下)分析ButterKnife
来源:互联网 发布:jetbrains软件 编辑:程序博客网 时间:2024/05/20 09:21
转载请注意:http://blog.csdn.net/wjzj000/article/details/54177914
本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)
注解(上):http://blog.csdn.net/wjzj000/article/details/53227352
安卓开发或多或少都会用到注解,无论是内置的注解还是其他的框架。我们都能感受到注解的作用,这里梳理一下ButterKinfe框架,也借此机会记录一下注解的用法。
在分析它的开始我们先简单写一个类似的框架,让我们更方便的去理解。
开始搞事情
- 直接贴代码,因为 思路比价清晰,解析过程主要是最基本的反射。
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface MyBindView { int value() default 0;}
public class MyBufferKnife { public static void bind(Activity activity){ getAnnotationInfos(activity); } private static void getAnnotationInfos(Activity activity) { Class clazz = activity.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { MyBindView myBindView = field.getAnnotation(MyBindView.class); if (myBindView != null) { int id = myBindView.value(); try { field.setAccessible(true); field.set(activity, activity.findViewById(id)); } catch (IllegalAccessException e) { } } } }}
public class MyButterKnifeActivity extends Activity{ @MyBindView(R.id.text) TextView text; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_butterknife); MyBufferKnife.bind(this); text.setText("我是MyButterKnife!"); }}
- 接下来让我们走进ButterKnife
Go
基本切入
- 最开始我们需要在Gradle中添加:
compile 'com.jakewharton:butterknife:8.4.0'apt 'com.jakewharton:butterknife-compiler:8.4.0'
- 最基本的应用我们会这么来写,首先是声明控件:
@BindView(R.id.rlv_main) RecyclerView rlvMain;//让我们简单看一看这个注解/** * Bind a field to the view for the specified ID. The view will automatically be cast to the field type. * 翻译:将字段绑定到指定ID的View,视图将自动转换为字段类型。 * <pre><code> * {@literal @}BindView(R.id.title) TextView title; * </code></pre> */@Retention(CLASS) @Target(FIELD)public @interface BindView { /** View ID to which the field will be bound. * 翻译:View将绑定字段的ID。(@IdRes注解时安卓内置的) */ @IdRes int value();}/** * Denotes that an integer parameter, field or method return value is expected * to be an id resource reference (e.g. {@code android.R.id.copy}). * 翻译:表示整数参数,字段或方法返回值应为id资源引用(例如{@code android.R.id.copy}) */@Documented@Retention(CLASS)@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})public @interface IdRes {}
- 其次便是在特定的地方就行绑定:
ButterKnife.bind(this);
我们都知道,注解的运转靠的是注解解释器。因此,接下来让我们进入ButterKnife,来看一看它内部的运转!
Ok,马上开始。让我们进入框架的入口:
@NonNull @UiThreadpublic static Unbinder bind(@NonNull Activity target) { View sourceView = target.getWindow().getDecorView(); return createBinding(target, sourceView);}
这里作者用到了俩个注解,一个不能为空(字段或方法返回值永远不能为空),一个是表示运行在Ui线程。
返回一个方法createBinding(),这里传进了一个我们传进来的Activity以及通过Activity获取的根部View。
进入ButterKnife
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) { Class<?> targetClass = target.getClass(); if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName()); //findBindingConstructorForClass()此方法的实现在下边展开 Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass); if (constructor == null) { return Unbinder.EMPTY; } //noinspection TryWithIdenticalCatches Resolves to API 19+ only type. try { return constructor.newInstance(target, source); } catch (IllegalAccessException e) { throw new RuntimeException("Unable to invoke " + constructor, e); } catch (InstantiationException e) { throw new RuntimeException("Unable to invoke " + constructor, e); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } if (cause instanceof Error) { throw (Error) cause; } throw new RuntimeException("Unable to create binding instance.", cause); } }
很简单,通过反射拿到Activity的Class,然后传到findBindingConstructorForClass()方法。
看的代码的时候让我们自动屏蔽一些判空啊,抛异常之类….它对我们的梳理代码没有关系…
@Nullable @CheckResult @UiThreadprivate static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) { /** * static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>(); * 这里通过通过传递的Class从Map中拿到Constructor * Unbinder:作者的一个接口...下文有展开。 */ Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls); if (bindingCtor != null) { if (debug) Log.d(TAG, "HIT: Cached in binding map."); return bindingCtor; } //获得类的全名 String clsName = cls.getName(); if (clsName.startsWith("android.") || clsName.startsWith("java.")) { if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search."); return null; } try { //反射相关的:返回与带有给定字符串名的类或接口相关联的 Class 对象。 Class<?> bindingClass = Class.forName(clsName + "_ViewBinding"); /** * public Constructor<T> getConstructor(Class<?>... parameterTypes) * 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 * parameterTypes 参数是 Class 对象的一个数组,这些 Class 对象按声明顺序标识构造方法的形参类型。 * 如果此 Class 对象表示非静态上下文中声明的内部类,则形参类型作为第一个参数包括显示封闭的实例。 */ bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class); if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor."); } catch (ClassNotFoundException e) { if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName()); bindingCtor = findBindingConstructorForClass(cls.getSuperclass()); } catch (NoSuchMethodException e) { throw new RuntimeException("Unable to find binding constructor for " + clsName, e); } //获取Constructor之后,加入到Map之中 BINDINGS.put(cls, bindingCtor); return bindingCtor;}
/** An unbinder contract that will unbind views when called. */// 通过它的命名我们可以大概知道含义,Unbinder:解绑定。public interface Unbinder { @UiThread void unbind(); Unbinder EMPTY = new Unbinder() { @Override public void unbind() { } };}
!!
到此ButterKnife这个类基本上就没有没有了处理业务的其他代码。
WTF?没了?没了…
让我们拉开ButterKnife的源码结构:
进入Utils
接下来我们看一看这里的方法。
public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who, Class<T> cls) { View view = findRequiredView(source, id, who); return castView(view, id, who, cls);}
public static View findRequiredView(View source, @IdRes int id, String who) { View view = source.findViewById(id); if (view != null) { return view; } String name = getResourceEntryName(source, id); throw new IllegalStateException("Required view '" + name + "' with ID " + id + " for " + who + " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'" + " (methods) annotation.");}
public static <T> T castView(View view, @IdRes int id, String who, Class<T> cls) { try { //将一个对象强制转换成此 Class 对象所表示的类或接口。 return cls.cast(view); } catch (ClassCastException e) { String name = getResourceEntryName(view, id); throw new IllegalStateException("View '" + name + "' with ID " + id + " for " + who + " was of the wrong type. See cause for more info.", e); }}
public static <T> T findOptionalViewAsType(View source, @IdRes int id, String who, Class<T> cls) { //在这我们通过传递进来的View进行findViewById() View view = source.findViewById(id); return castView(view, id, who, cls);}
在Utils之中我们可以看到我们想看到的东西也就是findViewById(),但是它任何被调用,我们似乎没有很明确的看到。
其实这些都蕴含在我们在Gradle的引用之中:
apt ‘com.jakewharton:butterknife-compiler:8.4.0’
apt就是注解解释工具。因此我们可以推断出相关的调用就在apt之中。
APT
APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。
Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
- 关于这方面的内容各位看官可以看一下:
- http://www.open-open.com/lib/view/open1462925582182.html
最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp
- Android杂谈(21)+Java随笔(4):注解(下)分析ButterKnife
- ButterKnife(注解)Android Studio插件安装
- Android Butterknife 框架源码解析(2)——谈谈Java的注解
- android注解框架ButterKnife详细使用文档(v7.0.1)
- Android-第三方框架ButterKnife(注解框架)
- Android-ButterKnife不能注解RatingBar(含ButterKnife部分原理以及View传递机制)
- JAVA 杂谈(4)
- Android 注解工具ButterKnife源码分析
- butterknife源码分析:谈一谈Java的注解
- 依赖注入(ButterKnife)(注解 )
- 简单实现ButterKnife(运行时注解)
- 简单实现ButterKnife(编译时注解)
- ButterKnife源码分析(一)
- ButterKnife源码分析(二)
- Android Butterknife 框架源码解析(3)——Butterknife 8.7.0源码分析
- 投资杂谈(下)
- 项目经理杂谈(下)
- 项目经理杂谈(下)
- C#反序列化XML异常:在 XML文档(0, 0)中有一个错误“缺少根元素”
- 【matlab】freqz函数的使用(一)
- Django添加js
- 【论坛项目】简单的论坛小项目-麻雀虽小,五脏俱全
- CV界的明星人物们和他们的主页地址
- Android杂谈(21)+Java随笔(4):注解(下)分析ButterKnife
- C语言main()主函数执行完毕后是否会再执行一段代码
- android studio异常处理之[Error: com.android.ide.common.process.ProcessException]
- 基于ssm框架正式环境echarts图表的使用
- TensorFlow使用基础(Basic Usage)
- -mtime+1
- 基于PCA和SVM的人脸识别
- 【C#】收官
- 一个demo,帮助您加深理解和记忆android activity 生命周期