1.http协议
http协议通常是基于TCP/IT协议之上的应用层协议,http默认端口是80,https默认端口是443。
http协议是无状态的协议,基于请求/响应的模型,先有请求,再有响应。
http请求报文包括:请求行、请求头、请求体
http响应报文包括:响应行、响应头、响应体
2.http请求报文结构

-
请求行 : 主要包括请求方式以及请求目标的
URL例如:POST /chapter17/user.html HTTP/1.1POST代表请求方式,/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不同请求方式
请求方式有多种,常见的有GET、POST和HEAD
-
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协议加密过程
前置条件完毕,接下来看具体的步骤:
- 首先网站有一对
公钥P1和私钥P1',然后把包含网站公钥P1、域名等信息的数据Data提供给CA机构。 - CA对Data做
Hash算法得到一段散列码H;用P'对H做加密得到数字签名S;把S和Data数据打包一起就是数字证书,给到网站。 - 客户端和服务端建立http连接之后,服务端会把数字证书发给客户端。客户端会得到数字签名S和原始数据Data,然后使用CA的公钥P对S解密得到
散列码H。 - 客户端会对Data做同样的hash算法得到
散列码H',判断H'和H是否相等,如果相等,则表示公钥P1是可信的。 - 客户端会针对此次对话随机生成一个对称加密密钥
SK,并使用P1对SK信息做加密,传递给服务器。 - 服务器在收到客户端数据后,使用自己的私钥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.1、HTTP/2、SPDY的请求和响应,可以理解为一个装备精良的"快递员"。
OkHttp 负责处理所有网络通信的底层脏活累活:
- 通过连接池
ConnectionPool,高效地复用TCP连接; socket自动选择最好路线,并支持自动重连;- 拥有队列线程池,轻松写并发,支持同步和异步两种请求方式;
- 拥有强大的拦截器机制(
interceptors),可以轻松修改请求报文和响应报文,比如统一修改请求头、日志记录、加解密、Cookie缓存等。
9.OkHttp的执行流程
- 首先需要构造一个
OkHttpClient客户端,并且在客户端配置相关的拦截器和监听器,以便扩展自定义的操作。 - 构建
Request请求类,包含URL、Post参数等信息。 - 然后通过OkHttpClient客户端去构建一个
Call类型的对象,代表一次请求。Call有两个方法,一个方法是execute(),表示同步执行请求; 另一个方法是enqueue(),表示把请求加入队列,实际会进入到Dispatcher的runningAsyncCalls这样的一个队列,并等待异步执行。 - 不管是同步执行还是异步执行,在执行的时候,都会先构建一个拦截器链(
RealInterceptorChain),它会add很多默认的或者我们自定义的拦截器。 - 一个拦截器执行完之后,会调用下一个拦截器,直到调用到最后一个
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的优势有如下几点:
- API接口化:使用简单的Interface接口来定义一组业务关系API。
- 注解驱动 :通过
@GET``````@POST,@Path,@Query,@Body等注解,你可以清晰地描述每个请求的 URL、HTTP 方法、参数和请求体 - 序列化/反序列化 :通过可自由配置的转换器(
Converter),如retrofit2:converter-gson,自动将 对象转换成请求体JSON,以及将响应体JSON自动转换成定义好的数据模型对象(Data Class)。 - 适配协程:能将标准的网络调用无缝集成到协程框架中
基本使用方式如下:
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) {
// 统一处理所有错误
}
}