作者:FreddyChen

写在前面

距离上一篇文章跟我一起开发商业级IM(3)—— 长连接稳定性之连接及重连发布的时间,大概已有一年多,先跟大家说声抱歉。主要是因为工作太忙,业务需求过多,没办法专心写博客。先立个Flag:IM系列文章一定会坚持写完,同时Github项目也会逐步完善,敬请期待。
这次就暂不更新IM系列相关的文章及项目了,先给大家带来一个稍微轻量级同时也比较实用的网络请求封装库:Shine,同时也希望自己借此机会重新拾起写博客和开源项目的激情,废话少说,我们直接开始吧。

Shine是什么?

基于Retrofit二次封装的网络请求库。通过统一封装、高内聚、低耦合、灵活配置、高度扩展等特性使Android网络请求更简单。

  • 版本

    • Java Retrofit+RxJava
    • Kotlin Retrofit+Coroutine

Shine能做什么?

  • 支持的请求

    • GET
    • POST
    • PUT
    • DELETE
  • 支持动态BaseUrl
  • 支持自定义Response Model(不同响应数据结构)
  • 支持自定义Response Parser(响应数据解析器)
  • 支持自定义Cipher(请求/响应数据加解密)
  • 支持自定义Content Type
  • 支持异步/同步请求
  • 统一的IApiService,新增接口时无需改动IApiService
  • 统一的异常处理,方便在接口请求失败时获取相关错误信息

为什么这样做?

  • 不统一的Response Model

日常开发中,大家应该会经常遇到Response Model不统一的情况,例如服务端A返回的数据格式为:codemsgdata,服务端B返回的数据格式为:errCodeerrMsgresult,服务端C返回的数据格式为statusmessagedata等,甚至即使是同一个服务端提供的接口,也可能存在不同接口返回不同数据格式,客户端兼容起来异常困难。在Shine中,通过自定义Response ModelResponse Parser即可轻松解决此问题,同时支持配置全局Response ModelResponse Parser,适应大多数单个服务器域名及返回数据格式的场景。

  • 不同的BaseUrl

日常开发中,难免需要对接不同的服务器。Shine通过内部封装,使BaseUrlRetrofit实例一一对应,应用层可配置全局BaseUrl或单个接口动态传递BaseUrl,使用灵活简单。

  • 统一的IApiService

通常情况下,使用Retrofit请求接口的步骤为:

  1. 定义IApiService,声明接口;
  2. ModelRepository层调用接口;
  3. PresenterViewModel层调用Model实现的接口。

Shine中,抽象为通用的IApiService,通过定义统一的get()/post()/put()/delete()/syncGet()/syncPost()/syncPut()/syncDelete()等接口,实现通用的IApiService,在新增接口或旧接口发生变动时,无需修改IApiService,降低开发成本并提升开发效率。

  • 灵活的请求/响应Cipher(数据加解密器)

可配置全局Cipher或单个接口动态传递Cipher,灵活实现接口请求及响应数据加解密功能。例如接口A数据加密方式为AES,接口B数据加密方式为RSA等。

  • 异步/同步请求支持

提供异步/同步请求方式支持。异步请求接口是我们平时请求的常用方式,但某些情况下,需要同步请求方式以实现某些需求,例如Ali OSS StsToken获取等。

  • 统一的异常处理

通过封装RequestException实现统一异常处理,调用方仅需在自定义Response Model时构造对应的RequestException并传入错误码错误信息等参数,使用Shine在接口请求失败时,通过RequestException提供的错误信息对业务做异常处理即可。

设计、封装思路及原理

  • 项目结构 com.freddy.shine.kotlin

    • cipher(数据加解密器相关)
    • config(配置相关)
    • exception(异常相关)
    • interf(抽象接口相关)
    • model(Response Model相关)
    • parser(数据解析器相关)
    • retrofit(Retrofit相关)
    • utils(工具类相关)
    • AbstractRequestManager.kt(RequestManager抽象类,自定义RequestManager需继承此类)
    • RequestManagerFactory.kt(RequestManager工厂,提供获取RequestManager方法,应用层直接调用[getRequestManager]即可,无需关心内部实现逻辑)
    • ShineKit.kt(Shine核心类)
  • 设计及封装
    Shine内部封装请求逻辑,同时提供以下方案使Shine更易用、更具扩展性:

    • 暴露ICipher接口使调用方灵活自定义相关数据加解密器实现,并可配置全局/单个接口请求使用;
    • 暴露IParser接口使调用方灵活自定义相关数据解析器实现,并可配置全局/单个接口请求使用;
    • 抽象统一的IApiService,支持异步/同步请求,并统一请求方式使Shine支持各项目使用;
    • 内部多Retrofit实例管理使Shine支持动态BaseUrl
    • 通过构建者模式使Shine请求调用参数传递更灵活等。
  • 原理

    • Retrofit多实例管理:采用Map保存多个Retrofit实例,key: BaseUrl, value: Retrofit Instance。当然有些同学可能觉得多个Retrofit会造成性能浪费、不好管理之类的,这个就见仁见智了。我觉得在一个项目中BaseUrl并不会过多,并且如果是统一的OkHttpClient的话,多个Retrofit实例并不会造成多大的性能浪费,并且多个Retrofit反而会更灵活。当然,后续我会增加移除Retrofit实例的接口,大家如果觉得在某个时刻(大概率不再请求该BaseUrl)可以适当移除该Retrofit实例的话直接移除即可,即使会重新请求,那也就是重新创建一个Retrofit实例而已(详见RetrofitManager.kt)。
    • 动态请求头:通过自定义OkHttp Interceptor获取请求Url实现Request Headers传递(详见OkHttpRequestHeaderInterceptor.kt)。
    • 自定义数据加解密器:通过自定义OkHttp Interceptor同时暴露ICipher接口使调用方灵活自定义请求/响应数据加解密器(详见OkHttpRequestEncryptInterceptor.ktOkHttpResponseDecryptInterceptor.ktDefaultCipher.kt)。
    • 自定义数据解析器:通过反射获取Parser实例,获取到Parser实例后会保存到Map方便下一次获取。同时暴露IParser接口使调用方灵活自定义数据解析器(详见AbstractRequestManager.ktDefaultParser.kt)。
  • Java泛型擦除问题
    大家应该有遇到过,在Java中无法传递ArrayList.class。在Kotlin中,可以通过inlinereified关键字获取泛型T class,但在Java中会存在泛型擦除的问题(关于Java泛型擦除大家可自行了解,在此不再展开),为了解决此问题,通过自定义ParameterizedTypeImpl实现ParameterizedType接口即可(详见TypeUtil.java及Demo中BaseRepository.java调用)。

参数及API说明

  • RequestOptions
参数名称 说明 类型 示例 默认值 备注
requestMethod 请求方式 RequestMethod RequestMethod.GET RequestMethod.GET /
baseUrl 服务器域名 String api.oick.cn/ / /
function 接口地址 String article/list/0/json / /
headers 请求头 ArrayMap<String, Any> / / /
params 请求参数 ArrayMap<String, Any> / / /
contentType 内容类型 String application/json; charset=utf-8 application/json; charset=utf-8 /
  • ShineOptions
参数名称 说明 类型 示例 默认值 备注
logEnable Shine日志开关 Boolean true true /
logTag Shine日志TAG String Custom Shine /
baseUrl Shine默认服务器域名 String / / 配置后,当某个接口没有动态设置BaseUrl时,将会用此默认BaseUrl
parserCls Shine默认数据解析器 KClass DefaultParser::class DefaultParser::class 配置后,当某个接口没有动态设置Parser时,将会用此默认Parser
  • IRequest
/*** 抽象的接口请求封装,自定义RequestManager实现此接口即可** @author: FreddyChen* @date  : 2022/01/07 13:47* @email : freddychencsc@gmail.com*/
interface IRequest {/*** 异步请求* @param options   请求参数* @param type      数据类型映射* @param parserCls 数据解析器* @param cipherCls 数据加解密器*/suspend fun <T> request(options: RequestOptions,type: Type,parserCls: KClass<out IParser>,cipherCls: KClass<out ICipher>? = null): T/*** 同步请求* @param options   请求参数* @param type      数据类型映射* @param parserCls 数据解析器* @param cipherCls 数据加解密器*/fun <T> syncRequest(options: RequestOptions,type: Type,parserCls: KClass<out IParser>,cipherCls: KClass<out ICipher>? = null): T
}
  • ICipher
/*** 加解密器抽象接口** @see [DefaultCipher]* @author: FreddyChen* @date  : 2022/01/13 16:07* @email : freddychencsc@gmail.com*/
interface ICipher {/*** 加密数据*/fun encrypt(original: String?): String?/*** 解密数据*/fun decrypt(original: String?): String?/*** 获取加解密字段名称*/fun getParamName(): String
}
  • IParser
/*** 数据解析器抽象接口** @see [DefaultParser]* @author: FreddyChen* @date  : 2022/01/06 17:53* @email : freddychencsc@gmail.com*/
interface IParser {fun<T> parse(url: String, data: String, type: Type): T
}
  • IApiService
/*** 统一的请求方式* @author: FreddyChen* @date  : 2022/01/07 11:08* @email : freddychencsc@gmail.com*/
internal interface IApiService {/*** 异步GET请求* 无参*/@GETsuspend fun get(@Url function: String): String/*** 异步GET请求* 带参*/@GETsuspend fun get(@Url function: String, @QueryMap params: ArrayMap<String, Any?>): String/*** 异步POST请求* 无参*/@POSTsuspend fun post(@Url function: String): String/*** 异步POST请求* 带参*/@POSTsuspend fun post(@Url function: String, @Body body: RequestBody): String/*** 异步PUT请求* 无参*/@PUTsuspend fun put(@Url function: String): String/*** 异步PUT请求* 带参*/@PUTsuspend fun put(@Url function: String, @Body body: RequestBody): String/*** 异步DELETE请求* 无参*/@DELETEsuspend fun delete(@Url function: String): String/*** 异步DELETE请求* 带参*/@DELETEsuspend fun delete(@Url function: String, @QueryMap params: ArrayMap<String, Any?>): String/*** 同步GET请求* 无参*/@GETfun syncGet(@Url function: String): Call<String>/*** 同步GET请求* 带参*/@GETfun syncGet(@Url function: String, @QueryMap params: ArrayMap<String, Any?>): Call<String>/*** 同步POST请求* 无参*/@POSTfun syncPost(@Url function: String): Call<String>/*** 同步POST请求* 带参*/@POSTfun syncPost(@Url function: String, @Body body: RequestBody): Call<String>/*** 同步PUT请求* 无参*/@PUTfun syncPut(@Url function: String): Call<String>/*** 同步PUT请求* 带参*/@PUTfun syncPut(@Url function: String, @Body body: RequestBody): Call<String>/*** 同步DELETE请求* 无参*/@DELETEfun syncDelete(@Url function: String): Call<String>/*** 同步DELETE请求* 带参*/@DELETEfun syncDelete(@Url function: String, @QueryMap params: ArrayMap<String, Any?>): Call<String>
}

使用方式

  1. 添加依赖
  • Java implementation "io.github.freddychen:shine-java:$lastest_version"
  • Kotlin implementation "io.github.freddychen:shine-kotlin:$lastest_version"

Note:最新版本可在maven central shine中找到。

  1. 初始化

使用Shine前进行初始化,建议放到Application#onCreate()

val options = ShineOptions.Builder().setLogEnable(true).setLogTag("FreddyChen").setBaseUrl("https://api.oick.cn/").setParserCls(CustomParser1::class).build()
ShineKit.init(options)

当然,初始化不是强制的,ShineOptions会有对应的默认值,默认值可参考参数及API说明#ShineOptions

  1. 使用
suspend fun fetchCatList(): ArrayList<Cat> {val options = RequestOptions.Builder().setRequestMethod(RequestMethod.GET).setBaseUrl("https://cat-fact.herokuapp.com/").setFunction("facts/random?amount=2&animal_type=cat").build()val type = object : TypeToken<ArrayList<Cat>>() {}.typereturn ShineKit.getRequestManager().request(options = options,type = type,parserCls = CustomParser1::class)
}

当然,TypeParser参数传递我们可以利用Kotlin特性封装一个通用的请求方法,这些大家根据自己的业务情况来选择就好,下面提供一个示例:

/*** 异步请求*/
suspend inline fun <reified T> request(requestMethod: RequestMethod,baseUrl: String = "https://api.oick.cn/",function: String,headers: ArrayMap<String, Any?>? = null,params: ArrayMap<String, Any?>? = null,contentType: String = NetworkConfig.DEFAULT_CONTENT_TYPE,parserCls: KClass<out IParser> = CustomParser1::class,cipherCls: KClass<out ICipher>? = null): T {val optionsBuilder = RequestOptions.Builder().setRequestMethod(requestMethod).setBaseUrl(baseUrl).setFunction(function).setContentType(contentType)if (!headers.isNullOrEmpty()) {optionsBuilder.setHeaders(headers)}if (!params.isNullOrEmpty()) {optionsBuilder.setParams(params)}return ShineKit.getRequestManager().request(optionsBuilder.build(), object : TypeToken<T>() {}.type, parserCls, cipherCls)}

这样的话,上面的请求可以简化为:

suspend fun fetchCatList(): ArrayList<Cat> {return request(requestMethod = RequestMethod.GET,baseUrl = "https://cat-fact.herokuapp.com/",function = "facts/random?amount=2&animal_type=cat",)
}
  1. 示例
  • 获取历史列表数据
服务器域名 接口地址 参数 返回数据结构 备注
api.oick.cn/ lishi/api.php / code、day、result /

例:

{"code":"1","day":"01/ 17","result":[{"date":"395年01月17日","title":"罗马帝国分裂为西罗马帝国和东罗马帝国"}]
}

调用方式:

suspend fun fetchHistoryList(): ArrayList<History> {return request(requestMethod = RequestMethod.POST,function = "lishi/api.php",)
}
  • 获取新闻列表数据
服务器域名 接口地址 参数 返回数据结构 备注
is.snssdk.com/ api/news/feed/v51/ / message、data /

例:

{"message":"success","data":[{"content":"test"}]
}

调用方式:

suspend fun fetchJournalismList(): ArrayList<Journalism> {return request(requestMethod = RequestMethod.GET,baseUrl = "https://is.snssdk.com/",function = "api/news/feed/v51/",parserCls = CustomParser2::class,)
}

Note:如有业务需求使用同步请求方式,只需要把request()方法改成syncRequest()方法即可

版本记录

版本号 修改时间 版本说明
0.0.7 2022.01.16 首次提交
0.0.8 2022.02.15 修改minSdkVersion为19

免费开放的Api

提供两个免费开放Api平台给大家,方便测试:

  • 红花会 / 免费的api接口
  • public-apis

写在最后

终于写完了,网络请求基本是每个Android应用必须用到的组件,Shine为平时工作中的积累,也算是一种总结,希望对大家有所帮助。由于水平有限,也许Shine并不是最好的封装方式,开源这个项目,旨在起到抛砖引玉的作用,欢迎大家star和fork,让我们为Android开发共同贡献一份力量。

Shine——更简单的Android网络请求库封装相关推荐

  1. Android 网络请求库Retrofit简单使用

    载请标明出处: http://blog.csdn.net/u011974987/article/details/50895633: 什么是 Retrofit ? Retrofit 是一套 RESTfu ...

  2. android网络请求库volley方法详解

    使用volley进行网络请求:需先将volley包导入androidstudio中 File下的Project Structrue,点加号导包 volley网络请求步骤: 1. 创建请求队列     ...

  3. android网络请求框架汇总

    网络 使用网络库不要忘记添加网络权限 2.1网络_Volley · 简介: Volley的中文翻译为"齐射.并发",是在2013年的Google大会上发布的一款Android平台网 ...

  4. android post请求添加公共参数_XHttp2 一个功能强悍的网络请求库

    XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装.还不赶紧点击使用说明文档,体验一下吧! 项目地址 关于我 https://github ...

  5. Android -- 网络请求

    一. HttpURLConnection 二. HttpClient 三.Volley 四.OkHttp 五. Retrofit ----------------------------------- ...

  6. 流行框架(二)网络请求库 OKhttp

    文章目录 概述 HttpURLConnection GET和POST获取文本数据 GET POST OKHttp 基本使用 依赖与权限 发起一个get请求 重要概念 OkHttpClient Requ ...

  7. 「Python 编程」编码实现网络请求库中的 URL 解析器

    相信各位 Python 开发者都用过 Requests 库,有些朋友还用过 WebSockets 库.这里回顾一下它们的基本用法,例如使用 Requests 库向目标网站发出 GET 请求: impo ...

  8. Android网络请求框架之Retrofit(二)

    前面一篇文章介绍了Retrofit的基本用法,没有看过的童鞋可以移步:Android网络请求框架之Retrofit(一),现在我们来继续介绍Retrofit配合RxJava.RxAndroid的用法. ...

  9. Python网络请求库Requests,妈妈再也不会担心我的网络请求了(一)

    本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 极客导航 即可关注,每个工作日都有文章更新. 一.概况 网络请求可能是每门语言比较重要的一部分了,在Python语言中,虽然有urll ...

  10. python的网络请求库urllib、urllib2、urllib3、request的联系

    文章目录 1. 简介 2. urllib 3. urllib2 4. urllib3 5. requests 6. 相关文章 1. 简介 urllib.urllib2.urllib3.request均 ...

最新文章

  1. powershell获取linux文件,powershell如何读取文件名并赋值到变量?
  2. Spring学习笔记八--Bean生命周期和后置处理器
  3. spring + shiro + cas 实现sso单点登录
  4. Linkis计算中间件部署过程记录
  5. 读书笔记 Effective C++: 02 构造析构赋值运算
  6. C# 实验三 判断一个字符、判断三角形、千名学生、a+aa+aaa+aaaa、求数列相加、约瑟夫环
  7. ASP.NET Core快速入门(第5章:认证与授权)--学习笔记
  8. java 实现中文排序,Java自定义比较器实现中文排序
  9. uml类图例子_五分钟带你读懂UML类图
  10. linux+vi+注掉代码,VI编辑器之删除操作(示例代码)
  11. 在GPU上部署Bert模型
  12. ajax:前后端json传值写法
  13. 万兆网文件服务器,万兆以太网网卡网吧服务器中的应用
  14. intellij idea 插件 开发 新加的mainmenu不显示
  15. spring jpa Specification in 查询
  16. 图片太大如何压缩?学会这个方法轻松压缩
  17. getline()与cin.getline()函数用法详解
  18. OA电子表单设计-年假申请单-数据验证
  19. 计蒜客python答案Top50
  20. No.4-VulnHub-Tr0ll:1-Walkthrough渗透学习

热门文章

  1. 开源 免费 java CMS - FreeCMS1.5-职位管理
  2. PHP实现队列之双向队列
  3. 使用计算机键盘的基本步骤,用键盘怎样关机(win7电脑键盘关机的操作方法)...
  4. MPB:湖南师大尹佳组-抑菌圈和药敏实验研究益生菌拮抗病原菌和抗生素敏感性的方法...
  5. idea运行web项目光标乱跳
  6. 【正则表达式】网页上敏感词过滤背后的原理你知道吗?
  7. 【四二学堂】基于uni-app开发的跨平台井字游戏(App+H5 web+微信小程序)
  8. 经纬度转换器_FME应用小实例:线面经纬度集合快速转几何图形
  9. 山东大学软件学院操作系统实验的准备
  10. qlv视频怎么转换成mp4格式工厂?如何用格式工厂将qlv格式转换成mp4格式?