zoukankan      html  css  js  c++  java
  • xUtils3源码分析(一):view的绑定

    概述

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

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

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


    项目结构

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


    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这个内部类:


    x.Ext

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


    x.Ext.init(Application app)方法


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

    View注入

    我们找到一个BaseActivity:


    BaseActivity


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


    x.view()


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


    ViewInjectorImpl.registerInstance()


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


    ViewInjectorImpl实现了ViewInjector接口

    看看ViewInjector接口:


    ViewInjector

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


    MainActivity

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


    ContentView


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


    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方法:


    BaseActivity


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


    x.view().inject(this)


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


    findContentView(handlerType)

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


    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方法,然后再反射调用该方法,将布局idR.layout.activity_main设置上去,这样就完成了MainActivity布局的设置,基本原理就是通过注解+反射,还是比较简单的。
    最后一句代码:


    injectObject()

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


    ViewFinder

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


    检测参数是否合法


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


    递归调用


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


    核心代码

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


    被注解的字段


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


    findViewById方法


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


    重载的 findViewById方法


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


    反射设置 view 对象


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



    文/悠嘻侠(简书作者)
    原文链接:http://www.jianshu.com/p/7791f3b8c234
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
  • 相关阅读:
    【PAT甲级】1043 Is It a Binary Search Tree (25 分)(判断是否为BST的先序遍历并输出后序遍历)
    Educational Codeforces Round 73 (Rated for Div. 2)F(线段树,扫描线)
    【PAT甲级】1042 Shuffling Machine (20 分)
    【PAT甲级】1041 Be Unique (20 分)(多重集)
    【PAT甲级】1040 Longest Symmetric String (25 分)(cin.getline(s,1007))
    【PAT甲级】1039 Course List for Student (25 分)(vector嵌套于map,段错误原因未知)
    Codeforces Round #588 (Div. 2)E(DFS,思维,__gcd,树)
    2017-3-9 SQL server 数据库
    2017-3-8 学生信息展示习题
    2017-3-5 C#基础 函数--递归
  • 原文地址:https://www.cnblogs.com/Sharley/p/5669774.html
Copyright © 2011-2022 走看看