Android http网络请求的那些事儿

1.http协议

http协议通常是基于TCP/IT协议之上的应用层协议,http默认端口是80,https默认端口是443

http协议是无状态的协议,基于请求/响应的模型,先有请求,再有响应。

http请求报文包括:请求行、请求头、请求体

http响应报文包括:响应行、响应头、响应体

2.http请求报文结构

  • 请求行 : 主要包括请求方式以及请求目标的URL 例如:POST /chapter17/user.html HTTP/1.1

    POST代表请求方式, /chapter17/user.html表示请求的URL

  • 请求头: 包含一些key-value值,主要包括Cookie值、请求体的数据类型、请求体的数据长度等例如:

    Content-Type: application/x-www-form-urlencoded

    Content-Length: 37

    Cookie: token_pass=92595c118b66417c395eb125e12c3438

  • 请求体: 具体的数据,比如登陆时候的用户名和密码: username=gandalf&password=19920930yus

3.http不同请求方式

请求方式有多种,常见的有GETPOSTHEAD

  • GET请求: GET请求没有请求体,会把参数直接加在URL后面,不安全,并且有长度限制。 但请求简单,是HTTP请求中使用最多的方式。

  • POST请求: 请求的参数在请求头内,较安全,且数据大小没有限制,适合提交较大的表单数据。

  • HEAD请求: 跟GET相似,只是服务器只会返回响应头,通常只为了查询某个页面的状态。

4.http响应报文结构

  • 响应行: 主要包含状态码

    1xx:指示信息,表示请求已接收,继续处理

    2xx:成功,表示请求已被成功接受

    3xx:重定向

    4xx:客户端错误

    5xx:服务器端错误

  • 响应头 : 包含多个key-value对,主要包括:Content-Type响应正文的类型(MIME类型)、Set-Cookie 于会话状态相关的Cookie技术

  • 响应体 : 数据内容,常用的如json纯文本内容、图片数据等

5.https协议

由于http是明文传输,信息容易被篡改和窃取,所以增加了https协议。

https协议是在http协议的基础上,对报文做了对称加密的处理,并且加密密钥只会在客户端和服务端保存,保证了信息的安全。

https协议的前提,是需要有一个公正权威的机构,叫CA机构,它内部维护了一套非对称加密的公钥P私钥P',公钥P会发布出来,浏览器会内置这个公钥。

一个网站如果要使用https协议,需要先在CA申请数字证书。

6.https协议加密过程

前置条件完毕,接下来看具体的步骤:

  1. 首先网站有一对公钥P1私钥P1',然后把包含网站公钥P1、域名等信息的数据Data提供给CA机构。
  2. CA对Data做Hash算法得到一段散列码H;用P'对H做加密得到数字签名S;把S和Data数据打包一起就是数字证书,给到网站。
  3. 客户端和服务端建立http连接之后,服务端会把数字证书发给客户端。客户端会得到数字签名S和原始数据Data,然后使用CA的公钥P对S解密得到散列码H
  4. 客户端会对Data做同样的hash算法得到散列码H',判断H'和H是否相等,如果相等,则表示公钥P1是可信的。
  5. 客户端会针对此次对话随机生成一个对称加密密钥SK,并使用P1对SK信息做加密,传递给服务器。
  6. 服务器在收到客户端数据后,使用自己的私钥P1'做解码,获取到了本次的SK,并开始做加密通信。

流程图如下:

  • 关键点补充 : CA的存在是为了保证客户端判断来自服务器公钥的可靠性。 前期非对称加密的一系列动作,是为了保证最后对称加密的密钥不能被第三方获取。 最后通信数据使用对称加密(AES),是因为非对称加密很耗性能,而对称加密效率更高。

7.Cookie

由于http/https是没有状态的协议,整个过程是请求/应答模式,而有些数据的请求是需要有状态的,比如: 请求收藏列表数据,那么肯定是需要先登陆才行,否则就会提示:

kotlin 复制代码
{"errorCode":-1001,"errorMsg":"请先登录!"}

因此引入了Cookie技术。

首先服务端会在响应头中带有Set-Cookie 字段的数据,当客户端解析到这样的响应头数据时,就可以把这些数据保存在本地。

当客户端下次要发起请求的时候,就可以把这些Cookie写入到请求头中,这样服务端就可以清楚状态并返回正确的数据了。

比如我们登陆了WanAndroid账号之后,服务端返回的响应报文头包含:

kotlin 复制代码
Set-Cookie: JSESSIONID=354B46332F155D0823934D17025C01CB; Path=/; Secure; HttpOnly
Set-Cookie: loginUserName=gandalf; Expires=Fri, 12-Dec-2025 07:00:21 GMT; Path=/
Set-Cookie: token_pass=92595c118b66417c395eb125e12c3438; Expires=Fri, 12-Dec-2025 07:00:21 GMT; Path=/
Set-Cookie: loginUserName_wanandroid_com=gandalf; Domain=wanandroid.com; Expires=Fri, 12-Dec-2025 07:00:21 GMT; Path=/
Set-Cookie: token_pass_wanandroid_com=92595c118b66417c395eb125e12c3438; Domain=wanandroid.com; Expires=Fri, 12-Dec-2025 07:00:21 GMT; Path=/

当我们请求收藏数据的时候,就需要把以上的Cookie信息带上,这样就会获取到收藏列表数据了,否则就会提示"请先登录!"。

8.OkHttp

Android使用http网络协议的利器,它负责处理HTTP/1.1HTTP/2SPDY的请求和响应,可以理解为一个装备精良的"快递员"。

OkHttp 负责处理所有网络通信的底层脏活累活:

  • 通过连接池 ConnectionPool,高效地复用 TCP 连接;
  • socket自动选择最好路线,并支持自动重连;
  • 拥有队列线程池,轻松写并发,支持同步和异步两种请求方式;
  • 拥有强大的拦截器机制(interceptors),可以轻松修改请求报文和响应报文,比如统一修改请求头、日志记录、加解密、Cookie缓存等。

9.OkHttp的执行流程

  1. 首先需要构造一个OkHttpClient客户端,并且在客户端配置相关的拦截器和监听器,以便扩展自定义的操作。
  2. 构建Request请求类,包含URLPost参数等信息。
  3. 然后通过OkHttpClient客户端去构建一个Call类型的对象,代表一次请求。Call有两个方法,一个方法是execute(),表示同步执行请求; 另一个方法是enqueue(),表示把请求加入队列,实际会进入到Dispatcher的runningAsyncCalls这样的一个队列,并等待异步执行。
  4. 不管是同步执行还是异步执行,在执行的时候,都会先构建一个拦截器链(RealInterceptorChain),它会add很多默认的或者我们自定义的拦截器。
  5. 一个拦截器执行完之后,会调用下一个拦截器,直到调用到最后一个CallServiceInterceptor并返回一个Response

10.OkHttp使用示例

只使用OkHttp的情况下,如果我们要发起一次登陆请求,则代码如下:

kotlin 复制代码
// 1. 创建客户端
val client = OkHttpClient()

// 2. 手动构建请求 (URL, 请求头, 请求体)
val requestBody = FormBody.Builder()
    .add("username", "gandalf")
    .add("password", "1234567")
    .build()

val request = Request.Builder()
    .url("https://api.example.com/login")
    .post(requestBody)
    .build()


// 3. 发起异步请求,并手动处理回调
client.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        // 处理网络失败
    }

    override fun onResponse(call: Call, response: Response) {
        // 在子线程中
        val responseBodyString = response.body?.string()
        // 4. 手动解析 JSON 字符串
        val user = Gson().fromJson(responseBodyString, User::class.java)
        // 5. 手动切换回主线程更新 UI
    }
})

这里创建的OkHttpClient客户端是最简单的方式,如果想要添加其他配置项,比如Cookie的操作、http日志打印操作等,就需要在这里做配置:

kotlin 复制代码
private val intercepter = HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BODY
    }

private val okhttpClient = OkHttpClient.Builder()
        .readTimeout(30, TimeUnit.SECONDS)   // 读超时时间
        .writeTimeout(30, TimeUnit.SECONDS)  // 写超时时间
        .connectTimeout(30, TimeUnit.SECONDS) //连接超时时间
        .addInterceptor(intercepter)  // 添加日志打印的拦截器
	.cookieJar(MyCookieJar()) //添加Cookie操作
        .build()

这里实现了CookieJar接口:

kotlin 复制代码
class MyCookieJar : CookieJar {
    //Http发送请求前回调,Request中设置Cookie
    override fun loadForRequest(url: HttpUrl): List<Cookie> {
        val cookieList = mutableListOf<Cookie>()
        CookieManager.getInstance().getCookie(url.host)?.let {
            if (it.isNotEmpty()) {
                val cookies = it.split(";".toRegex())
                for (cookie in cookies) {
                    Cookie.parse(url, cookie)?.apply {
                        cookieList.add(this)
                    }
                }
            }
        }
        return cookieList
    }


    //Http请求结束,Response中有Cookie时候回调,并在回调中实现Cookie的保存
    override fun saveFromResponse(
        url: HttpUrl,
        cookies: List<Cookie>
    ) {
        val cookieManager = CookieManager.getInstance()
        cookieManager.setAcceptCookie(true)
        for (cookie in cookies) {
            cookieManager.setCookie(url.toString(), cookie.toString())
        }
        cookieManager.flush()
    }
}

11. Retrofit

Retrofit是一种更高层级的封装,它让开发者更容易去描述"做什么"。而它自己会把这些业务层的逻辑转换成OkHttp能够识别的指令,具体"怎么做"的事情,就交给OkHttp去完成。

Retrofit的优势有如下几点:

  1. API接口化:使用简单的Interface接口来定义一组业务关系API。
  2. 注解驱动 :通过 @GET``````@POST, @Path, @Query, @Body 等注解,你可以清晰地描述每个请求的 URL、HTTP 方法、参数和请求体
  3. 序列化/反序列化 :通过可自由配置的转换器(Converter),如retrofit2:converter-gson,自动将 对象转换成请求体JSON,以及将响应体JSON自动转换成定义好的数据模型对象(Data Class)。
  4. 适配协程:能将标准的网络调用无缝集成到协程框架中

基本使用方式如下:

kotlin 复制代码
// 1. 在接口中声明 API
interface ApiService {
    @POST("login")
    @FormUrlEncoded
    suspend fun login(@Field("username") username: String): User
}

// 2. 创建 Retrofit 实例 (它内部会持有并使用一个 OkHttpClient)
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(myOkHttpClient) // 把 OkHttp 引擎装进去
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val apiService = retrofit.create(ApiService::class.java)

// 3. 在协程中像调用本地方法一样调用 API
viewModelScope.launch {
    try {
        val user = apiService.login("test")  // 直接得到 User 对象,不用关心 JSON 解析和线程切换
    } catch (e: Exception) {
        // 统一处理所有错误
    }
}
相关推荐
im_AMBER4 小时前
HTTP概述 01
javascript·网络·笔记·网络协议·学习·http
ManageEngine卓豪4 小时前
如何在IIS中配置HTTP重定向
http·iis·http重定向
Entropless6 小时前
解剖OkHttp:那些主流教程未曾深入的设计精髓
android·okhttp
00后程序员张1 天前
HTTP抓包工具推荐,Fiddler配置方法、代理设置与使用教程详解(开发者必学网络调试技巧)
网络·http·ios·小程序·fiddler·uni-app·webview
JZZC21 天前
29. HTTP
计算机网络·http·ensp
Chief_fly1 天前
RestTemplate 和 Apache HttpClient 实现 HTTP 请求
网络协议·http·apache
それども1 天前
HTTP接口和Dubbo接口区别
网络协议·http·dubbo
铭哥的编程日记1 天前
【Linux网络】应用层协议HTTP
linux·运维·http
Moonbit1 天前
MoonBit Pearls Vol.13: 使用 MoonBit 开发一个 HTTP 文件服务器
服务器·后端·http