• 用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()
              }
          }
      }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374

    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)
    
    
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156

    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>>
    
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

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

    
    object Url {
    const val BASE_URL="http://xxx.xxx.xxx/"
    }
    
    • 1
    • 2
    • 3
    • 4

    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?) {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    真实调用

    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
                }
            })
        }
    
    
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
  • 相关阅读:
    RISC-V (五)上下文切换和协作式多任务
    Qt——窗口
    Vue3.0 项目启动(打造企业级音乐App)
    故障演练的关键要素及重要性
    黑客(网络安全)技术自学30天
    【Rust】包和模块,文档注释,Rust格式化输出
    我复现的第一个神经网络: LeNet
    DHorse系列文章之镜像制作
    论文相关知识:扁平化设计
    presto框架【博学谷学习记录】
  • 原文地址:https://blog.csdn.net/qigaidawang111/article/details/125389416