写在前面

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

Shine是什么?

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

  • 版本测试抑郁程度的问卷
    • Java
      Ret接口测试rofit+RxJava
    • Kotlin
      Retrofit+Coroutine

Shine能做什么?

  • 支持的请求
    • GET
    • POST
    • PUT
    • D接口英文ELETE
  • 支持动态BaseUrl
  • 支持自定义Response Model(不同数据结构)
  • 支持自定义Response Parser(响应数据解析器)
  • 支持自定义Cipher(请求/响应数据加解密)测试手机是否被监控
  • 支持自定义Content Type
  • 支持异步/同步请求
  • 统一的IApiService,新增接口时无需测试你的自卑程度改动IApiService
  • 统一的异常处理,方便在接口请求失败时获取相关错误信apple苹果官网

为什么这样做?

  • 不统一的Respons服务器操作系统银河麒麟e Model

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

  • 动态BaseUrl

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

  • 统一的IApiService

通常情况下,使用Retrofit请求接java环境变量配置口的步骤为:

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

Shine中,抽象为通用的IApiService,通测试手机是否被监控过定义统一的get()/post()/put()/delete()/syncGet()/syncPost()/syncPut()/syncDelete()等接口测试抑郁症,实现通用的IApiService,在新增接口或旧接口发生变动时,无需修改接口文档Ijava编译器ApiService,降低开发成本并提升开发效率。

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

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

  • 异步/同步请求支持

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

  • 统一的异常处理

通过封装RequestException实现统一异常处理,调用方测试抑郁症的20道题仅需java怎么读在自定义Response Model时构造对应的Reque测试你适合学心理学吗stException并传入错误码错误信息等参数,使用Shine在接口请求java语言失败测试你适合学心理学吗时,通过RequestException服务器是什么提供的错误信息对业务做异常服务器内存条和普通内存条区别处理即可。

设计、封装思路及原理

  • 项目结构
    com.fr接口英文eddy.shine.kotli服务器租用多少钱一年n

    • cipherappearance数据加解密器相关)
    • config(配置相关)
    • exception(异常相关)
    • interf(抽象接口相关)
    • model(Response Model相关)
    • parser(数据解析器相关)
    • reappearancetrofitjava怎么读Retrofit相关)
    • utils(工具类相关)
    • AbstractRequestManager.kt(RequestManager抽象类,自定义RequestManager需继承此类)
    • RequestManagerFactory.k测试t(RequestManager工厂,提供获取RequestManager方法,应用层直接调用[getRequestManager]即可,无需关心内部实现逻辑)
    • ShineKit.kt(Shine核心类)
  • 设计及封装

Shine内部封装请求逻辑,同时提供以下方案使Shine服务器怎么搭建更易用、更具扩展性:

  • 暴露ICipher接口使调用方灵活自定义相关数据加解密器实现,并可配置全局/单个接口请求使用;
  • 暴露IParser接口使调用方灵活自定义测试你的自卑程度相关数据解析器实现,并可配置全局/单个接口请求使用;
  • 抽象统一的IApiService,支持异步/同步请求,并统一请求方式使Shine支持各项目服务器怎么搭建使用;
  • 内部多Retro服务器系统fit实例管理使Shineapp小胖子持动态BaseUrl
  • 通过构建者模式使Sappearancehine请求调用参数传测试纸怀孕图片递更灵活等。
  • appreciate
  • Retrofit多实例管理:采用Map保存多个Retrofit实例,key: BaseUrl, value: Retrofit Instancjava怎么读e。当然有些同学可能觉得多个Retrofit会造成性能浪费、不好管理之类的,这个就见仁见智了。我觉得在一个项目中BaseUrl并不会过多,并且如果是统一的OkHttpClienjava面试题t的话,多个Retrofit实例并不会造成多大的性能浪费,并且多个Retrofit反而会更灵活。当然,后续我会增加移除Retrofit实例的接口,大家如果觉得在某个时刻服务器配置(大概率不再请求该BaseUrl)可以适当移除该Retrofit实例的话直接移除即可,即使会app小胖子重新请求,那也就是重新创建一个Retrofit实例而已(详见RetrofitManager.kt)。
  • 动态请求头:通过自定义OkHttp Interceptor获取请求Url实现Request接口英文 Headers传递(详见OkHttpRequestHeaderInterceptor.kt)。
  • 自定义数据加解密器:通过自定义OkHttp Interceptor同时暴露ICipher接口使调用方灵活自定义请求/响应数测试用例据加解密器(详见OkHttpRequestEncryp接口自动化tInterceptor.ktOkHttpResponseDecryptInterceptor.k测试你适合学心理学吗tDefaultCipher.kt)。
  • 自定义数据解析器:通过反射获取Parser实例,获取到Parser实例后会保存到Map方便下一次获取。同时暴露IParser接口使调用方灵活自定义数据解析器(详见AbstractRequestManager.ktDefaultPjava模拟器arser.kt)。
  • Java泛型擦除问服务器租用多少钱一年:大家应该有遇到过,在Java中无法传递ArrayList.class。在Kotlin中,可以通过inlinereified关键字获取泛型T classapple苹果官网,但在Java中会存在泛型擦除的问题(关接口和抽象类的区别于Java泛型擦除大家可自行了解,在apple苹果官网此不再展开),为了解决此问题,通过自定义ParameterizedT服务器怎么搭建ypeImpl实现ParameterizedType接口即可(详见TypeUtil.java及Demo中BaseRepository.ja测试工程师va调用)。

参数及API说明

  • RequAPPestOptions
参数名称 说明 类型 示例 默认值服务器租用 备注
reque接口是什么stMethod 请求方式 RequestMethodjava模拟器 RequestMethod.GET RequestMethod.GET /
bas测试你适合学心理学吗eUrl 服务器域名 String api.oick.cn/ / /
function 接口地址 String article/list/0/jjava面试题son / /
headers 请求头 ArrayMap<String, Any> / / /
params 请求参数 ArrayMap<String, Any> / / /
conjava语言tentType 内容类型 String application/接口文档j服务器系统son; charset=utf-8 app测试用例lication/json; charset=utf-8 /
  • ShineOpti服务器怎么搭建ons
参数名称 说明 类型 示例 默认值 备注
logEnable Shine日志开关 Boolean true true /
logTag ShAPPine测试你的自卑程度日志TAG String Custom Shine /
baseUrl Shine默认服务器域名 String / / 配置后,当某个接口没有动态设置BaseUrl时,将会用此默认BaseUrl
parserCls Shine默认数据解析器 KClass DefaultParser::class Defa接口的作用ultParser::class 配置后,当某个接口没有动态设置接口类型Par服务器是什么ser时,将会用此默认Parser
  • IRequestjavascript
/**
 * 抽象的接口请求封装,自定义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
}
  • ICappreciateiphjava模拟器er
/**
 * 加解密器抽象接口
 *
 * @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请求
     * 无参
     */
    @GET
    suspend fun get(@Url function: String): String
    /**
     * 异步GET请求
     * 带参
     */
    @GET
    suspend fun get(@Url function: String, @QueryMap params: ArrayMap<String, Any?>): String
    /**
     * 异步POST请求
     * 无参
     */
    @POST
    suspend fun post(@Url function: String): String
    /**
     * 异步POST请求
     * 带参
     */
    @POST
    suspend fun post(@Url function: String, @Body body: RequestBody): String
    /**
     * 异步PUT请求
     * 无参
     */
    @PUT
    suspend fun put(@Url function: String): String
    /**
     * 异步PUT请求
     * 带参
     */
    @PUT
    suspend fun put(@Url function: String, @Body body: RequestBody): String
    /**
     * 异步DELETE请求
     * 无参
     */
    @DELETE
    suspend fun delete(@Url function: String): String
    /**
     * 异步DELETE请求
     * 带参
     */
    @DELETE
    suspend fun delete(@Url function: String, @QueryMap params: ArrayMap<String, Any?>): String
    /**
     * 同步GET请求
     * 无参
     */
    @GET
    fun syncGet(@Url function: String): Call<String>
    /**
     * 同步GET请求
     * 带参
     */
    @GET
    fun syncGet(@Url function: String, @QueryMap params: ArrayMap<String, Any?>): Call<String>
    /**
     * 同步POST请求
     * 无参
     */
    @POST
    fun syncPost(@Url function: String): Call<String>
    /**
     * 同步POST请求
     * 带参
     */
    @POST
    fun syncPost(@Url function: String, @Body body: RequestBody): Call<String>
    /**
     * 同步PUT请求
     * 无参
     */
    @PUT
    fun syncPut(@Url function: String): Call<String>
    /**
     * 同步PUT请求
     * 带参
     */
    @PUT
    fun syncPut(@Url function: String, @Body body: RequestBody): Call<String>
    /**
     * 同步DELETE请求
     * 无参
     */
    @DELETE
    fun syncDelete(@Url function: String): Call<String>
    /**
     * 同步DELETE请求
     * 带参
     */
    @DELETE
    fun syncDelete(@Url function: String, @QueryMap params: ArrayMap<String, Any?>): Call<String>
}

使用方式服务器配置

  1. 添加依赖
  • Java
    implementation "io.github.freddychen:shine-java:$lastest_version"
  • Kotlin
    implementation "io.gith服务器ub.freddychen:shine-kotlin:$服务器租用lastest_version"

Note:最新版本可在mjavaeeaven central shine中找到apple

  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会有对应的默认值,默认值可APP参考参数及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>>() {}.type
    return ShineKit.getRequestManager().request(
      options = options,
      type = type,
      parserCls = CustomParser1::class
    )
}

当然,TypeP测试用例arser参数传递我们可以利用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()方法改成syncReque测试你的自卑程度st()方法即可

版本记录

版本号 修改时间 版本说明
0.0.7 2022.01.16 首次提交

免费开放的Api

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

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

写在最后

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

GitHub地址:

  • Java版本
  • Kotlin版本