打造Android轻量级框架XSnow的后继之路

前言

因为有使用的朋友提到 XSnow 框架信息量有点大,但愿能有篇文章详细介绍框架中每一个模块的细节,因此本文会围绕该框架将每个模块的相关构思和重要技术点作一个详细的剖析,让使用该框架的朋友对 XSnow 有一个清晰的认识。html

准备

因为 XSnow 框架是基于 RxJava2Retrofit2 打造的,其中也依赖了如网络基础库 OkHttp 、图片加载库 Glide 、数据库基础库 GreenDao ,因此使用该项目的朋友须要对这几个基础框架有基本的认识,下面将对这几个框架作一个简单的说明。java

RxJava

熟悉的朋友可能知道该框架是属于 ReactiveX 编程库的一员,这是一种新的编程思想,通常都把它叫作响应式编程思想,对于使用该思想的优点在这里就不赘述了,感兴趣的朋友能够看看这篇文章 ReactiveX 文档中文翻译git

RxJava 主要的模式就是观察者模式,何为观察者模式,能够简单的理解为:两个对象观察者与被观察者,其中观察者与被观察者创建了订阅关系,若是被观察者发生了变化那么须要及时通知观察者,观察者收到通知后作出相应的处理。描述可能会有点绕,举个通俗易懂的例子吧,好比说:顾客去买蛋糕这样一个场景(把顾客和蛋糕店店员分别看作观察者与被观察者),因为蛋糕是现作须要必定时间才能完成,顾客在购买时通常会先付钱给店员拿到一个付款凭证后离开去忙其余事,这样他们就创建了购买关系(创建订阅关系),店员在作完蛋糕后(被观察者发生变化),店员就会通知顾客来拿(消息通知到观察者),顾客在收到店员通知后前来拿蛋糕(观察者收到通知后作出相应的处理)。到这里,一个完整的观察者模式场景就讲解完毕,但愿能帮助朋友们加深对观察者模式的理解。github

下面继续讲解对 RxJava 的理解,先想象一个这样的场景,有一台万能机器,它能够生产任何东西,它有一个输入端、一个输出端,中间有不少核心组件,好比说转换器、转移器等,它们对外部都是隐藏的,可是外部有一个控制终端,能够输入任何想要的规则,机器就会根据规则,将输入端的东西经过终端制定的规则输送到输出端。RxJava 就是这样一台万能机器,它拥有数据输入端、数据输出端,中间也有不少能够操控数据的规则,好比说各类操做符、线程转换,它们都有一个目的,就是如何让数据源经过必定规则进行输出。算法

以上的讲解只是为了让你对 RxJava 有一个清晰的认识,不牵扯具体的技术细节,若是想详细了解的能够去看看给初学者的 RxJava2.0 教程,这是一个系列,看完这些文章,基本就可使用 RxJava 作一些基础的功能了,这个系列是针对 RxJava2 的,若是想了解 RxJava 初版,想看看两个版本到底发生了什么改变,那么能够看看给 Android 开发者的 RxJava 详解以及 RxJava2 vs RxJava1.数据库

Retrofit

Retrofit 简单来讲就是一个基于 OkHttpRESTful API 请求工具。RESTful 是一种架构风格,它的特色是资源、统一接口、URI 和无状态,若是想更详细的了解 RESTful 能够看看RESTful 架构风格概述编程

Retrofit 在使用时其实就充当了一个适配器(Adapter)的角色,主要是将一个 Java 接口翻译成一个 HTTP 请求对象,而后用 OkHttp 去发送这个请求。其中核心思想就是动态代理机制,什么是动态代理,就是当你要调用某个 Class 的方法前或后,插入你想要执行的代码。通俗来说,就是你要执行某个操做的先后须要增长一些操做,好比查看用户我的信息前须要判断用户是否登陆,用户访问数据库后想清除用户的访问记录等操做。api

Retrofit 的设计很是插件化且轻量级,高内聚且低耦合。
Retrofit 主要定义了 4 个接口:数组

  • Callback<T>:请求数据的返回;
  • Converter<F, T>:对返回数据进行解析,通常用 GSON
  • Call<T>:发送请求,Retrofit 默认的实现是 OkHttpCall<T>,也能够依需自定义 Call<T>
  • CallAdapter<T>:将 Call 对象转换成其余对象,如转换成支持 RxJavaObservable 对象。

Retrofit 进行网络请求的过程:缓存

  • 经过 Retrofit 对象和 Method 对象获取 callAdapterresponseType 以及 responseConverter 三个对象;
  • 经过解析 Method 中的注解以及进行一系列的检查获得中心管理对象 ServiceMethod
  • 经过 ServiceMethod 对象获取实际执行的 Call 对象执行 Http 请求。

以上讲解只是对 Retrofit 的核心功能作了相关的解释,若是想更详细的了解 Retrofit 能够看看拆轮子系列:拆 Retrofit

OkHttp

OkHttp 是一个高效的 HTTP 库,它的整体设计图以下(图片来源:泡在网上的日子):

OkHttp整体设计图
OkHttp整体设计图

OkHttp 的请求由 OkHttpClient 统一管理,采用的是门面模式,OkHttpClient 拥有子模块的全部配置和参数,并将请求分发到相应的子系统。它由如下几个核心子系统组成:路由、链接协议、拦截器、代理、安全性认证、链接池以及网络适配。主要是经过 Dispatcher 不断从 RequestQueue 中取出请求(Call),根据是否已缓存调用 CacheNetwork 这两类数据来获取某个接口,再从内存缓存或是服务器取得请求的数据。该引擎有同步和异步请求,同步请求经过Call.execute()直接返回当前的 Response,而异步请求会把当前的请求(AsyncCallCall.enqueue 添加到请求队列中,并经过回调(Callback)的方式来获取最后结果。

OkHttp 中的 Interceptor(拦截器)方式对于总体的设计提供了很大的帮助,它采用的是责任链模式,它不仅是负责拦截请求进行一些额外的处理(例如增长请求头),实际上它把实际的网络请求、缓存、透明压缩等功能都统一了起来,每个功能都只是一个 Interceptor,它们再链接成一个 Interceptor.Chain,环环相扣,最终圆满完成一次网络请求。

以上讲解只是对 OkHttp 的流程作了相关的解释,若是想更详细的了解 OkHttp 能够看看拆轮子系列:拆 OkHttp

Glide

Glide 是为图片加载而生,一行代码Glide.with(this).load(url).into(imageView);就搞定图片的加载。使用很是简单,若是想详细了解的,这里推荐郭霖的 Glide 源码解析,Android 图片加载框架最全解析,这是一个系列,看完后对于 Glide 基本就能知道怎么用和为啥要这样用了。

GreenDao

GreenDao 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案。若是想更详细的了解 GreenDao 能够看看Android 数据存储之 GreenDao 3.0 详解

注:

  • 以上对于基础库的讲解只是作了简单的介绍,让你对 XSnow 框架利用的相关技术有个直观的感觉,若是想详细了解的,上面在每一个基础库后面都备注了我的认为比较好的文章,能够当作学习该框架的参考。
  • 如下介绍都以包名做为每一个模块的标题,这样也代表模块之间充分解耦,也方便读者对照代码进行理解,分析起来逻辑更清晰。

Http(网络模块,包含网络请求,上传下载)

该模块是 XSnow 框架的核心功能,其核心思想就是将请求分离和基于动态配置,采用门面模式,上层不用关系具体实现细节,只须要简单配置相关的请求信息就能够达到完整的网络请求功能。该模块相对于其余模块代码量比较大,下面将会对该模块下的每个包进行详细拆分讲解,如今咱们首先来看看 ViseHttp 类,该类至关于该模块的门面类,也是网络请求的惟一入口类,全部的请求都是由该类构建,如:

  • BASE:传入自定义请求对象,方便外部根据本身的需求自定义请求;
  • GET:获取 GET 方式请求对象;
  • POST:获取 POST 方式请求对象;
  • HEAD:获取 HEAD 方式请求对象;
  • PUT:获取 PUT 方式请求对象;
  • PATCH:获取 PATCH 方式请求对象;
  • OPTIONS:获取 OPTIONS 方式请求对象;
  • DELETE:获取 DELETE 方式请求对象;
  • UPLOAD:获取上传文件请求对象,支持传入上传回调;
  • DOWNLOAD:获取下载文件请求对象。

该类还提供了根据 tag 中断单个网络请求以及中断全部网络请求功能,也提供了根据 key 删除缓存和清除全部网络缓存功能。必须注意的是,在应用初始化时必须调用该类的初始化方法

ViseHttp.init(this);复制代码

以及相关的网络配置

ViseHttp.CONFIG()
        //配置请求主机地址
        .baseUrl("http://10.8.4.39/")
        //配置全局请求头
        .globalHeaders(new HashMap<String, String>())
        //配置全局请求参数
        .globalParams(new HashMap<String, String>())
        //配置读取超时时间,单位秒
        .readTimeout(30)
        //配置写入超时时间,单位秒
        .writeTimeout(30)
        //配置链接超时时间,单位秒
        .connectTimeout(30)
        //配置请求失败重试次数
        .retryCount(3)
        //配置请求失败重试间隔时间,单位毫秒
        .retryDelayMillis(1000)
        ......;复制代码

这样才能在应用中调用相关的网络请求功能。若是没有初始化,在调用网络请求时该模块会抛出以下异常信息:

Please call ViseHttp.init(this) in Application to initialize!复制代码

切记!

下面来分别讲解该模块下每一个包的功能:

  • api
    该包提供的是请求的 API,目前只有一个类 ApiService,主要提供的是 Retrofit 进行网络请求的请求方法。

  • body
    该包提供的是相关的请求和响应 body,目前只有一个上传进度展现的请求实体类 UploadProgressRequestBody,经过传入 UCallback 来处理上传文件的进度回调。

  • callback
    该包提供的是相关的回调类,目前包含上传回调 UCallback 和请求 API 回调 ACallback

  • config
    该包提供的是配置相关类,目前只有请求全局配置类 HttpGlobalConfig,该类提供了很丰富的配置方法,提供该类的目的是想将配置与请求分离。

  • core
    该包提供的是一些核心功能类,目前包含缓存处理类 ApiCacheCookie 管理类 ApiCookie 以及网络请求订阅管理类 ApiManager。缓存采用磁盘缓存方式,支持定制各类缓存策略,策略将在 strategy 包下进行讲解。Cookie 采用 SharedPreferences 存储,存储对象以十六进制进行保存。

  • exception
    该包提供网络请求相关异常类,目前提供了请求异常统一处理类 ApiException,可用来断定请求失败的缘由。

  • func
    该包提供一些数据转换类,目前提供了由 ResponseBody 对象转 TApiFunc<T> 类以及由 Observable<? extends Throwable>Observable<?>ApiRetryFunc 类。ApiRetryFunc 类主要用来处理网络超时重试机制,可传入重试次数和重试间隔时间,这些均可以在配置类中进行配置。

  • interceptor
    该包提供的是一系列的拦截器类,这个包也算是该模块的核心包,基本大部分功能均可以采用拦截器的方式来提供,目前该包下面包含以下拦截器:
    一、GzipRequestInterceptor:包含Gzip压缩的请求拦截;
    二、HeadersInterceptor:请求头拦截;
    三、HttpLogInterceptor:Http日志打印拦截;
    四、NoCacheInterceptor:无缓存拦截;
    五、OfflineCacheInterceptor:离线缓存拦截;
    六、OnlineCacheInterceptor:在线缓存拦截;
    七、UploadProgressInterceptor:上传文件进度展现拦截。

  • mode
    该包提供的是相关的实体类。

  • request
    该包提供的是全部请求相关的类,全部的网络请求都须要建立一个请求对象,该包提供了一个基础请求类 BaseRequest<R extends BaseRequest>,该类需将 R 写成实际请求类,这样才能获取对应请求类的对象来进行相关的请求信息配置,若是请求配置与全局配置冲突,那么优先请求配置,意思就是局部请求配置会替换掉相同的全局配置。
    请求类中提供了一系列请求头配置、请求参数配置等信息,若是是 POST 请求还提供了上传 JSON 字符串、上传表单等方式,而若是是上传文件则提供了添加文件、添加字节数组和添加流的方式。
    因为带缓存请求和不带缓存请求返回的结果不同,因此须要分开处理,故有 cacheExecuteexecute 的区分。

  • strategy
    该包提供的是缓存相关的策略,包含以下几种策略:
    一、CacheAndRemoteStrategy:先加载缓存数据后加载网络数据;
    二、FirstCacheStrategy:优先加载缓存数据;
    三、FirstRemoteStrategy:优先加载网络数据;
    四、OnlyCacheStrategy:只加载缓存数据;
    五、OnlyRemoteStrategy:只加载网络数据。
    缓存策略采用面向接口编程原则,定义了一个缓存策略接口 ICacheStrategy<T>,并将具体的策略实现交由各个子类。

  • subscriber
    该包提供的是相关的订阅者,目前包含 API 请求的统一订阅者 ApiSubscriber<T>、包含回调的订阅者 ApiCallbackSubscriber<T> 以及包含下载回调的订阅者 DownCallbackSubscriber<T>

Cache(缓存)

该模块提供了几种缓存方式,分别是内存缓存、磁盘缓存以及 SharedPreferences 存储。该模块主要思想是面向接口编程,提供了 ICache 接口,主要包含添加缓存、获取缓存、删除缓存、清除全部缓存以及判断是否包含该缓存这几个能力。下面将对每一个缓存方式作详细的解释说明:

  • 内存缓存:内存缓存采用单例模式管理缓存对象,缓存对象为 LruCache,缓存算法采用 Lru 算法(Least Recently Used 近期最少使用算法),缓存大小为最大内存的八分之一。
  • 磁盘缓存:磁盘缓存 KEY 采用 MD5 加密,可定制缓存时长,没有定制则默认永久存储,缓存对象为 DiskLruCache,缓存算法也是 Lru 算法,缓存位置和缓存大小均可以定制,若是没有定制就会使用默认值,默认的缓存位置为该应用缓存目录下的 disk_cache 目录,优先存储到 SD 卡中,默认的缓存大小为 20M
  • SharedPreferences 存储:SharedPreferences 存储对缓存对象进行 Base64 加密存储,可定制缓存文件名,若是没有定制则使用默认文件名 sp_cache

Event(事件总线)

该模块使用 Rx 思想实现了 RxBus 功能,其 Bus 的设计思想与 EventBus 相似。其主要由如下几个核心类组成:

  • EventBase :事件处理基类,包含粘性事件 Map 以及普通事件 Subject,也定义了获取 Flowable 以及删除粘性事件和删除粘性事件 Map 的方法。
  • EventComposite :事件复合类,就是将须要接收事件的类中全部事件组合到一块儿进行处理,并提供了一个发送粘性事件的方法。
  • EventSubscriber :事件订阅者,提供了订阅事件和分发事件的方法。
  • EventFind :根据注解查找事件接收方法,由此获得 EventSubscriber,最后组合到一块儿获得 EventComposite
  • ThreadMode :线程模型,包含如主线程、IO线程等,经过 getScheduler(ThreadMode thread) 来获取线程调度者。

事件接收采用注解方式类进行管理,事件订阅后依据注解来查找对应的事件接收地。

该模块为了能将事件总线统一,定义了 IBus 接口,提供了以下四个方法:

void register(Object object);

void unregister(Object object);

void post(IEvent event);

void postSticky(IEvent event);复制代码

也提供了 IEvent 接口,全部事件都实现该接口,这样就能够将具体的事件实现类抽离,其实也是面向接口编程。

该模块也提供了插件化思想,上层能够将如 EventBus 的实现类注入该模块,那么事件的处理就会采用 EventBus 实现的策略,但这里有一个问题,因为事件接收采用的是注解方式,而 EventBus 中的注解是不一样的,因此仍是须要把注解事件进行统一替换,耦合性过高,目前没有发现更好的方式,若是哪位朋友有好的去耦合方式,欢迎留言交流!

Loader(图片加载)

该模块针对图片加载作了二次封装,面向接口编程,每一个实现就是一种图片加载策略,默认采用 Glide 图片加载框架,上层也能够依需自定义实现接口 ILoader,好比 Demo 中提供的 Fresco 图片框架的实现类 FrescoLoader,其主要思想就是插件化,外部可注入任何加载策略,这样可达到高内聚低耦合。

接口中提供了以下四种加载图片的方式:

  • 加载网络图片
    void loadNet(ImageView target, String url, Options options);复制代码
  • 加载资源图片
    void loadResource(ImageView target, int resId, Options options);复制代码
  • 加载Assets中的图片
    void loadAssets(ImageView target, String assetName, Options options);复制代码
  • 加载本地图片
    void loadFile(ImageView target, File file, Options options);复制代码

默认的 Glide 框架采用 provided 方式依赖,这样就只是编译时依赖,运行时不依赖,上层若是肯定使用 GlideLoader加载策略,那么还须要本身使用 compile 进行依赖,这样运行时才不会报错,GlideLoader 在初始化时也增长了以下验证机制

try {
    Class.forName("com.bumptech.glide.Glide");
} catch (ClassNotFoundException e) {
    throw new IllegalStateException("Must be dependencies Glide!");
}复制代码

若是没有依赖 Glide 库则会抛出异常。

Database(数据库)

该模块将 GreenDao 做为底层数据库,定义了数据库的操做接口 IDatabase<M, K>,统一由 DBManager<M, K> 抽象类管理,因为每一个实体类对应的 Dao 不同,因此定义了抽象方法 getAbstractDao()。因为 GreenDao 的特殊性,该方法的实现类不能在框架中搭建,全部数据库操做均可以参考 DemoDbHeDlper 类实现本身的数据库操做管理类,不一样的 Dao 实现对应的 getAbstractDao() 方法就行。

Permission(权限管理)

该模块利用 Rx 思想统一管理权限的申请,一行代码搞定权限的申请问题。

PermissionManager.instance().with(this).request(onPermissionCallback, Manifest.permission.CALL_PHONE);复制代码

该模块很简洁,就几个类,下面分别对它们进行介绍:

  • OnPermissionCallback:权限申请回调接口,在接口实现类中调用具体的业务逻辑。
  • Permission:权限实体类,包含权限名称、是否授予权限以及是否显示权限申请理由变量。
  • PermissionManager:权限管理类,也是权限申请的惟一入口,采用单例模式,须要经过 with 将当前的 Activity 对象传进去,调用 request 方法就能够进行权限申请,须要传入回调和权限列表,权限列表采用可变数组方式传入,使用示例以下:

    PermissionManager.instance().with(this).request(new OnPermissionCallback() {
      @Override
      public void onRequestAllow(String permissionName) {
          DialogUtil.showTips(mContext, getString(R.string.permission_control),
                  getString(R.string.permission_allow) + "\n" + permissionName);
      }
    
      @Override
      public void onRequestRefuse(String permissionName) {
          DialogUtil.showTips(mContext, getString(R.string.permission_control),
                  getString(R.string.permission_refuse) + "\n" + permissionName);
      }
    
      @Override
      public void onRequestNoAsk(String permissionName) {
          DialogUtil.showTips(mContext, getString(R.string.permission_control),
                  getString(R.string.permission_noAsk) + "\n" + permissionName);
      }
    }, Manifest.permission.CALL_PHONE);复制代码
  • RxPermissions:权限管理核心类,该类经过传入 Activity 获取一个 Fragment 来进行权限申请回调处理,提供了一系列权限申请的方法,有多个权限单独处理回调和统一处理回调的方式,分别是 requestEachrequest 方式。
  • RxPermissionsFragment:权限申请回调处理 Fragment

UI(UI模块,包含万能适配器、视图切换)

该模块包含万能适配器和试图切换功能。适配器部分采用 ViewHolder 来管理数据的装载和展现,将数据与展现分离,提供了 DataHelper 接口来装载数据,ViewHelper 接口来处理 UI 的展现。其中的 HelperAdapter 提供了适配器的经常使用方法,基本能知足适配器的经常使用需求。
视图切换部分由 StatusLayoutManager 统一管理,经过传入相关配置进行视图展现处理。内部提供了 OnRetryListener 重试监听和 OnStatusViewListener 试图切换监听,并定义了一个自定义视图 StatusLayout 用来展现如下五种视图:

  • CONTENT:内容视图;
  • LOADING:加载视图;
  • EMPTY:空视图;
  • NETWORK_ERROR:网络错误视图;
  • OTHER_ERROR:其余错误视图。

最后

到此,XSnow 框架的全部模块就介绍完毕了,不知各位朋友是否对该框架有了更深刻的了解。

以上描述有些部分可能讲解比较简单,这是由于有些模块自己不是很复杂,因此就没有作过多的讲解,若是想更清晰的了解该框架,最好的办法是直接 down 下源码观察,若是在看源码过程当中有哪里不理解或以为实现有问题而你有更好的方案均可以留言交流!

源码地址:github.com/xiaoyaoyou1…,源码地址中提供了详细的使用文档,里面有版本介绍以及QQ群等信息,若是喜欢该框架不妨点点 star 并分享给身边的朋友,让更多的朋友参与进来,谢谢你们!

相关文章
相关标签/搜索