IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 用HttpURLConnection来实现retrofit类似功能(由于retrofit不支持5.0以下) -> 正文阅读

[移动开发]用HttpURLConnection来实现retrofit类似功能(由于retrofit不支持5.0以下)

原因:项目已经上线了,但是突然有一天,客户说他们的仓库有个4.3版本的pda需要装这个app,然后发现装上去闪退的。经过我的排查,原来是retrofit不支持5.0的,当时做项目的时候,也没想这么多,谁能想到还有5.0以下的版本的pda呢,然后让客户换pda不现实,所以只能用HttpURLConnection来实现咯,然后为了少改动代码,决定用HttpURLConnection来近似实现一下retrofit,能少改动一点是一点。废话不多说,开始上代码吧。

HttpCreator.kt

HttpCreator.kt这个文件的功能:
1.自定义注解,入HTTPGET,HTTPPOST等,之所以和retrofit定义的GET不一样是因为自己的项目是5.0以上用retrofit,然后5.0以下用自己的代码,所以注解不一样。虽然可以直接抛弃retrofit,但是由于项目是老早上线了,还是尽量少改,所以采用的是5.0以下的版本才走这个逻辑。
2.HttpCreator的单例类,实现外部的便捷调用,内部依靠动态代理实现,对定义的接口上的参数注解和方法上的注解进行校验,最后返回一个自己定义的MyCall对象(继承Call接口)。
3.sendHttpByHttpURLConnection 方法实现正真的发送网络请求,并且对数据进行处理。

(功能都在这个地方实现,所以如果要加功能的话,其实就在这里改的,我只是写了一些常见的,我没用到的就没写上)

package com.rengda.testdemon
import android.util.Log
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import java.io.BufferedReader
import java.io.InputStreamReader
import java.lang.annotation.Documented
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.net.HttpURLConnection
import java.net.SocketTimeoutException
import java.net.URL
import java.net.UnknownServiceException
import kotlin.concurrent.thread


@Documented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(RetentionPolicy.RUNTIME)
annotation class HTTPMultipart

//自定义注解get注解
@Documented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(RetentionPolicy.RUNTIME)
annotation class HTTPGET(
      val value: String = "")


//自定义注解post注解
@Documented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(RetentionPolicy.RUNTIME)
annotation class HTTPPOST(
      val value: String = "")


//自定义注解put注解
@Documented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(RetentionPolicy.RUNTIME)
annotation class HTTPPUT(
      val value: String = "")


@Documented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(RetentionPolicy.RUNTIME)
annotation class HTTPDELETE(
      val value: String = "")



//自定义注解header注解
@Documented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(RetentionPolicy.RUNTIME)
annotation class HTTPHeaders(val value: kotlin.Array<kotlin.String> )





@Documented
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
annotation class HTTPQuery(
      /** The query parameter name.  */
      val value: String,
      /**
       * Specifies whether the parameter [name][.value] and value are already URL encoded.
       */
      val encoded: Boolean = false)


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(AnnotationTarget.VALUE_PARAMETER)
annotation class HTTPPath(val value: String,
                    /**
                     * Specifies whether the argument value to the annotated method parameter is already URL encoded.
                     */
                    val encoded: Boolean = false)


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(AnnotationTarget.VALUE_PARAMETER)
annotation class HTTPHeader(val value: String)


@Documented
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
annotation class HTTPBody

@Documented
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
annotation class HTTPMultiBody







//动态代理接口 得到接口的注释 并返回一个call类型
object HttpCreator {
  //创建接口的的代理
  fun <T> createProxy(service: Class<T>): T {
      return Proxy.newProxyInstance(
              service.classLoader, arrayOf<Class<*>>(service),
              object : InvocationHandler {

                  @Throws(Throwable::class)
                  override fun invoke(proxy: Any, method: Method, args: Array<Any>?): Any? {
                      return createHttpByHttpURLConnection(method,args)
                  }
              }) as T
  }



  fun <T> create(serviceClass:Class<T>):T= createProxy(serviceClass)

  //外部用的
  inline fun <reified T>creat():T= create(T::class.java)



  //进行解析 最后返回自定义的call
  fun  createHttpByHttpURLConnection(method: Method, args: Array<Any>?):Call<Any?>{



      val queryMap= linkedMapOf<String,Any>()
      val pathMap= linkedMapOf<String,Any>()
      val headersMap= linkedMapOf<String,String>()
      var body:RequestBody?=null
      var url=""
      var partUrl=""
      var httpMethod=""


      try {



      //获取接口方法上的参数
     val methodAnnotations= method.annotations

     for (item in methodAnnotations){//遍历所有的注解

         if (item.annotationClass.simpleName=="HTTPGET"){
             partUrl= method.getAnnotation(HTTPGET::class.java).value
             httpMethod="GET"
         }else if( item.annotationClass.simpleName=="HTTPPOST"){
             partUrl= method.getAnnotation(HTTPPOST::class.java).value
             httpMethod="POST"
         }else if (item.annotationClass.simpleName=="HTTPPUT"){
             partUrl= method.getAnnotation(HTTPPUT::class.java).value
             httpMethod="PUT"
         }else if (item.annotationClass.simpleName=="HTTPDELETE"){
             partUrl= method.getAnnotation(HTTPDELETE::class.java).value
             httpMethod="DELETE"
         }else if(item.annotationClass.simpleName=="HTTPHeaders"){
             val headersList=method.getAnnotation(HTTPHeaders::class.java).value
             Log.d("HttpCreator", headersList.toString())
             for (item in headersList){
                 val font=item.indexOf(":")
                 headersMap[item.substring(0,font)]=item.substring(font+1,item.length)
             }
         }
     }




      //获取参数上的注解
      val parameterAnnotations=method.parameterAnnotations
      var i=0
      while(i<args?.size?:0){
          //根据参数进行遍历
          for (item in parameterAnnotations[i]){

              if (item.annotationClass.simpleName.toString()=="HTTPQuery"){//是查询的参数
                  val item=item as HTTPQuery
                  if (args!![i]!=null&&args!![i]!=""){//不为空我才往上拼
                      queryMap[item.value]=args!![i]
                  }
              }else if (item.annotationClass.simpleName.toString()=="HTTPPath") {//是url上的参数
                  val item=item as HTTPPath
                  pathMap[item.value]=args!![i]
              }else if (item.annotationClass.simpleName.toString()=="HTTPBody"){//是请求体
                    body=args!![i] as RequestBody
              }
              else if (item.annotationClass.simpleName.toString()=="HTTPHeader"){
                  val item=item as HTTPHeader
                  headersMap[item.value]=args!![i].toString()
              }else if (item.annotationClass.simpleName.toString()=="HTTPMultiBody"){//是mutil的
                  body=args!![i] as MutilBody
              }
          }
          i++

      }



      if (Url.BASE_URL.substring(Url.BASE_URL.length-1,Url.BASE_URL.length)=="/"){//基础的以/结尾了
          if (partUrl.indexOf("/")==0){//分路径又以/开头 重复了
              partUrl=partUrl.substring(1,partUrl.length)
          }
      }

       url=Url.BASE_URL+partUrl

      //替换掉路径里的{}
      for (item in pathMap){
          while (url.indexOf("{")!=-1){//说明有{}
              val front= url.indexOf("{")
              val back=url.indexOf("}")
              if(back==url.length-1){
                  url=url.substring(0,front)+item.value
              }else{
                  url=url.substring(0,front)+item.value+url.substring(back+1)
              }
          }
      }
      //显示的拼接到路径上
      if (queryMap.size>0){
          url= "$url?"
      }
      for (item in queryMap){
          url= "${url}${item.key}=${item.value}&"
      }
      if (queryMap.size>0){
          url=url.substring(0,url.length-1)
      }

      }catch (e:java.lang.Exception){
          e.printStackTrace()
      }




      return MyCall(url,headersMap,httpMethod,body)
  }







  class MyCall<T>(val url:String,val headerMap:LinkedHashMap<String,String>,val httpMethod: String,val body:RequestBody?) :Call<T>{
      override fun enqueue(callback: Callback<T>) {
          sendHttpByHttpURLConnection(url,httpMethod,headerMap,body,callback,this)
      }
  }




      //原生的
  fun <T> sendHttpByHttpURLConnection(url: String,requestMethod: String,headerMap:LinkedHashMap<String,String>, body:RequestBody?,callback:Callback<T>,call:Call<T>) {
          thread {
          var connection: HttpURLConnection? = null
          try {
              val url = URL(url)
              connection = url.openConnection() as HttpURLConnection
              connection.connectTimeout = 10000
              connection.readTimeout = 10000
              connection.requestMethod = requestMethod


              //参数里面的放头信息
              for (item in headerMap){
                  Log.d("HttpCreator", "自己的头信息: ${item.key}:${item.value}")
                  connection.setRequestProperty(item.key,item.value)
              }

              //如果是有body方法
              if (body!=null){
                  connection.setRequestProperty("Content-Type",body.contentType)
                  connection.setDoOutput(true);
                  val outputStream = connection.outputStream
                  outputStream.write(body.bytes);
                  outputStream.flush();
                  outputStream.close();
              }

              Log.d("HttpCreator", "url: "+url)
              Log.d("HttpCreator", "头信息: "+connection.headerFields)

              if (connection.responseCode == -1) {//连接发生错误
                  callback.onFailure(call,Throwable("无状态码", null))
              } else {
                  if (connection.responseCode >= 200 && connection.responseCode < 300) {//通话成功 [200..300)

                      Log.d("HttpCreator", "responseCode "+connection.responseCode )

                      // json 解析成功的就去成功,解析失败的就去失败
                      if (connection.responseCode == 200) {//真的成功
                          val rawData = StringBuilder()
                          val input = connection.inputStream
                          val reader = BufferedReader(InputStreamReader(input))
                          reader.use {
                              reader.forEachLine {
                                  rawData.append(it)
                              }
                          }
                          try {
                              //解析

                              val data = Gson().fromJson(rawData.toString(), callback.myResultClassType(call)) as T
                              callback.onResponse(call,Response(connection.responseCode, data, rawData.toString(), null))
                          } catch (e: JsonSyntaxException) {//不是有效的json
                              callback.onFailure(call,Throwable(rawData.toString(), null))
                          }
                      } else {//连接成功了 但是某些原因出错的
                              val errorData = StringBuilder()
                              val error = connection.errorStream
                              val reader1 = BufferedReader(InputStreamReader(error))
                              reader1.use {
                                  reader1.forEachLine {
                                      errorData.append(it)
                                  }
                              }
                              callback.onResponse(call,Response(connection.responseCode, null, null, ErrorBody(errorData.toString())))

                      }
                  } else {

                      val errorData = StringBuilder()
                      val error = connection.errorStream
                      val reader1 = BufferedReader(InputStreamReader(error))
                      reader1.use {
                          reader1.forEachLine {
                              errorData.append(it)
                          }
                      }

                      callback.onFailure(call,Throwable(errorData.toString(), null))
                  }
              }


          }
          catch (e: SocketTimeoutException){//超时
              callback.onFailure(call,Throwable("timeout", null))
          }
          catch (e: UnknownServiceException){
              Log.d("HttpCreator", "UnknownServiceException: "+e.message)
              callback.onFailure(call,Throwable(e.message, null))
              e.printStackTrace()
          }
          catch (e: Exception) {
              Log.d("HttpCreator", "Exception: "+e.message)
              callback.onFailure(call,Throwable(e.message, null))
              e.printStackTrace()
          } finally {
              connection?.disconnect()
          }
      }
  }


}

Callback.kt

Callback.kt文件实现了,很多用到的接口的定义和类的定义,都是在发送请求和回调的时候需要用到的,定义的类和接口我都是和retrofit重名的,因为这样才能少改动原来的接口。

package com.rengda.testdemon


import android.util.Log
import java.io.*
import java.lang.Exception
import java.lang.StringBuilder
import java.lang.reflect.Type
import java.nio.charset.Charset


interface  Call<T> : Cloneable {
    fun enqueue(callback: Callback<T>)
}


interface Callback<T> {


    //失败的回调
    fun onFailure( call: Call<T>,t: Throwable)

    //成功的回调
    fun onResponse(call: Call<T>,response: Response<T>)

    fun myResultClassType(call: Call<T>):Type
}


class  Response<T> (val code:Int,val body:T?,val raw:String?,val errorData:ErrorBody?){
    fun body():T?{
        return body
    }

    fun errorBody():ErrorBody?{
        return errorData
    }

}

class ErrorBody(val msg:String?){
    fun string():String{
        return msg.toString()
    }
}

//自己用的 缩减过代码
 open class  RequestBody(val bytes:ByteArray?=null,val contentType:String="application/json"){
    //创建
    companion object{
        //创建普通的
        fun create(content: String): RequestBody {
            var charset = Charset.forName("UTF-8")
            val bytes = content.toByteArray(charset!!)
            return RequestBody(bytes)
        }

        //创建文件
        fun create(content: File,contentType: String): RequestBody {
            return RequestBody(content.readBytes(),contentType)
        }


    }
}



//可以根据request body来创建表单内容 contentType是文件的类型  例如照片的话image/jpeg
class MutilBodyPart(val name:String,val fileName:String,val contentType:String,val requetBody: RequestBody )



//发送复杂的文件的原理 可以参考
//http://t.zoukankan.com/zyzl-p-4526914.html
//https://blog.csdn.net/qq_16957817/article/details/109205773
class MutilBody( bytes:ByteArray?=null, contentType:String="multipart/form-data"):RequestBody(bytes,contentType){

    companion object{
        fun createImageFormData(mutilBodyPartList: kotlin.Array<MutilBodyPart>): MutilBody {
            val boundary="aaaaaaaaaaaaaaaaaaaaaaa" //用来分割信息的边界 正文的数据就以该字段来区分
             var bodyByte= byteArrayOf()

            for (item in mutilBodyPartList){
                val  content=StringBuilder()//用来拼接数据
                //每个参数开始之前 要--bundery和一个回车
                content.append("--").append(boundary)
                content.append("\r\n")
                //来到对参数的描述
                content.append("Content-Disposition: form-data;")
                content.append("name=${item.name};")
                content.append("filename=${item.fileName};")
                content.append("filename=${item.fileName};")
                content.append("\r\n")
                content.append("Content-Type: ${item.contentType}")//这里是文件的类型
                //开始真正的数据前 要有两个回车换行
                content.append("\r\n")
                content.append("\r\n")

                //要拼接真实数据了
                //由于我的item的内容直接是ByteArray的 所以 要先把前面一段变成ByteArray,然后才拼接真的数据
                bodyByte += content.toString().toByteArray()//真实数据之前的内容的ByteArray
                bodyByte += item.requetBody.bytes?: byteArrayOf()//数据的ByteArray
                //真实数据结束之后 要一个回车
                bodyByte+= "\r\n".toByteArray()
            }

            //参数都结束时 boundary前后各需添加两上横线,最添加添回车换行
            val  content=StringBuilder()
            content.append("--").append(boundary).append("--").append("\r\n")
            bodyByte += content.toString().toByteArray()//真实数据之前的内容的ByteArray

            return MutilBody(bodyByte,"multipart/form-data ;boundary="+boundary)
        }


        fun createImageFormData(item: MutilBodyPart): MutilBody {
                val boundary="aaaaaaaaaaaaaaaaaaaaaaa" //用来分割信息的边界 正文的数据就以该字段来区分
                var bodyByte= byteArrayOf()

                var  content=StringBuilder()//用来拼接数据
                //每个参数开始之前 要--bundery和一个回车
                content.append("--").append(boundary)
                content.append("\r\n")
                //来到对参数的描述
                content.append("Content-Disposition: form-data;")
                content.append("name=${item.name};")
                content.append("filename=${item.fileName};")
                content.append("filename=${item.fileName};")
                content.append("\r\n")
                content.append("Content-Type: ${item.contentType}")//这里是文件的类型
                //开始真正的数据前 要有两个回车换行
                content.append("\r\n")
                content.append("\r\n")


                //要拼接真实数据了
                //由于我的item的内容直接是ByteArray的 所以 要先把前面一段变成ByteArray,然后才拼接真的数据
                bodyByte += content.toString().toByteArray()//真实数据之前的内容的ByteArray
                bodyByte += item.requetBody.bytes?: byteArrayOf()//数据的ByteArray
                //真实数据结束之后 要一个回车
                bodyByte+= "\r\n".toByteArray()


                //参数都结束时 boundary前后各需添加两上横线,最添加添回车换行
                content=StringBuilder()
                content.append("--").append(boundary).append("--").append("\r\n")
                bodyByte += content.toString().toByteArray()//真实数据之前的内容的ByteArray

                return MutilBody(bodyByte,"multipart/form-data ;boundary="+boundary)


        }
    }
}

InServiceHttp

用来定义访问的接口的,典型的几个接口,get,post,put,delete,有动态Header的通过参数传递的。还有文件的上传

package com.rengda.testdemon





interface InServiceHttp {


    @HTTPGET("code")
    fun getRandomStr(): Call<MyResult<RandomStr>>

    @HTTPDELETE ("/wms/car-info-fee/{id}")
    fun deleteCarInfoFee(@HTTPHeader("Authorization") authorization: String,@HTTPPath("id") id:String):Call<MyResult<String>>

    @HTTPPOST ("/wms/car-info-fee")
    fun saveCarInfoFee (@HTTPHeader("Authorization") authorization: String,@HTTPHeader("Content-Type") contentType: String,@HTTPBody body: RequestBody):Call<MyResult<CarInfo>>

    @HTTPPUT ("/wms/car-info")
    fun updateCarInfoRemark(@HTTPHeader("Authorization") authorization: String,@HTTPHeader("Content-Type") contentType: String,@HTTPBody body: RequestBody):Call<MyResult<CarInfo>>



    //图片上传
    @HTTPMultipart
    @HTTPPOST("/admin/file/upload")
    fun upload(@HTTPMultiBody requestBody: MutilBody):Call<MyResult<String>>



}

Url单例类,用来写基础地址的


object Url {
const val BASE_URL="http://xxx.xxx.xxx/"
}

entity.kt 定义了真实调用 数据接口返回需要的类

package com.rengda.testdemon

import com.google.gson.annotations.SerializedName


data class RandomStr (val randomStr:String,val base64:String)
data class MyResult<T>(val code:Int,val msg:String?, val data:T?)
data class  CarInfo(val appId: String?, val applyFlag: String?, val arrivalTime: String?, val carTeamId: String?)
data class User(@SerializedName("access_token")val accessToken:String?, @SerializedName("token_type")val tokenType:String?, @SerializedName("refresh_token") val refreshToken:String?, @SerializedName("expires_in")  val expiresIn:String?, val scope:String?, val license:String?, @SerializedName("user_id")val userId:String?, @SerializedName("user_name") val username:String?, val mobile:String?, val active:String?, @SerializedName("dept_id")val deptId:String?, @SerializedName("display_name") val displayName: String?) {
}

真实调用

package com.rengda.testdemon

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import com.google.gson.reflect.TypeToken
import org.json.JSONObject
import java.io.File
import java.lang.reflect.Type

class HttpConnectionActivity : AppCompatActivity() {
    var context:Context?=null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_http_connection)

        context=this
        doQuery()
        doPost()
        doUploadFile()

    }


    fun doQuery(){
        val service= HttpCreator.creat<InServiceHttp>()
        service.getRandomStr().enqueue(object :Callback<MyResult<RandomStr>>{
            override fun onFailure(call: Call<MyResult<RandomStr>>, t: Throwable) {
                runOnUiThread {  //里面执行失败的操作
                    Toast.makeText(context,"错误信息:${t?.message}",Toast.LENGTH_SHORT).show()
                }
            }

            override fun onResponse(
                call: Call<MyResult<RandomStr>>,
                response: Response<MyResult<RandomStr>>
            ) {
                runOnUiThread {//里面实现成功的操作
                    val myResult= response.body()
                    if (myResult !=null){//说明解析成功了,而且成功被转为 了MyResult类型
                        if (myResult.code==0){//成功
                            myResult.data as RandomStr
                            Toast.makeText(context,"获取到的随机字符:${myResult?.data.randomStr}",Toast.LENGTH_SHORT).show()
                        }else{//失败
                            Toast.makeText(context,"错误信息:${myResult?.msg}",Toast.LENGTH_SHORT).show()
                        }
                    }else{
                        Toast.makeText(context,"错误信息:${response.errorBody()?.string()}",Toast.LENGTH_SHORT).show()
                    }
                }
            }

            override fun myResultClassType(call: Call<MyResult<RandomStr>>): Type {
                //用来gson解析的时候需要的类型,由于我不会和retrofit一样的的动态获取类型,所以只能手动传递了
                return  object : TypeToken<MyResult<RandomStr>>() {}.type
            }
        })
    }



    fun doPost(){
        val service= HttpCreator.creat<InServiceHttp>()
        val body= JSONObject()
        body.put("carId","111")
        body.put("tallyId","222")
        body.put("remark","444")
        body.put("content","333")
        val requestBody=RequestBody.create(body.toString())//默认contentType是application/json
        service.saveCarInfoFee("11","111",requestBody).enqueue(object :Callback<MyResult<CarInfo>>{
            override fun onFailure(call: Call<MyResult<CarInfo>>, t: Throwable) {
                runOnUiThread {  //里面执行失败的操作


                }
            }

            override fun onResponse(call: Call<MyResult<CarInfo>>, response: Response<MyResult<CarInfo>>) {
                runOnUiThread {  //里面是成功的操作

                }
            }

            override fun myResultClassType(call: Call<MyResult<CarInfo>>): Type {
                return  object : TypeToken<MyResult<CarInfo>>() {}.type
            }
        })
    }





    fun doUploadFile(){
        val service= HttpCreator.creat<InServiceHttp>()
        val filePath="一个图片的路径"
        val file = File(filePath)
        val fileBody = RequestBody.create(file,"multipart/form-data")
        val mutilBodyPart=MutilBodyPart("file",file.name,"image/jpeg",fileBody)
        val mutilBody=  MutilBody.createImageFormData(mutilBodyPart)
        service.upload(mutilBody).enqueue(object :Callback<MyResult<String>>{
            override fun onFailure(call: Call<MyResult<String>>, t: Throwable) {
                runOnUiThread {  //里面执行失败的操作


                }
            }

            override fun onResponse(call: Call<MyResult<String>>, response: Response<MyResult<String>>) {
                runOnUiThread {  //里面是成功的操作

                }
            }

            override fun myResultClassType(call: Call<MyResult<String>>): Type {
                return  object : TypeToken<MyResult<CarInfo>>() {}.type
            }
        })
    }


}


  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 18:16:18  更:2022-06-25 18:18:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 3:30:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码