打造Android轻量级框架XSnow的后继之路
2017-07-22 15:41 阅读(306)

前言

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

准备

由于 XSnow 框架是基于 RxJava2 和 Retrofit2 打造的,其中也依赖了如网络基础库 OkHttp 、图片加载库 Glide 、数据库基础库 GreenDao ,所以使用该项目的朋友需要对这几个基础框架有基本的认识,下面将对这几个框架做一个简单的说明。

RxJava

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

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

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

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

Retrofit

Retrofit 简单来说就是一个基于 OkHttp 的 RESTful API 请求工具。RESTful 是一种架构风格,它的特点是资源、统一接口、URI 和无状态,如果想更详细的了解 RESTful 可以看看RESTful 架构风格概述

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

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

Retrofit 进行网络请求的过程:

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

OkHttp

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

OkHttp总体设计图
OkHttp总体设计图


OkHttp 的请求由 OkHttpClient 统一管理,采用的是门面模式,OkHttpClient 拥有子模块的所有配置和参数,并将请求分发到相应的子系统。它由以下几个核心子系统组成:路由、连接协议、拦截器、代理、安全性认证、连接池以及网络适配。主要是通过 Dispatcher 不断从 RequestQueue 中取出请求(Call),根据是否已缓存调用 Cache 或 Network 这两类数据来获取某个接口,再从内存缓存或是服务器取得请求的数据。该引擎有同步和异步请求,同步请求通过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 详解

注:

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

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

该类还提供了根据 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!

切记!

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

Cache(缓存)

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

Event(事件总线)

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

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

该模块为了能将事件总线统一,定义了 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);
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 的特殊性,该方法的实现类不能在框架中搭建,所有数据库操作都可以参考 Demo 中 DbHeDlper 类实现自己的数据库操作管理类,不同的 Dao 实现对应的 getAbstractDao() 方法就行。

Permission(权限管理)

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

PermissionManager.instance().with(this).request(onPermissionCallback, Manifest.permission.CALL_PHONE);

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

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);

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

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

最后

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

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

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