xUtils3源码分析(一):view的绑定

xUtils3源码分析(一):view的绑定_第1张图片

概述

xUtils3是国人开发的一款功能丰富的Android快速开发框架,值得研究下。
zip包下载:[ZIP]
xutils主要分以下几个模块

  • 视图绑定模块
  • 网络请求模块
  • 数据库模块
  • 图片加载模块

我们将逐一透过源码分析,本文分析视图绑定模块,包含View的注入和View事件的注入。
我们将项目导入AndroidStudio,项目结构:

xUtils3源码分析(一):view的绑定_第2张图片
项目结构

xutils为项目源码,sample为使用方法举例。
我们通过分析sample这个示例项目来分析xutils的内部细节。
首先我们看看MyApplication这个类:

xUtils3源码分析(一):view的绑定_第3张图片
MyApplication

xUtils3在初始化的时候必须在自定义的 Application中来完成初始化,代码为 x.Ext.init(this);,首先就涉及到了这个 x类,我们打开看看:

/**
 * Created by wyouflf on 15/6/10.
 * 任务控制中心, http, image, db, view注入等接口的入口.
 * 需要在在application的onCreate中初始化: x.Ext.init(this);
 */
public final class x {
...省略代码
}

通过类注释我们可以看到x类是所有模块的入口。
那我们看看x.Ext这个内部类:

xUtils3源码分析(一):view的绑定_第4张图片
x.Ext

该类提供了一系列的静态成员变量,和对应的set方法,对应了xutils提供的几个功能模块。
看看init方法:

x.Ext.init(Application app)方法

很明显将 Application绑定到 app上,方便全局调用。
接下来我们分模块说明:

View注入

我们找到一个BaseActivity:

xUtils3源码分析(一):view的绑定_第5张图片
BaseActivity

ActivityonCreate方法中,调用:
x.view().inject(this)来完成视图注解框架的初始化。
看看 view()方法做了什么:

xUtils3源码分析(一):view的绑定_第6张图片
x.view()

该方法是用来进行 Ext.viewInjector的初始化的,那么我们到 ViewInjectorImpl看看
ViewInjectorImpl.registerInstance();是如何初始化的:

xUtils3源码分析(一):view的绑定_第7张图片
ViewInjectorImpl.registerInstance()

非常清楚,一个单例模式,而 ViewInjectorImpl其实实现了 ViewInjector接口:

ViewInjectorImpl实现了ViewInjector接口

看看ViewInjector接口:

xUtils3源码分析(一):view的绑定_第8张图片
ViewInjector

该接口的说明很明了,就是可以不同对象类型进行视图注入,如View,Avtivity,以及ViewHolder,fragment,以满足各个场景的使用。
那我们进入到具体的Activity进行分析吧:

xUtils3源码分析(一):view的绑定_第9张图片
MainActivity

可以看到MainActivity继承了BaseActivity,另外我们可以很明显的看到两种注解:
@ContentView(id)@ViewInject(id),我们先看看ContentView注解的源码:

xUtils3源码分析(一):view的绑定_第10张图片
ContentView

@Target(ElementType.TYPE)说明了该注解作用于类,接口或者枚举类型上。
@Retention(RetentionPolicy.RUNTIME)说明该注解会一直保留到JVM运行时。
int Value()说明可以注解参数的类型为 int类型;
那么 @ContentView(R.layout.activity_main)放入的就是布局 activity_mainid值。
再来看看 ViewInject注解:

xUtils3源码分析(一):view的绑定_第11张图片
ViewInject

@Target(ElementType.FIELD)说明该组件作用在成员变量上。
@Retention(RetentionPolicy.RUNTIME)说明该注解会保留到JVM运行时。
int value();说明注解参数类型为 int,而 int parentId() default 0说明可以填写一个父View的id,默认为0。
关于 java注解的基本使用,大家可以自行搜索。
现在我们知道了两个注解的作用:
ContentView注解是用来注入主布局界面的,而 ViewInject注解是用来注入具体控件的。
那么当 MainActivity回调 onCreate方法时,因为继承了 BaseActivity,所以自然就走到 BaseActivityonCreate方法:
xUtils3源码分析(一):view的绑定_第12张图片
BaseActivity

那么接下来我们看看这个 x.view().inject(this);中的 inject(this)实现方法吧:

xUtils3源码分析(一):view的绑定_第13张图片
x.view().inject(this)

先获取了传入 ActivityClass对象,然后将这个 Class作为参数传入 findContentView(handlerType)方法,从名字就可以看出该方法肯定是获取 ContentView的注解对象的:

xUtils3源码分析(一):view的绑定_第14张图片
findContentView(handlerType)

该方法也是比较简单的,首先判断了 thisCls是不是null,或者是不是非法的Class,看下IGNORED:

xUtils3源码分析(一):view的绑定_第15张图片
IGNORED

这里看到 IGNORED是一个 HashSet保存了一些需要忽略的 Class对象。
通过检测后 ContentView contentView = thisCls.getAnnotation(ContentView.class);这句代码其实就是获取 thisCls上的注解 ContentView类,这里就是 MainActivity上的 ContentView注解。
如果获取的 ContentViewnull就继续在 thisCls的父类中获取。
这样我们分析完了 findContentView(handlerType)方法,作用就是获取传入类或父类上的注解 ContentView类。
我们继续回到 inject()方法,获取到 ContentView注解后,如果不为 null那么就通过 int viewId = contentView.value();获取注解中填写的 id值,也就是 R.layout.activity_main的值,然后

获取注解中填写的 id 值

之后就是通过反射获取 MainActivity上的 setContentView方法,然后再反射调用该方法,将布局id值 R.layout.activity_main设置上去,这样就完成了 MainActivity布局的设置,基本原理就是通过注解+反射,还是比较简单的。
最后一句代码:

injectObject()

首先我们看看方法中的第三个参数是个ViewFinder对象,将MainActivity通过构造传递进去了。
先看看这个ViewFinder类的内容:

xUtils3源码分析(一):view的绑定_第16张图片
ViewFinder

该类的主要作用就是用于获取绑定的View对象,就是将ViewActivityfindViewById方法进行封装,先大致了解下。
然后再返回injectObject()方法,该方法较长,一部分一部分的贴出:

检测参数是否合法

首先还是检测是否是合法的类,然后:

递归调用

这里进行递归调用,然后是重点:

xUtils3源码分析(一):view的绑定_第17张图片
核心代码

146行是获取所有声明的字段,这里我们就是MainActivity中的字段了,然后开始循环。
150-157行是检测字段是不是合法的类型,如果合法才能继续。
159行就是获取字段上的ViewInject注解类。
162行就是如果获取到的ViewInject类不为null,就将ViewInject注解中填写的viewid和父viewid作为参数传递给finder类来获取绑定的View对象,回顾下MainActivity中的字段:

xUtils3源码分析(一):view的绑定_第18张图片
被注解的字段

可以看到只写了 view的id,并没有写父 view的id,那么父 view的id就是默认值 0了。
明白了再看看 ViewFinder类中:

xUtils3源码分析(一):view的绑定_第19张图片
findViewById方法

36-38行就是说如果 pid大于 0,那么就获取父 view对象,看看 findViewById()方法:

重载的 findViewById方法

这里就是封装了 findViewById方法,适用于 View对象或者 Activity,这里我们是 Activity
继续看40-45行,因为我们没有写 pid所以代码执行44行,这样我们就获取到了绑定的 View对象了。
这样我们再回到 ViewInjectorImpl类的 injectObject(...)方法:

反射设置 view 对象

这里就很清楚了,如果获取的 View对象不为 null,那么通过反射调用,将 View对象设置到 field上,这样就完成了一个视图控件的绑定,过程并不是很复杂。
今天就先到这里吧,下一篇分析 xutils3是如何绑定事件的。

你可能感兴趣的:(xUtils3源码分析(一):view的绑定)