ToyBricks简介以及原理分析
ToyBricks背景
我始终认为,在高内聚,低耦合的原则下,进行组件化,模块化,插件化都是移动应用开发的趋势。
为什么这么说呢?下面我们举个栗子:
大家都知道,以前Android应用开发中,可以使用HttpClient或者HttpUrlConnection来进行http访问。这里假设有一个耦合严重,但代码量巨大的项目,使用了基于HttpClient封装的loopj/android-async-http来进行http访问。但是,后来,Google明确支持使用HttpUrlConnection。此时,经过调研,你们觉得square/okhttp基于HttpUrlConnection,符合你们的要求。
现在,不管是否将HttpClient替换成okhttp,你们都可能面临以下困境:
- 需求都做不完,根本没有排期做这个替换。于是,你们面临离google的支持越来越远,离风险越来越近的困境;
- 辛辛苦苦耗费人力将HttpClient替换成okhttp。但由于两者变化很大,需要投入很多测试资源,来重新确认这些接口是否正常访问。一旦出现问题,还需要安排研发资源,去一一排查。
- 替换的工作量太大。替换一部分之后,发现没有足够人力去继续完成。于是,替换终止。整个工程又变得混乱和臃肿,同时包含了两种http的封装库和调用。
**模块化可以有效解决这些问题。通用的做法,是按照业务,功能等将整个项目分成不同的模块,由不同的研发测试小组负责。
每个模块又分为接口和实现两个部分。接口部分提供给模块外部调用,而实现部分则禁止来自外部的调用。**
那么,如何将模块的接口和实现部分关联起来呢?通过APT工具,可以轻松地将接口部分和实现部分关联起来。
APT,即Annotation Processing Tool,可以理解为“编译时注解处理器工具”。
官方说明:
“The apt tool is a command-line utility for annotation processing. It includes a set of reflective APIs and supporting infrastructure to process program annotations (JSR 175). These reflective APIs provide a build-time, source-based, read-only view of program structure. They are designed to cleanly model the Java programming language's type system after the addition of generics (JSR 14).”
简单理解如下:
apt工具是javac工具的一部分。在编译时,apt工具首先会扫描工程下Java源码中的编译时注解,再根据预先定义的编译时注解处理工具,生成指定的Java源码文件。紧接着,生成的Java源码文件和之前项目下的Java源码一起,由javac工具来编译成class。
但是,APT有一个局限性,就是只会扫描Java源码,不会扫描jar ,aar 和class 。也就是说,所有模块需要以源码形式存在。而现在通用的做法是,将模块打包成jar或者aar,发布到Maven库,再由其他模块自行引用。
有没有办法将APT的这种功能和特性延伸到jar和aar呢?
于是,ToyBricks应运而生。
ToyBricks简介
ToyBricks简介
ToyBricks是一个Android项目模块化的解决方案,主要包括四个部分,APT注解,APT注解处理器,ToyBricks插件(Gradle Plugin),ToyBricks库。
其中:
- APT注解,主要定义了两个注解:Interface(接口,例如:IText),Implementation (实现,例如:TextImpl)
- APT注解处理器,在javac编译java源码之前。APT注解处理器会扫描Java源码中带有上面两个注解的接口和类,并且生成一个json文件, ToyBricks.json.
- ToyBricks插件(Gradle Plugin),负责ToyBricks.json的打包,合并,生成Java源文件等工作
- ToyBricks,提供对外调用方法。通过参数传入接口,返回相应的实现。
ToyBricks原理分析
下面以接口IText和实现TextImpl为例,简单介绍下ToyBricks原理。
主要分为两个部分:
Android Library(最终可能打包成jar,aar,并发布到maven库)
如果工程是Android库模块,则主要流程如下:
1.在javac编译java源码之前,由APT注解处理器扫描Java源码中带有上面两个注解的接口和类,生成ToyBricks.json。
{ "interfaceList" : [ "com.github.snowdream.toybricks.app.IText" ], "globalImplementation" : { "com.github.snowdream.toybricks.app.IText" : "com.github.snowdream.toybricks.app.impl.NewTextGobalImpl" }, "defaultImplementation" : { "com.github.snowdream.toybricks.app.IText" : "com.github.snowdream.toybricks.app.impl.TextImpl" }, "singletonImplementation" : [ "com.github.snowdream.toybricks.app.impl.NewTextGobalImpl" ] }
2.在打包jar,aar的时候,由ToyBricks插件(Gradle Plugin)提前处理,保证ToyBricks.json能被拷贝进去jar包或者aar包的根目录下,并随同一起分布到maven仓库。
Android Application
如果工程是Android应用模块,则主要流程如下:
- 第一步,和Android Library第一步一致。
- Javac编译Java源代码
- 扫描所有依赖的库文件,过滤出所有包含ToyBricks.json文件的jar包或者aar包,并且提取出来。提取完毕后,合并所有的ToyBricks.json文件,成为一个ToyBricks.json。
- 将最终的ToyBricks.json按照预定规则生成一个Java源码文件.文件名为: InterfaceLoaderImpl.java
package com.github.snowdream.toybricks.annotation.impl; import com.github.snowdream.toybricks.annotation.InterfaceLoader; import java.lang.Class; import java.lang.Override; import java.util.HashMap; /** * * Created by snowdream * * This file is automatically generated by apt(Annotation Processing Tool) * Do not modify this file -- YOUR CHANGES WILL BE ERASED! * * This file should *NOT* be checked into Version Control Systems, * as it contains information specific to your local configuration. */ final class InterfaceLoaderImpl implements InterfaceLoader { private static HashMap<Class, Object> sSingletonMap = new HashMap<Class, Object>(); private static HashMap<Class, Class> sGlobalMap = new HashMap<Class, Class>(); private static HashMap<Class, Class> sDefaultMap = new HashMap<Class, Class>(); public InterfaceLoaderImpl() { addGlobalMap(); addDefaultMap(); addSingletonMap(); } private void addGlobalMap() { sGlobalMap.put(com.github.snowdream.toybricks.app.IText.class,com.github.snowdream.toybricks.app.impl.NewTextGobalImpl.class); } private void addDefaultMap() { sDefaultMap.put(com.github.snowdream.toybricks.app.IText.class,com.github.snowdream.toybricks.app.impl.TextImpl.class); } private void addSingletonMap() { sSingletonMap.put(com.github.snowdream.toybricks.app.impl.NewTextGobalImpl.class,null); } @Override public <T> T getImplementation(Class<T> clazz) { T implementation = null; boolean isSingleton = false; Class implClass; implClass = sGlobalMap.get(clazz); if (implClass == null) { implClass = sDefaultMap.get(clazz); } if (implClass != null) { isSingleton = sSingletonMap.containsKey(implClass); if (isSingleton) { implementation = (T) sSingletonMap.get(implClass); if (implementation != null) { return implementation; } } try { implementation = (T) implClass.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } if (isSingleton && implementation != null) { sSingletonMap.put(implClass, implementation); } } return implementation; } }
5.再次使用Javac工具编译InterfaceLoaderImpl.java文件。
6.这个文件就类似字典索引,通过这个文件,就可以通过传入接口,来查找对应的实现类。
总结
与APT工具相比,ToyBricks能够将接口和实现之间的关系进行持久化,存储在jar和aar中,并随之发布到Maven仓库,实现接口和实现的彻底分离。
如果您对ToyBricks有什么问题或者建议,欢迎通过后面的联系方式联系我。
参考资料:
- SnowdreamFramework/ToyBricks
- Annotation Processing Tool (apt)
- ANNOTATION PROCESSING 101
- Annotation-Processing-Tool详解
- Java注解处理器
- 什么是高内聚、低耦合?
联系方式
- Email:yanghui1986527#gmail.com
- Github: https://github.com/snowdream
- Blog: http://snowdream.github.io/blog/
- 简书:http://www.jianshu.com/u/748f0f7e6432
- 云栖博客:https://yq.aliyun.com/u/snowdream86
- QQ群: 529327615
- 微信公众号: sn0wdr1am

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Android生命周期
在 Android 中,多数情况下每个程序都是在各自独立的 Linux 进程中运行的。当一个程序或其某些部分被请求时,它的进程就“出生”了;当这个程序没有必要再运行下去且系统需要回收这个进程的内存用于其他程序时,这个 进程就“死亡”了。可以看出,Android 程序的生命周期是由系统控制而非程序自身直接控制。这和我们编写桌面应用程序时的思维有一些不同,一个桌面应用程序的进程也是在其他进程或用户请求时被创 建,但是往往是在程序自身收到关闭请求后执行一个特定的动作(比如从 main 函数中 return)而导致进程结束的。要想做好某种类型的程序或者某种平台下的程序的开发,最关键的就是要弄清楚这种类型的程序或整个平台下的程序的一般工作 模式并熟记在心。在 Android 中,程序的生命周期控制就是属于这个范畴——我的个人理解:) 在 Android 系统中,当某个 activity调用 startActivity(myIntent) 时,系统会在所有已经安装的程序中寻找其 intent filter 和 myIntent 最匹配的一个 activity,启动这个进程,并把这个 intent ...
- 下一篇
安卓之页面跳转与传值和按钮事件
一:新建页面 即新建Activity,new-other-Android Activity,next, 新建Activity的时候, 1:eclipse会自动创建Layout,我们发现Layout目录下会多了对应的xml文件; 2:ec会自动在AndroidManifest.xml中创建对应的activity节点; 需要注意的是,这些都是ec帮我们自动创建的,我们完全可以手动创建 class,然后让它继承自activity,然后指定layout的那个xml,然后手动创建节点完成。 二:点击按钮进行页面跳转 在主页面创建一个button,对应 <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/editText1" android:layout_below="@+id/editText1" android:layout_marginLeft="4...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8编译安装MySQL8.0.19
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Hadoop3单机部署,实现最简伪集群
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8安装Docker,最新的服务器搭配容器使用
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Docker使用Oracle官方镜像安装(12C,18C,19C)