The vernacular

  • We definitely needed a unified wrapper around things like POST and GET, so we defined oneA unified interface class for network requestsHttpApi
  • Since post and GET have requests, there must be a callback for success and failure of the request, so we need to define thisIHttpCallbackThis is needed in HttpApi
  • Now that We’ve defined HttpApi, we also need a concrete class named, for example, HttpApiOkHttpApiAnd all future requests,
  • OkHttpApiThe get and POST methods, which are copied inside, need to do something specific. Of course, we can’t avoid building a Client instance, where we can configure many parameters, such as timeout, such as interceptor, such as Cookie

In plain English, that’s what it means.


(This example is not Retrofit yet, a better practice would be Retrofit plus Jetpack, I’ll write one later)


In the code

  • HttpApi
package com.am.kttt

import com.am.kttt.support.IHttpCallback

/** * Unified interface class for network requests */
interface HttpApi {

    /** * Abstract HTTP get request encapsulation, asynchronous */
    fun get(params: Map<String, Any>, urlStr: String, callback: IHttpCallback)

    /** * Abstract HTTP synchronous GET request */
    fun getSync(params: Map<String, Any>, urlStr: String): Any? {
        return Any()
    }

    /** * Abstract HTTP post request asynchronous */
    fun post(body: Any, urlStr: String, callback: IHttpCallback)

    /** * Abstract Http POST synchronization request */
    fun postSync(body: Any, urlStr: String): Any? = Any()


    fun cancelRequest(tag: Any)

    fun cancelAllRequest(a)
}
Copy the code

.

  • OkHttpApi
package com.am.kttt

import androidx.collection.SimpleArrayMap
import com.google.gson.Gson
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import com.am.kttt.config.HeaderInterceptor
import com.am.kttt.config.KtHttpLogInterceptor
import com.am.kttt.config.LocalCookieJar
import com.am.kttt.config.RetryInterceptor
import com.am.kttt.support.IHttpCallback
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit

/** * OkHttpApi core class * implement get and POST operations * build Client, add specific configuration; Interceptors, cookies, etc. */
class  OkHttpApi : HttpApi {

    var maxRetry = 0// Maximum number of retries

    // Store the request for cancellation
    private val callMap = SimpleArrayMap<Any, Call>()

    //okHttpClient
    private val mClient = OkHttpClient.Builder()
        .callTimeout(10, TimeUnit.SECONDS)// Complete request timeout duration, from the time the request is initiated to the time the return data is received, default value 0, unlimited,
        .connectTimeout(10, TimeUnit.SECONDS)// Duration of establishing a connection with the server. The default value is 10 seconds
        .readTimeout(10, TimeUnit.SECONDS)// How long it takes to read the data returned by the server
        .writeTimeout(10, TimeUnit.SECONDS)// Duration of writing data to the server. The default value is 10 seconds
        .retryOnConnectionFailure(true)/ / reconnection
        .followRedirects(false)/ / redirection
        .cache(Cache(File("sdcard/cache"."okhttp"), 1024))
// .cookieJar(CookieJar.NO_COOKIES)
        .cookieJar(LocalCookieJar())
        .addNetworkInterceptor(HeaderInterceptor())// Public header interceptor
        .addNetworkInterceptor(KtHttpLogInterceptor {
            logLevel(KtHttpLogInterceptor.LogLevel.BODY)
        })// Add a network interceptor that intercepts okHttp network requests. Unlike the application interceptor, this interceptor is aware of all network states, such as redirects.
        .addNetworkInterceptor(RetryInterceptor(maxRetry))
// .hostnameVerifier(HostnameVerifier { p0, p1 -> true })
// .sslSocketFactory(sslSocketFactory = null,trustManager = null)
        .build()


    override fun get(params: Map<String, Any>, urlStr: String, callback: IHttpCallback) {
        val urlBuilder = urlStr.toHttpUrl().newBuilder()
        params.forEach { entry ->
            urlBuilder.addEncodedQueryParameter(entry.key, entry.value.toString())
        }

        val request = Request.Builder()
            .get()
            .tag(params)
            .url(urlBuilder.build())
            .cacheControl(CacheControl.FORCE_NETWORK)
            .build()
        val newCall = mClient.newCall(request)
        // Store the request for cancellation
        callMap.put(request.tag(), newCall)
        newCall.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                callback.onFailed(e.message)
            }

            override fun onResponse(call: Call, response: Response){ callback.onSuccess(response.body? .string()) } }) }override fun post(body: Any, urlStr: String, callback: IHttpCallback) {

        val reqBody = Gson().toJson(body).toRequestBody("application/json".toMediaType())

        val request = Request.Builder()
            .post(reqBody)
            .url(urlStr)
            .tag(body)
            .build()

        val newCall = mClient.newCall(request)
        // Store the request for cancellation
        callMap.put(request.tag(), newCall)
        newCall.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                callback.onFailed(e.message)
            }

            override fun onResponse(call: Call, response: Response){ callback.onSuccess(response.body? .string()) } }) }/** * cancel the network request. Tag is the id of each request
    override fun cancelRequest(tag: Any) {
        callMap.get(tag)? .cancel() }/** * Cancel all network requests */
    override fun cancelAllRequest(a) {
        for (i in 0 until callMap.size()) {
            callMap.get(callMap.keyAt(i))? .cancel() } } }Copy the code

.

  • MainActivity
package com.am.kttt

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.am.kttt.databinding.ActivityMainBinding
import com.blankj.utilcode.util.GsonUtils
import com.blankj.utilcode.util.LogUtils
//import kotlinx.android.synthetic.main.activity_main.*
import com.am.kttt.model.NetResponse
import com.am.kttt.support.NetTranUtils
import com.am.kttt.support.IHttpCallback

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    val httpApi: HttpApi = OkHttpApi()

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btGet.setOnClickListener(View.OnClickListener {
            httpApi.get(
                emptyMap(),
                "https://wanandroid.com/wxarticle/chapters/json".object : IHttpCallback {

                    override fun onSuccess(data: Any?). {
                        LogUtils.d("success result : ${data.toString()}")
                        runOnUiThread {

                            binding.tvText.text = data.toString()
                        }
                    }

                    override fun onFailed(error: Any?). {
                        LogUtils.d("failed msg : ${error.toString()}")
                    }

                })
        })

        binding.btPost.setOnClickListener(View.OnClickListener {
            val mapParams = mapOf("username" to "123456" ,"username" to "password" ,"repassword" to "123456" )
            httpApi.post(
                mapParams,
                "https://www.wanandroid.com/user/register".object : IHttpCallback {
                    override fun onSuccess(data: Any?). {
                        LogUtils.d("success result : ${data.toString()}")
                        runOnUiThread {
                            val toString = data.toString()

                            binding.tvText.text = toString

                            /*val (code, dataObj, message) = GsonUtils.fromJson
      
       ( toString, NetResponse::class.java ) if(dataObj! DecodeData (dataobj.toString ()?:"")}else{binding.tvtext.text = "empty data"}*/
      }}override fun onFailed(error: Any?). {
                        LogUtils.d("failed msg : ${error.toString()}")

                        binding.tvText.text = "${error.toString()}"}})})}}Copy the code

.

xml


      
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center_horizontal"
        >

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/bt_get"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="To launch the get"
            android:layout_marginTop="10dp"
            />

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/bt_post"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="By Post"
            android:layout_marginTop="10dp"
            android:layout_marginBottom="10dp"
            />

        <TextView
            android:id="@+id/tv_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />


    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code

.

  • HeaderInterceptor
package com.am.kttt.config

import com.blankj.utilcode.util.*
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import okhttp3.CacheControl
import okhttp3.FormBody
import okhttp3.Interceptor
import okhttp3.Response
import okio.Buffer


/** * Project specific, add public header interceptor */
class HeaderInterceptor : Interceptor {

    companion object {
        private val gson = GsonBuilder()
            .enableComplexMapKeySerialization()
            .create()
        private val mapType = object : TypeToken<Map<String, Any>>() {}.type
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        val originRequest = chain.request()

        // Additional public headers, encapsulation clientInfo,deviceInfo, etc. You can also customize the headers field in a POST request
        // Note that only the following fields can be used by the server for verification. The fields can be missing, but cannot be added because the server has not processed them
        val attachHeaders = mutableListOf<Pair<String, String>>(
            "appid" to NET_CONFIG_APPID,
            "platform" to "android".The yAPI platform tag does not
            "timestamp" to System.currentTimeMillis().toString(),

            "brand" to DeviceUtils.getManufacturer(),
            "model" to DeviceUtils.getModel(),
            "uuid" to DeviceUtils.getUniqueDeviceId(),
            "network" to NetworkUtils.getNetworkType().name,
            "system" to DeviceUtils.getSDKVersionName(),


            "version" to AppUtils.getAppVersionName()

        )
        // Tokens are passed only if they have a value,
        val tokenstr = ""
        val localToken = SPStaticUtils.getString(SP_KEY_USER_TOKEN, tokenstr)
        if (localToken.isNotEmpty()) {
            attachHeaders.add("token" to localToken)
        }
        val signHeaders = mutableListOf<Pair<String, String>>()
        signHeaders.addAll(attachHeaders)
        // Get request parameters
        if (originRequest.method == "GET") { originRequest.url.queryParameterNames.forEach { key -> signHeaders.add(key to (originRequest.url.queryParameter(key) ? :""))}}// Post requests in formBody form, or json form, need to iterate through the internal fields to participate in the calculation of sign
        val requestBody = originRequest.body
        if (originRequest.method == "POST") {
            //formBody
            if (requestBody is FormBody) {
                for (i in 0 until requestBody.size) {
                    signHeaders.add(requestBody.name(i) to requestBody.value(i))
                }
            }
            // Json body needs to deserialize the requestBody into JSON to map Application /json
            if(requestBody? .contentType()? .type =="application"&& requestBody.contentType()? .subtype =="json") {
                kotlin.runCatching {
                    val buffer = Buffer()
                    requestBody.writeTo(buffer)
                    buffer.readByteString().utf8()
                }.onSuccess {
                    val map = gson.fromJson<Map<String, Any>>(it, mapType)
                    map.forEach { entry ->
                        // FIXME:2020/8/25 value Current JSON layer level
                        signHeaders.add(entry.key to entry.value.toString())
                    }
                }
            }
        }

        // Todo algorithm: all must be non-null arguments!! Sign = MD5 (headers and params key=value &, appkey =value)
        val signValue = signHeaders
            .sortedBy { it.first }
            .joinToString("&") { "${it.first}=${it.second}" }
            .plus("&appkey=$NET_CONFIG_APPKEY")

        val newBuilder = originRequest.newBuilder()
            .cacheControl(CacheControl.FORCE_NETWORK)
        attachHeaders.forEach { newBuilder.header(it.first, it.second) }
        newBuilder.header("sign", EncryptUtils.encryptMD5ToString(signValue))

        if (originRequest.method == "POST"&& requestBody ! =null) {
            newBuilder.post(requestBody)
        } else if (originRequest.method == "GET") {
            newBuilder.get()}return chain.proceed(newBuilder.build())
    }

}
Copy the code

.

  • KtHttpLogInterceptor
package com.am.kttt.config

import android.util.Log
import okhttp3.*
import okio.Buffer
import com.am.kttt.support.NetTranUtils
import java.net.URLDecoder
import java.text.SimpleDateFormat
import java.util.*

/** * An interceptor for logging okHttp web logs */
class KtHttpLogInterceptor(block: (KtHttpLogInterceptor.() -> Unit)? = null) : Interceptor {

    private var logLevel: LogLevel = LogLevel.NONE// Prints the date mark
    private var colorLevel: ColorLevel = ColorLevel.DEBUG// The default is debug logcat
    private var logTag = TAG// The Logcat Tag of the log

    init{ block? .invoke(this)}/** * Set LogLevel */
    fun logLevel(level: LogLevel): KtHttpLogInterceptor {
        logLevel = level
        return this
    }

    /** * Set colorLevel */
    fun colorLevel(level: ColorLevel): KtHttpLogInterceptor {
        colorLevel = level
        return this
    }

    /** * sets the Log Tag */
    fun logTag(tag: String): KtHttpLogInterceptor {
        logTag = tag
        return this
    }


    override fun intercept(chain: Interceptor.Chain): Response {
        / / request
        val request = chain.request()
        / / response
        return kotlin.runCatching { chain.proceed(request) }
            .onFailure {
                it.printStackTrace()
                logIt(
                    it.message.toString(),
                    ColorLevel.ERROR
                )
            }.onSuccess { response ->
                if (logLevel == LogLevel.NONE) {
                    return response
                }
                // Log the request
                logRequest(request, chain.connection())
                // Record the response log
                logResponse(response)
            }.getOrThrow()
    }

    /** * log the request */
    private fun logRequest(request: Request, connection: Connection?). {
        val sb = StringBuilder()
        sb.appendln("\r\n")
        sb.appendln("- > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - >")
        when (logLevel) {
            LogLevel.NONE -> {
                /*do nothing*/
            }
            LogLevel.BASIC -> {
                logBasicReq(sb, request, connection)
            }
            LogLevel.HEADERS -> {
                logHeadersReq(sb, request, connection)
            }
            LogLevel.BODY -> {
                logBodyReq(sb, request, connection)
            }
        }
        sb.appendln("- > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - > - >")
        logIt(sb)
    }

    //region log request

    private fun logBodyReq(
        sb: StringBuilder,
        request: Request,
        connection: Connection?). {
        logHeadersReq(sb, request, connection)
        // Read the contents of the request Body
        val req = request.newBuilder().build()
        valsink = Buffer() req.body? .writeTo(sink) sb.appendln("RequestBody: ${sink.readUtf8()}")}private fun logHeadersReq(
        sb: StringBuilder,
        request: Request,
        connection: Connection?). {
        logBasicReq(sb, request, connection)
        val headersStr = request.headers.joinToString("") { header ->
            "Request Header: {${header.first}=${header.second}}\n"
        }
        sb.appendln(headersStr)
    }

    private fun logBasicReq(
        sb: StringBuilder,
        request: Request,
        connection: Connection?). {
        sb.appendln("The request method:${request.method} url: ${decodeUrlStr(request.url.toString())} tag: ${request.tag()} protocol: ${connection? .protocol() ? : Protocol.HTTP_1_1}")}//endregion

    /** * Record the response log * [response] Response data */
    private fun logResponse(response: Response) {
        val sb = StringBuffer()
        sb.appendln("\r\n")
        sb.appendln("< < < < < < - < < - < < < < < < - < < - < < < < < < - < < - < < < < - < < < < < < - < < - < < < < < < - < < - < < - < < < < - < < < < - < <")
        when (logLevel) {
            LogLevel.NONE -> {
                /*do nothing*/
            }
            LogLevel.BASIC -> {
                logBasicRsp(sb, response)
            }
            LogLevel.HEADERS -> {
                logHeadersRsp(response, sb)
            }
            LogLevel.BODY -> {
                logHeadersRsp(response, sb)
                //body.string throws AN I/O exception
                kotlin.runCatching {
                    // Peek is similar to clone stream data, monitoring, snooping, can not directly use the original body stream data as a log, will consume IO, so this is peek, monitoring
                    val peekBody = response.peekBody(1024 * 1024)
                    sb.appendln(NetTranUtils.unicodeDecode(peekBody.string()))
                }.getOrNull()
            }
        }
        sb.appendln("< < < < < < - < < - < < < < < < - < < - < < < < < < - < < - < < < < < < - < < - < < < < < < - < < - < < < < < < - < < - < < < < < < - < < - < < < < < < - < < - < < < < - < < < < - < < - < < < < - < < -- < < < < < < - < < < < - < < < < - < <")
        logIt(sb, ColorLevel.INFO)
    }

    //region log response

    private fun logHeadersRsp(response: Response, sb: StringBuffer) {
        logBasicRsp(sb, response)
        val headersStr = response.headers.joinToString(separator = "") { header ->
            "Response Header: {${header.first}=${header.second}}\n"
        }
        sb.appendln(headersStr)
    }

    private fun logBasicRsp(sb: StringBuffer, response: Response) {
        sb.appendln("The response protocol:${response.protocol} code: ${response.code} message: ${response.message}")
            .appendln("Response request Url:${decodeUrlStr(response.request.url.toString())}")
            .appendln(
                "Response sentRequestTime:${ toDateTimeStr( response.sentRequestAtMillis, MILLIS_PATTERN ) } receivedResponseTime: ${ toDateTimeStr( response.receivedResponseAtMillis, MILLIS_PATTERN ) }")}//endregion

    /** * For urL-encoded string decoding */
    private fun decodeUrlStr(url: String): String? {
        return kotlin.runCatching {
            URLDecoder.decode(url, "utf-8")
        }.onFailure { it.printStackTrace() }.getOrNull()
    }

    /** * Print logs * [any] Data object to print * [tempLevel] for temporary adjustment of print color level */
    private fun logIt(any: Any, tempLevel: ColorLevel? = null) {
        when(tempLevel ? : colorLevel) { ColorLevel.VERBOSE -> Log.v(logTag, any.toString()) ColorLevel.DEBUG -> Log.d(logTag, any.toString()) ColorLevel.INFO -> Log.i(logTag, any.toString()) ColorLevel.WARN -> Log.w(logTag, any.toString()) ColorLevel.ERROR -> Log.e(logTag, any.toString()) } }companion object {
        private const val TAG = "<KtHttp>"// The default TAG

        // Time formatting
        const val MILLIS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSSXXX"

        // Converts to a formatted time string
        fun toDateTimeStr(millis: Long, pattern: String): String {
            return SimpleDateFormat(pattern, Locale.getDefault()).format(millis)
        }
    }


    /** * The range of logs to be printed */
    enum class LogLevel {
        NONE,/ / not print at all
        BASIC,// Paper prints the beginning of the line, request/response
        HEADERS,// Prints all headers for the request and response
        BODY,// Print all
    }

    /** ** Logcat is divided into V, D, I, W, e */
    enum class ColorLevel {
        VERBOSE,
        DEBUG,
        INFO,
        WARN,
        ERROR
    }

}
Copy the code

.

  • LocalCookieJar
package com.am.kttt.config

import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl

/** * cookieJar implementation class for persistence */
internal class LocalCookieJar : CookieJar {

    // Local storage of cookies
    private val cache = mutableListOf<Cookie>()

    override fun loadForRequest(url: HttpUrl): List<Cookie> {
        // An expired Cookie
        val invalidCookies: MutableList<Cookie> = ArrayList()
        // A valid Cookie
        val validCookies: MutableList<Cookie> = ArrayList()

        for (cookie in cache) {
            if (cookie.expiresAt < System.currentTimeMillis()) {
                // Determine whether it expires
                invalidCookies.add(cookie)
            } else if (cookie.matches(url)) {
                // Match Cookie to URL
                validCookies.add(cookie)
            }
        }

        // Remove expired cookies from the cache
        cache.removeAll(invalidCookies)

        // Return List
      
        for Request to set
      
        return validCookies
    }

    /** * Save the cookie */
    override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
        cache.addAll(cookies)
    }
}
Copy the code

.

  • NetPjConfig.kt
package com.am.kttt.config

/** * App configuration */


// Region network interaction configuration parameters

const val NET_CONFIG_APPID = "xxxxxx"// appID marks the project APK
const val NET_CONFIG_APPKEY = "xxxxxxH\$pHx\$!"// AppKey is used to parse encrypted data returned by the server

//endregion


//region Data field key of the local cache

const val SP_KEY_USER_TOKEN = "sp_key_user_token"// Indicates the user's token


//endregion
Copy the code

.

  • RetryInterceptor
package com.am.kttt.config

import android.util.Log
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response

/** * Retries the request's network interceptor */
class RetryInterceptor(private val maxRetry: Int = 0) : Interceptor {

    private var retriedNum: Int = 0// The number of retry times, so the total number of requests may be the original 1 + maxRetry

    override fun intercept(chain: Interceptor.Chain): Response {
        val request: Request = chain.request()
        Log.d("RetryInterceptor"."Intercept Line 29: Current retriedNum=$retriedNum")
        var response = chain.proceed(request)
        while(! response.isSuccessful && retriedNum < maxRetry) { retriedNum++ Log.d("RetryInterceptor"."Intercept line 33: Current retriedNum=$retriedNum")
            response = chain.proceed(request)
        }
        return response
    }
}
Copy the code

.

  • NetResponse
package com.am.kttt.model

/** * The underlying network returns a data structure */
data class NetResponse(
    val code: Int./ / the response code
    val data: Any? .// Response data content
    val message: String// The result description of the response data
)
Copy the code

.

  • IHttpCallback

package com.am.kttt.support

/** * Network request interface callback */
interface IHttpCallback {

    /** * Network request successful callback * [data] returns the data result of the callback *@paramData returns the result of the callback */
    fun onSuccess(data: Any?).


    /** * Interface callback failed * [error] Data class of the error message */
    fun onFailed(error: Any?).

}
Copy the code

.

  • NetTranUtils
package com.am.kttt.support;

import androidx.annotation.Nullable;

import com.blankj.utilcode.util.EncryptUtils;

import com.am.kttt.config.NetPjConfigKt;

import java.util.regex.Matcher;
import java.util.regex.Pattern;


/** * Network conversion tools * Chinese to Unicode, Unicode to Chinese * encryption and decryption */
public class NetTranUtils {

    private NetTranUtils() {
    }

    /** * To unicode **@param string
     * @return* /
    public static String unicodeEncode(String string) {
        char[] utfBytes = string.toCharArray();
        String unicodeBytes = "";
        for (int i = 0; i < utfBytes.length; i++) {
            String hexB = Integer.toHexString(utfBytes[i]);
            if (hexB.length() <= 2) {
                hexB = "00" + hexB;
            }
            unicodeBytes = unicodeBytes + "\\u" + hexB;
        }
        return unicodeBytes;
    }

    /** * Unicode to Chinese **@param string
     * @return* /
    public static String unicodeDecode(String string) {
        Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
        Matcher matcher = pattern.matcher(string);
        char ch;
        while (matcher.find()) {
            ch = (char) Integer.parseInt(matcher.group(2), 16);
// Integer.valueOf("", 16);
            string = string.replace(matcher.group(1), ch + "");
        }
        return string;
    }

    /** * Parse the returned data **@paramDataStr Undecrypted response data *@returnDecrypted data String */
    @Nullable
    public static String decodeData(@Nullable String dataStr) {
        // Java code, no automatic null judgment, need to handle their own
        if(dataStr ! =null) {
            System.out.println("DataStr = = = = = = = = :"+dataStr);
            return new String(EncryptUtils.decryptBase64AES(
                    dataStr.getBytes(), NetPjConfigKt.NET_CONFIG_APPKEY.getBytes(),
                    "AES/CBC/PKCS7Padding"."J#y9sJesv*5HmqLq".getBytes()
            ));
        } else {
            return null; }}}Copy the code

.


The code is all up there

  • Under the APP build. Gradle
plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.am.kttt"
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    buildFeatures {
        viewBinding true}}dependencies {

    implementation 'androidx. Core: the core - KTX: 1.7.0'
    implementation 'androidx. Appcompat: appcompat: 1.4.1'
    implementation 'com. Google. Android. Material: material: 1.5.0'
    implementation 'androidx. Constraintlayout: constraintlayout: 2.1.3'
    implementation 'androidx. Lifecycle: lifecycle - livedata - KTX: against 2.4.1'
    implementation 'androidx. Lifecycle: lifecycle - viewmodel - KTX: against 2.4.1'
    implementation 'androidx. Navigation: navigation - fragments - KTX: against 2.4.1'
    implementation 'androidx. Navigation: navigation - UI - KTX: against 2.4.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx. Test. Ext: junit: 1.1.3'
    androidTestImplementation 'androidx. Test. Espresso: espresso - core: 3.4.0'


    // Required for this project
    implementation("Com. Squareup. Okhttp3: okhttp: 4.8.0")
    implementation("Com. Squareup. Okhttp3: logging - interceptor: 4.8.0")
    implementation("Com. Google. Code. Gson: gson: 2.8.6")
    implementation 'com. Blankj: utilcodex: 1.29.0'
    implementation "Androidx. Constraintlayout: constraintlayout: 2.1.0."



}
Copy the code

.

  • Build. Gradle under the project
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "Com. Android. Tools. Build: gradle: 7.0.1"
        classpath "Org. Jetbrains. Kotlin: kotlin - gradle - plugin: 1.5.20"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files}}task clean(type: Delete) {
    delete rootProject.buildDir
}
Copy the code

.

Running effect

A post

Don’t worry about what’s displayed, the interface is just random, and then this returns data that looks a lot like GET.

Initiate the get

It’s a random interface. .

Pan.baidu.com/s/1NiqjMmAn…