Writing in the front

It’s been more than a year since my last post on commercial-grade IM (3) — long Connection Stability connection and reconnection — was released, so I apologize in advance. Mainly because work is too busy, business demands too much, can’t concentrate on writing blog. Let’s set a Flag: THE IM series will be finished, and the Github project will be improved. Stay tuned. I’m not going to update any of my IM series articles and projects, but I’m going to bring you Shine, a lightweight and useful repository for web requests, and I hope I can rekindle my passion for blogging and open source projects. Let’s get started.

What is Shine?

Network request library based on Retrofit secondary encapsulation. Android network requests are made easier by unified packaging, high cohesion, low coupling, flexible configuration, and high scalability.

  • version
    • Java Retrofit+RxJava
    • Kotlin Retrofit+Coroutine

What does Shine do?

  • Supported requests
    • GET
    • POST
    • PUT
    • DELETE
  • Support for dynamic BaseUrl
  • Support custom Response Model (different data structures)
  • Support for custom Response Parser
  • Support custom ciphers (request/response data encryption and decryption)
  • Supports custom Content Type
  • Support for asynchronous/synchronous requests
  • Unified IApiService without changing the IApiService when adding an interface
  • Unified exception handling helps you obtain error information when an interface request fails

Why do you do that?

  • Inconsistent Response Model

In daily development, we should often encounter different Response models. For example, the data format returned by server A is code, MSG and data, and the data format returned by server B is: ErrCode, errMsg, and result: The data returned by server C is in the format of Status, message, and data. Even if the interfaces provided by the same server may have different data formats returned by different interfaces, it is extremely difficult for the client to be compatible. In Shine, this problem can be easily solved by customizing Response Model and Response Parser. Meanwhile, global Response Model and Response Parser can be configured to adapt to most scenarios of single server domain name and returned data format.

  • Dynamic BaseUrl

In daily development, it is inevitable to connect to different servers. Shine makes BaseUrl and Retrofit instances correspond one by one through internal encapsulation. The application layer can configure global BaseUrl or a single interface to dynamically transfer BaseUrl, which is flexible and simple to use.

  • Unified IApiService

Typically, the steps for requesting an interface using Retrofit are:

  1. Define IApiService, declare interface;
  2. Call the interface at the Model or Repository layer;
  3. Call the Model implementation’s interface in the Presenter or ViewModel layer.

In Shine, it is abstracted as a universal IApiService, and unified interfaces such as GET (), POST (), PUT (), delete(), syncGet(), syncPost(), syncPut(), and syncDelete() are defined. To achieve a universal IApiService, there is no need to modify the IApiService when the new interface or the old interface changes, reducing the development cost and improving the development efficiency.

  • Flexible request/response ciphers

You can configure the global Cipher or dynamically transfer the Cipher on a single interface to flexibly encrypt and decrypt interface request and response data. For example, the data encryption mode of interface A is AES and that of interface B is RSA.

  • Asynchronous/synchronous request support

Provides asynchronous/synchronous request mode support. The asynchronous request interface is a common way for us to request, but in some cases, the synchronous request approach is required to fulfill certain requirements, such as Ali OSS StsToken fetch.

  • Unified exception handling

Unified exception processing can be achieved by encapsulating RequestException. The caller only needs to construct the corresponding RequestException and pass in error code, error information and other parameters when customizing the Response Model. When the interface request fails with Shine, Use the error information provided by RequestException to handle exceptions for services.

Parameters and API description

  • RequestOptions
The parameter name instructions type The sample The default value note
requestMethod Request way RequestMethod RequestMethod.GET RequestMethod.GET /
baseUrl Server domain name String api.oick.cn/ / /
function Address of the interface String article/list/0/json / /
headers Request header ArrayMap<String, Any> / / /
params Request parameters ArrayMap<String, Any> / / /
contentType Content type String application/json; charset=utf-8 application/json; charset=utf-8 /
  • ShineOptions
The parameter name instructions type The sample The default value note
logEnable Shine Log Switch Boolean true true /
logTag Shine log TAG String Custom Shine /
baseUrl Shine Default server domain name String / / After configuration, the default BaseUrl will be used when an interface has no dynamic BaseUrl set
parserCls Shine Default data parser KClass DefaultParser::class DefaultParser::class When configured, the default Parser is used when an interface is not dynamically configured
  • IRequest
** @author: FreddyChen * @date: 2022/01/07 13:47 * @email: [email protected] */ interface IRequest {/** * asynchronous request * @param options Request parameters * @param type Data type mapping * @param parserCls */ Suspend Fun <T> Request (options: RequestOptions, type: type, parserCls: KClass<out IParser>, cipherCls: KClass<out ICipher>? = null ): T /** * synchronous request * @param options request parameters * @param type data type mapping * @param parserCls data parser * @Param cipherCls data encryption and decryption */ fun <T> syncRequest( options: RequestOptions, type: Type, parserCls: KClass<out IParser>, cipherCls: KClass<out ICipher>? = null ): T }Copy the code
  • ICipher
/** * @see [DefaultCipher] * @author: FreddyChen * @date: 2022/01/13 16:07 * @email: [email protected] */ interface ICipher {/** * encrypt data */ fun encrypt(original: String?): String? /** * decrypts data */ fun Decrypt (original: String?): String? /** * Gets the name of the decrypt field */ fun getParamName(): String}Copy the code
  • IParser
/** * @see [DefaultParser] * @author: FreddyChen * @date: 2022/01/06 17:53 * @email: [email protected] */ interface IParser { fun<T> parse(url: String, data: String, type: Type): T }Copy the code
  • IApiService
* @author: FreddyChen * @date: 2022/01/07 11:08 * @email: [email protected] */ internal interface IApiService {/** * asynchronous GET request * no arguments */ @get suspend fun GET (@url function: String): String /** * asynchronous GET request * parameter */ @get suspend fun GET (@url function: String, @querymap params: ArrayMap<String, Any?>): String /** * Asynchronous POST request * no arguments */ @post suspend fun POST (@URL function: String): String /** * Asynchronous POST request * with parameters */ @post suspend fun POST (@URL function: String, @body: RequestBody): String /** * Asynchronous PUT request * no arguments */ @put suspend fun PUT (@URL function: String): String /** * Asynchronous PUT request * with parameters */ @put suspend fun PUT (@URL function: String, @body Body: RequestBody): @delete suspend fun DELETE (@url function: String): @delete suspend fun DELETE (@url function: String, @querymap params: ArrayMap<String, Any?>): String /** * syncGet request * no arguments */ @get fun syncGet(@url function: String): Call<String> /** * syncGet(@url function: String, @queryMap params: ArrayMap<String, Any?>): Call<String> /** * syncPost request * no arguments */ @post fun syncPost(@url function: String): @post fun syncPost(@url function: String, @body Body: RequestBody): Call<String> /** * syncPut request * no arguments */ @put fun syncPut(@url function: String): Call<String> /** * syncPut request * parameter */ @put fun syncPut(@url function: String, @body Body: RequestBody): Call<String> /** * syncDelete request * no parameter */ @delete fun syncDelete(@url function: String): Call<String> /** * syncDelete request * with parameter */ @delete fun syncDelete(@url function: String, @querymap params: ArrayMap<String, Any?>): Call<String> }Copy the code

use

  1. Add the dependent
  • Java implementation "io.github.freddychen:shine-java:$lastest_version"
  • Kotlin implementation "io.github.freddychen:shine-kotlin:$lastest_version"

Note: The latest version is available atmaven central shineFound.

  1. Initialize the

Shine is initialized before use. It is recommended to place it in Application#onCreate().

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

Of course, the initialization is not mandatory, ShineOptions will have a default value, the default value can refer to parameters and API description #ShineOptions

  1. use
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 ) }Copy the code

Of course, we can use the Kotlin feature to encapsulate a common request method, which you can choose according to your business situation. Here is an example:

Request (requestMethod: requestMethod, baseUrl: 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) }Copy the code

In this case, the above request can be simplified as:

suspend fun fetchCatList(): ArrayList<Cat> {
    return request(
        requestMethod = RequestMethod.GET,
        baseUrl = "https://cat-fact.herokuapp.com/",
        function = "facts/random?amount=2&animal_type=cat",
    )
}
Copy the code
  1. The sample
  • Get the history list data
Server domain name Address of the interface parameter Return data structure note
api.oick.cn/ lishi/api.php / Code, day, result /

Ex. :

{" code ":" 1 ", "day" : "01/17", "result" : [{" date ":" January 17, 395 ", "title" : "the Roman empire split into western Roman empire and the eastern Roman empire"}]}Copy the code

Call method:

suspend fun fetchHistoryList(): ArrayList<History> {
    return request(
        requestMethod = RequestMethod.POST,
        function = "lishi/api.php",
    )
}
Copy the code
  • Get news list data
Server domain name Address of the interface parameter Return data structure note
is.snssdk.com/ api/news/feed/v51/ / The message, the data /

Ex. :

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

Call method:

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

Copy the code

Note: If there is a business requirement to use synchronous request, simply change the request() method to syncRequest().

Version of the record

The version number Modify the time Release notes
0.0.7 2022.01.16 For the first time to submit

Free open Api

Provide two free development Api platform for everyone, easy to test:

  • Honghua/free API interface
  • public-apis

Write in the last

Finally finished, network request is basically the component that every Android application must use, Shine is accumulated in daily work, also can be a kind of summary, I hope to help you. Due to the limited level, maybe Shine is not the best way to package, open source this project, aiming to play a role of casting a brick to attract jade, welcome everyone Star and Fork, let’s make a joint contribution to the development of Android.

Making address:

  • Java version
  • Kotlin version