我正在参加「启航方案」

1. Android网络编程

  • OkHttp

OkHttp是一个高效的HTTP客户端,它的横空出世,让其他的网络恳求结构都变得黯然失色。

  • Retrofit

Retrofit是一个根据OkHttp的RESTful网络恳求结构,功用强壮、简洁易用及高可拓展性。Retrofit说起来适当简略,简略到源码只要37个文件,其中22个文件是注解,还都和HTTP有关,真实露出给用户的类并不多。

  • 封装

Retrofit其实就是一个根据OKHttp的网络恳求结构的封装。使恳求接口和数据解析愈加简洁明了。为什么需求封装呢?说白了,就是为了解耦,为了便利日后切换到不同结构完成,而无需到处修改调用的当地。

比方咱们项目傍边常常会用到一些之前比较盛行的的网络结构,后期这个结构中止维护或许功用无法满足业务需求,咱们想切换到新的结构,或许调用的当地会非常多,假如不做封装,直接切换的话,改动量将非常非常大,并且还很有或许会有遗失,风险度非常高。

OkHttp是一个HTTP骑牛引擎 ,担任任何底层网络操作,缓存,恳求和呼应操作等

Retrofit是在OkHttp之上构建的高档REST笼统。使恳求接口和数据解析愈加简洁明了

2. OkHttp

1. 呈现背景

在okhttp呈现以前,android上建议网络恳求要么运用系统自带的HttpClientHttpURLConnection、要么运用google开源的Volley、要么运用第三方开源的AsyncHttpClient, 随着互联网的发展,APP的业务发展也越来越复杂,APP的网络恳求数量急剧增加,但是上述的网络恳求结构均存在难以功能和并发数量的约束

OkHttp盛行得益于它的良好的架构设计,强壮的拦截器(intercepts)使得操纵网络非常便利;OkHttp现在现已得到Google官方认可,大量的app都选用OkHttp做网络恳求,其源码详见OkHttp Github。

也得益于强壮的生态,大量的盛行库都以OkHttp作为底层网络结构或供给支撑,比方RetrofitGlideFrescoMoshiPicasso等。

当OKhttp问世之后,瞬间成为各个公司的开发者的新宠,终年霸占github star榜单,okhttp能够说是为高效而生,投合了互联网高速发展的需求

2. 特色

1.一起支撑HTTP1.1与支撑HTTP2.0

2.一起支撑同步与异步恳求;

3.一起具备HTTP与WebSocket功用;

4.具有主动维护的socket衔接池,削减握手次数;

5.具有行列线程池,轻松写并发;

6.具有Interceptors(拦截器),轻松处理恳求与呼应额定需求(例:恳求失利重试、呼应内容重定向等等);

Kotlin第五章:网络编程

3. 运用

1. 增加网络拜访权限

在AndroidManifest.xml 中增加网络拜访权限

<uses-permission android:name="android.permission.INTERNET" />

2. 增加相关的依赖

app/build.gradledependencies下增加依赖

implementation("com.squareup.okhttp3:okhttp:4.9.0")
// 网络恳求日志打印
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")

3. Get恳求

1. 同步Get恳求

创立一个OkHttpDemoTest.kt文件,选用object关键字使本类在整个程序运转期间只要一个示例,适当所以单例模式,然后安卓规定网络恳求不能在主线程,所以咱们的get恳求需求新起一个线程运转

  1. 创立kt文件
package com.example.myapplication.http
import android.util.Log
import okhttp3.OkHttpClient
import okhttp3.Request
import java.util.concurrent.TimeUnit
// 程序运转的时分只要一份,适当所以单例模式
// 能够直接运用 OkHttpDemoTest1.get()调用
object OkHttpDemoTest1{
    private val client = OkHttpClient.Builder()
        .connectTimeout(10,TimeUnit.SECONDS) // 衔接超时时刻
        .readTimeout(10,TimeUnit.SECONDS) //读取超时
        .writeTimeout(10,TimeUnit.SECONDS) // 恳求超时
        .build()
    fun get(url: String){
        Thread(Runnable {
            val request = Request.Builder()
                .url(url)
                .build()
            // 结构恳求目标
            val call = client.newCall(request)
            // 建议同步恳求
            val response = call.execute()
            // 获取恳求的回来信息
            val body = response.body?.string()
            // Log.e是安卓自带的一个打印日志的办法,日志信息会打印到logcat里
            Log.e("OkHttp $this","get response: $body")
        }).start()
    }
}
  1. MainActivity中调用
package com.example.myapplication
import android.os.Bundle
import com.example.myapplication.databinding.ActivityMainBinding
import com.example.myapplication.http.OkHttpDemoTest1
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
		// 中心代码省略
        val url = "http://123.56.232.18:8080/serverdemo/user/query?userId=1"
        OkHttpDemoTest1.get(url)
    }
}
  1. 假如创立运用的时分是高版别的安卓的话是不支撑http恳求的,有必要要运用https,在AndroidManifest.xmlapplication标签中增加相应开关
<application
    android:usesCleartextTraffic="true">
</application>
  1. 运转模拟器,运转运用
    Kotlin第五章:网络编程

2. 异步Get恳求

异步恳求是没有回来值的,需求完成callback办法来操作

  1. 创立办法
 // 异步恳求
fun getAsync(url: String){
    val request = Request.Builder()
    .url(url)
    .build()
    // 结构恳求目标
    val call = client.newCall(request)
    // 运用enqueue 建议异步恳求
    val response = call.enqueue(object: Callback{
        // 失利的话会回调此办法
        override fun onFailure(call: Call, e: IOException) {
            Log.e("OkHttp Get Async  $this","")
        }
		// 接口调用成功之后调用此办法
        override fun onResponse(call: Call, response: Response) {
            // 获取恳求的回来信息
            val body = response.body?.string()
            Log.e("OkHttp Get Async $this","get response: $body")
        }
    })
}
  1. MainActivityonViewCreated中测验
val url = "http://123.56.232.18:8080/serverdemo/user/query?userId=1"
//OkHttpDemoTest1.get(url)
OkHttpDemoTest1.getAsync(url)
  1. 当然也能够将获取call目标的办法封装一下
fun getClientCall(url: String): Call{
    val request = Request.Builder()
    .url(url)
    .build()
    // 结构恳求目标
    return client.newCall(request)
}

3. Get恳求总结

异步恳求的过程和同步恳求相似,仅仅调用了Callenqueue办法异步恳求,结果通过回调CallbackonResponse办法及onFailure办法处理。

看了两种不同的Get恳求,基本流程都是先创立一个OkHttpClient目标,然后通过Request.Builder()创立一个Request目标,OkHttpClient目标调用newCall()并传入Request目标就能取得一个Call目标。

而同步和异步不同的当地在于execute()enqueue()办法的调用,

调用execute()为同步恳求并回来Response目标;

调用enqueue()办法测验通过callback的方式回来Response目标。

留意:无论是同步仍是异步恳求,接收到Response目标时均在子线程中,onFailureonResponse的回调是在子线程中的,咱们需求切换到主线程才干操作UI控件

4. Post恳求

POST恳求与GET恳求不同的当地在于Request.Builderpost()办法,post()办法需求一个RequestBody的目标作为参数

1. 同步Post恳求

  1. 书写恳求
fun post(url: String): Unit{
    val body = FormBody.Builder()
    .add("userId","123")
    .add("key","value")
    .add("tagId","71")
    .build()
    val request = Request.Builder()
    .url(url)
    .post(body)
    .build()
    val call = client.newCall(request)
    // 由于传入的是一个函数,直接简略写法写到大括号里完事
    Thread {
        val response = call.execute()
        Log.e("OkHttp Post formData ${body.toString()}","")
        Log.e("OkHttp Post response","response $response")
    }.start()
}
  1. MainActivity调用
val baseUrl = "http://123.56.232.18:8080/serverdemo"
OkHttpDemoTest1.post("$baseUrl/tag/toggleTagFollow")

2. 异步Post表单提交

与Get恳求相同,只需求将execute()换成enqueue()即可

  1. 书写办法
fun postAsyncForm(url: String){
    val body = FormBody.Builder()
    .add("userId","123")
    .add("key","value")
    .add("tagId","71")
    .build()
    val request = Request.Builder()
    .url(url)
    .post(body)
    .build()
    val call = client.newCall(request)
    call.enqueue(object :Callback{
        override fun onFailure(call: Call, e: IOException) {
            Log.e("Post异步提交表单数据失利")
        }
        override fun onResponse(call: Call, response: Response) {
            Log.i("Post异步提交表单成功","response $response")
        }
    })
}
  1. 调用
val baseUrl = "http://123.56.232.18:8080/serverdemo"
OkHttpDemoTest1.postAsyncForm("$baseUrl/tag/toggleTagFollow")

3. Post恳求文件上传

读取存储卡的文件需求在清单文件中声明权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
/**
     * 异步post上传文件
     * 在android6.0今后,读取外部存储卡的文件需求动态恳求权限
     * 即时声明了权限也需求动态授权的
     */
fun postAsyncMultipart(context: Context,url: String){
    val file = File(Environment.getExternalStorageDirectory(),"1.png")
    if(!file.exists()){
        Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show()
        return
    }
    val multipartBody = MultipartBody.Builder()
    .addFormDataPart("key", "value")
    .addFormDataPart(
        "file", "file.png",
        RequestBody.create("application/octet-stream".toMediaType(), file)
    )
    .build()
    val request = Request.Builder().url(url)
    .post(multipartBody)
    .build()
    val call = client.newCall(request)
    call.enqueue(object :Callback{
        override fun onFailure(call: Call, e: IOException) {
            Log.e("异步post恳求上传文件失利","$e")
        }
        override fun onResponse(call: Call, response: Response) {
            Log.e("异步post恳求上传文件成功","response $response")
        }
    })
}

4. Post提交字符串

fun postAsyncString(url: String){
    val jsonObject = JSONObject()
    jsonObject.put("'key1","value1")
    jsonObject.put("'key2","value2")
    // 这儿假如想要提交纯文本的话需求指定的恳求头为 text/plain;charset=utf-8
    val body = RequestBody.create(
        "application/json;charset=utf-8".toMediaType(),
        jsonObject.toString()
    )
    val request = Request.Builder().url(url)
    .post(body)
    .build()
    val call = client.newCall(request)
    call.enqueue(object :Callback{
        override fun onFailure(call: Call, e: IOException) {
            Log.e("OkHttp Post发送json参数失利","错误信息 $e")
        }
        override fun onResponse(call: Call, response: Response) {
            Log.e("OkHttp Post发送json参数成功","回来结果 $response")
        }
    })
}

5. 拦截器

拦截器是OkHttp傍边的一个比较强壮的机制,能够监视,重写和重试调用恳求.

本次比方书写一个拦截恳求记载并日志信息输出的拦截器

  1. 创立拦截器
package com.example.myapplication.interceptor
import android.util.Log
import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.ResponseBody
import okio.Buffer
// 自界说拦截器需求完成 okhttp中的接口
class LogInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        // 获取恳求执行时的时刻戳
        val timeStart = System.nanoTime()
        // 获取调用链中的request目标
        val request = chain.request()
        var buffer = Buffer()
        request.body?.writeTo(buffer)
        val requestBodyStr = buffer.readUtf8()
        Log.e("OkHttp",
            String.format("Sending request %s with params 5s",request.url,requestBodyStr))
        val response = chain.proceed(request)
        // response 只能读取一次,后续再次读取body的时分就会报错
        val responseData = response.body?.string()?: "response body null"
        // 构建新的responseBody
        val responseBody = ResponseBody.create(response.body?.contentType(), responseData)
        val endTime = System.nanoTime()
        Log.e("OkHttp","接口恳求地址为 ${request.url},接口回来的数据是 $responseData,用时${(endTime - timeStart) / 1e6}ms ")
        // 回来新的 response
        return response.newBuilder().body(responseBody).build()
    }
}
  1. 运用拦截器

在上边创立client的时分增加上拦截器即可

private val client = OkHttpClient.Builder()
        .connectTimeout(10,TimeUnit.SECONDS) // 衔接超时时刻
        .readTimeout(10,TimeUnit.SECONDS) //读取超时
        .writeTimeout(10,TimeUnit.SECONDS) // 恳求超时
        .addInterceptor(LogInterceptor())
        .build()

Kotlin第五章:网络编程

  1. 优化日志输出

能够看到,上边的比方输出的日志中心有许多的无用信息,所以能够优化一下日志输出,将interceptor的级别设置为Body,这样输出的日志就会美观一点

// 将client目标提取到外边
private val client : OkHttpClient
// 初始化类的时分加载这个办法
init {
    // 运用okhttp自带的拦截器
    val httpLoggingInterceptor = HttpLoggingInterceptor()
    // 设置拦截器级别为 Body
    httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
    // 给client增加 interceptor
    client = OkHttpClient.Builder()
    .connectTimeout(10,TimeUnit.SECONDS) // 衔接超时时刻
    .readTimeout(10,TimeUnit.SECONDS) //读取超时
    .writeTimeout(10,TimeUnit.SECONDS) // 恳求超时
    .addInterceptor(httpLoggingInterceptor)
    .build()
}

Kotlin第五章:网络编程

3. 运用Gson

1. 增加依赖

增加在app/build.gradle

dependencies{
    implementation 'com.google.code.gson:gson:2.8.6'
}

2. 解析json到目标

这儿类目标需求用 Account::class.java而不是Account.class

package com.example.myapplication.http
import com.google.gson.Gson
class Account {
    var uid: String = ""
    var userName: String = "Freeman"
    var password: String = "pwd"
    var phone: String = "17663333333"
    override fun toString(): String {
        return "Account(uid='$uid', userName='$userName', password='$password', phone='$phone')"
    }
}
fun main() {
    val jsonStr = """
            {
              "uid": "123",
              "userName": "test",
              "password": "pwd",
              "phone": "16666666666"
            }
        """.trimIndent()
    var gson = Gson()
    var fromJson = gson.fromJson<Account>(jsonStr, Account::class.java)
    println(fromJson.toString())
}

3. 目标转Json

var toJson = gson.toJson(fromJson)
println(toJson)

4. 集合转json

val jsonArrayStr = """
        [{
          "uid": "123",
          "userName": "test",
          "password": "pwd",
          "phone": "16666666666"
        }]
    """.trimIndent()
var fromJsonArray: List<Account> =
        gson.fromJson(jsonArrayStr, object : TypeToken<List<Account>>(){}.type)
println(fromJsonArray)

5. 优化实体类

运用class 界说类的时分,类里边的字段需求有初始值,不是很便利运用,所以这个时分能够用data class,并且还不用写toString()办法

data class Account2(
    val uid: String = "111",
    val userName: String,
    val password: String,
    val phone: String
)
fun main(){
    var fromJson2 = gson.fromJson(jsonStr, Account2::class.java)
    println(fromJson2)
}

6. JsonToKotlinClass插件

File –> plugins–> JsonToKotlinClass插件下载,快捷键是 alt + K,或许右键找到 generate然后选择kotlinm data class from JSON

{
  "status": 200,
  "message": "成功",
  "data": {
    "data": {
      "id": 3117,
      "userId": 160093269,
      "name": "qvelychubby",
      "avatar": "",
      "description": "更多android进阶课程请在慕课授索lovelychubby",
      "likeCount": 985,
      "topCommentCount": 200,
      "followCount": 100,
      "followerCount": 10,
      "qqOpenId": "A8747C32A5D614281E65DA5B473D1F31",
      "expires_time": 1640266383000,
      "score": 1000,
      "historyCount": 10,
      "commentCount": 3,
      "favoriteCount": 0,
      "feedCount": 10,
      "hasFollow": false
    }
  }
}
  • 生成实体类
// 主动生成的是没有泛型的,这儿能够直接增加上泛型
// 后续直接改泛型就行了,就不需求改实体类了
data class Result<T>(
    val data: Data<T>,
    val message: String,
    val status: Int
)
data class Data<T>(
    val data: T
)
data class UserInfo(
    val avatar: String,
    val commentCount: Int,
    val description: String,
    val expires_time: Long,
    val favoriteCount: Int,
    val feedCount: Int,
    val followCount: Int,
    val followerCount: Int,
    val qqOpenId: String,
    val hasFollow: Boolean,
    val historyCount: Int,
    val id: Int,
    val likeCount: Int,
    val name: String,
    val score: Int,
    val topCommentCount: Int,
    val userId: Int
)
fun main(){
    val responseJson = """ 上边的json """
    var result = gson.fromJson<Result<UserInfo>>(responseStr, Result::class.java)
    println(result)
}

4. Retrofit

Retrofit是一个高质量高效率的HTTP恳求库,是一个restful的恳求库,和OkHttp同样出自Square公司。Retrofit内部依赖于OkHttp,它将OKHttp底层的代码和细节都封装了起来,功用上做了更多的扩展,比方回来结果的主动解析,网络引擎的切换,拦截器……

有了Retrofit之后对于一些恳求咱们就只需求一行代码或许一个注解、大大简化了网络恳求的代码量。

1. 注解

etrofit注解驱动型上层网络恳求结构,运用注解来简化恳求,大体分为以下几类:

  • 用于标注网络恳求方式的注解
  • 标记网络恳求参数的注解
  • 用于标记网络恳求和呼应格局的注解
interface ApiService{
    @GET("user/query")
    Call<User> queryUser(@Query("userId") String userId);
}
val mApi = retrofit.create(ApiService.class);
val response = mApi.queryUser("100086").execute()

Kotlin第五章:网络编程

1. 恳求办法注解

序号 注解 阐明
1 @GET get恳求
2 @POST post恳求
3 @PUT put恳求
4 @DELETE delete恳求
5 @PATCH patch恳求,该恳求是对put恳求的补充,用于更新部分资源
6 @HEAD head恳求
7 @OPTIONS option恳求
8 @HTTP 通用注解,能够替换以上一切的注解,其具有三个属性:method,path,hasBody

2. 恳求头注解

注解 阐明
@Headers 用于增加固定恳求头,能够一起增加多个。通过该注解增加的恳求头不会相互掩盖,而是共同存在
@Header 作为办法的参数传入,用于增加不固定值的Header,该注解会更新已有的恳求头

3. 恳求参数注解

称号 阐明
@Body 多用于post恳求发送非表单数据,比方想要以post方式传递json格局数据
@Filed 多用于post恳求中表单字段,Filed和FieldMap需求FormUrlEncoded结合运用
@FiledMap 和@Filed作用共同,用于不确定表单参数
@Part 用于表单字段,Part和PartMap与Multipart注解结合运用,合适文件上传的情况
@PartMap 用于表单字段,默许承受的类型是Map,可用于完成多文件上传
@Path 用于url中的占位符
@Query 用于Get中指定参数
@QueryMap 和Query运用相似
@Url 指定恳求途径

4. 恳求和呼应格局注解

称号 阐明
@FormUrlEncoded 表示恳求发送编码表单数据,每个键值对需求运用@Field注解
@Multipart 表示恳求发送multipart数据,需求合作运用@Part
@Streaming 表示呼运用字节流的方式回来.假如没运用该注解,默许会把数据全部载入到内存中.该注解在在下载大文件的特别有用

2. 运用

1. 引进依赖

app/build.gradle中增加

// 引进 retrofit结构
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'

2. 初始化

  • 创立东西类

baseUrl有必要以 / 结束,不然会报错

package com.example.myapplication.http
import com.example.myapplication.interceptor.LogInterceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
object RetrofitUtil {
    // 创立 okhttp的client,能够在这儿增加拦截器等
    private val client = OkHttpClient.Builder()
        .connectTimeout(60,TimeUnit.SECONDS)
        .readTimeout(60,TimeUnit.SECONDS)
        .writeTimeout(60,TimeUnit.SECONDS)
        .addInterceptor(LogInterceptor())
        .build()
    // 运用okhttp自带的日志输出
    private var clientWithHttpLoggingInterceptor = OkHttpClient()
    init {
        val httpLoggingInterceptor = HttpLoggingInterceptor()
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
        clientWithHttpLoggingInterceptor = OkHttpClient.Builder()
            .connectTimeout(10,TimeUnit.SECONDS) // 衔接超时时刻
            .readTimeout(10,TimeUnit.SECONDS) //读取超时
            .writeTimeout(10,TimeUnit.SECONDS) // 恳求超时
            .addInterceptor(httpLoggingInterceptor)
            .build()
    }
    // 创立出来 retrofit的目标
    private var retrofit = Retrofit.Builder()
    	// 这儿能够运用自己界说的client
        .client(clientWithHttpLoggingInterceptor)
    	// 留意,这儿有必要以 / 结束,不然会报错
        .baseUrl("http://123.56.232.18:8080/serverdemo/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    fun <T> create(clazz: Class<T>): T{
        return retrofit.create(clazz)
    }
}
  1. 创立接口
package com.example.myapplication.http
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
interface ApiServiceKotlin  {
    @GET(value = "user/query")
    fun queryUser(
        @Query(value = "userId", encoded = true) userId: String): Call<Result<UserInfo>>
}
  1. MainActivity中运用

retrofit结构现已帮助咱们完成了线程切换,在这儿能够直接操作主线程了

var serviceKotlin: ApiServiceKotlin = RetrofitUtil.create(ApiServiceKotlin::class.java)
serviceKotlin.queryUser("123456")
    .enqueue(object: Callback<Result<UserInfo>> {
        override fun onResponse(
            call: retrofit2.Call<Result<UserInfo>>,
            response: retrofit2.Response<Result<UserInfo>>
        ) {
            Log.e("Retrofit Success","'$response")
        }
        override fun onFailure(call: retrofit2.Call<Result<UserInfo>>, t: Throwable) {
            Log.e("Retrofit",t.message?: "unknown reason")
        }
    })

3. 测验用例

  • java格局
public interface ApiService {
    @GET("user/query")
    Call<User> queryUser(@Query("userId") String userId);
    //运用@Headers增加多个恳求头
    @Headers({"User-Agent:android", "apikey:123456789", })
    @GET("user/query")
    Call<User> queryUser(@Query("userId") String userId);
    // 多个参数的情况下能够运用@QueryMap,但只能用在GET恳求上
    @GET("user/query"")
    Call<User> queryUser(@QueryMap Map<String, String> params);
    /**
     * 许多情况下,咱们需求上传json格局的数据。当咱们注册新用户的时分,由于用户注册时的数据相对较多
     * 并或许今后会变化,这时分,服务端或许要求咱们上传json格局的数据。此时就要@Body注解来完成。
     * 直接传入实体,它会自行转化成Json, @Body只能用在POST恳求上
     *
     * 字符串提交
     */
    @POST("user/update")
    Call<User> update(@Body News post);
    /**
    * 表单提交(键值对提交)
    */
    @POST()
    @FormUrlEncoded  
    Call<User> executePost(@FieldMap Map<String, Object> maps);
    /**
     * 表单上传文件(键值对提交、一起上传文件)
     */
    @Multipart
    @POST("upload/upload")
    Call<> register(@Field("openId") String openId, @PartMap Map<String, MultipartBody.Part> map);
}
  • kotlin 版别
interface ApiServiceKotlin  {
    @GET(value = "user/query")
    fun queryUser(
        @Query(value = "userId", encoded = true) userId: String): Call<Result<UserInfo>>
}

tring> params);

/**
 * 许多情况下,咱们需求上传json格局的数据。当咱们注册新用户的时分,由于用户注册时的数据相对较多
 * 并或许今后会变化,这时分,服务端或许要求咱们上传json格局的数据。此时就要@Body注解来完成。
 * 直接传入实体,它会自行转化成Json, @Body只能用在POST恳求上
 *
 * 字符串提交
 */
@POST("user/update")
Call<User> update(@Body News post);
/**
* 表单提交(键值对提交)
*/
@POST()
@FormUrlEncoded  
Call<User> executePost(@FieldMap Map<String, Object> maps);
/**
 * 表单上传文件(键值对提交、一起上传文件)
 */
@Multipart
@POST("upload/upload")
Call<> register(@Field("openId") String openId, @PartMap Map<String, MultipartBody.Part> map);

}


- kotlin 版别
```kotlin
interface ApiServiceKotlin  {
    @GET(value = "user/query")
    fun queryUser(
        @Query(value = "userId", encoded = true) userId: String): Call<Result<UserInfo>>
}