网络库源码

网络框架比较

453842de-7718-465d-9dbd-210f6c940968

  • 在Android 2.3版本及以后,HttpClientHttpURLConnection则是最佳的选择,HttpURLConnection的API提供的比较简单,可以更加容易地去使用和扩展它。而且速度快、节省电量。内部也改成了OkHttp

  • OkHttp 处理了很多网络问题:自动重连、会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP。OkHttp还处理了代理服务器问题、SSL握手失败问题、自签名。

  • volley的设计目标就是非常适合数据量小,通信量大的客户端,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。Volley停止了更新,而OkHttp得到了官方的认可,并在不断优化。因此我最终替换为了OkHttp

  • 如何取消一个网络请求call.cancel();(okhttp),在activity的ondestory里取消网络请求

okHttp

优势

大部分库都有,自动智能请求重试 ;持久化 cookie 存储
1.高性能Http请求库。
2.支持SPDY,共享同一个Socket来处理同一个服务器所有的请求.
3.支持http2.0、websocket;支持同步、异步。
4.内部封装了线程池、数据转换、参数使用、错误处理等。
5.支持GZIP来减少数据流量。
6.基于NIO和OKio,性能更高。

HTTP 1. x 中,如果想并发多个请求,必须使用多个 TCP 链接
unknown_filename.4

用法

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
    //okhttp第一步
private final OkHttpClient client = new OkHttpClient();

public void okhttpAsyGet() throws Exception {
//okhttp第二步
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();

//okhttp第三步
okhttp3.Response response = client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}

System.out.println(response.body().string());
}

public void OkHttpSyncGet() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();

client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}


@Override
public void onResponse(Call call, okhttp3.Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(response.body().string());//只能获取一次,可以用string保存
}
});
}

String post(String url, String json) throws IOException {

RequestBody formBody = new FormEncodingBuilder()
.add("platform", "android")
.add("name", "bug")
.add("subject", "XXXXXXXXXXXXXXX")
.build();

Request request = new Request.Builder()
.url(url)
.post(body)
.build();

Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
}

源码

peiniwan/okhttpTest: okhttp源码解析注释

先构建request对象,然后 okhttp3.Response response = client.newCall(request).execute();

总结:

  • 在 newCall (Request request) (request 是请求参数和 URL)的时候,其实是里面创建了一个 RealCall 的对象,里面有 execute () 方法。里面有 getResponseWithInterceptorChain () ,添加了很多 Interceptor,并返回 Response 对象的。
  • RealCall.enqueue () 被调⽤的时候⼤同⼩异,区别在于 enqueue () 会使⽤ Dispatcher 的线程池来把请求放在后台线程进⾏(把当前的请求Call.enqueue添加(AsyncCall)到请求队列中,并通过回调(Callback)的方式来获取最后结果),但实质上使⽤的同样也是 getResponseWithInterceptorChain () ⽅法。
  • getResponseWithInterceptorChain () ⽅法做的事:把所有配置好的 Interceptor 放在⼀个 List ⾥,然后作为参数,创建⼀个 RealInterceptorChain 对象,并调⽤ chain.proceed (request) 来发起请求和获取响应。
    1. RetryAndFollowlnterceptor:负责失败重试、重定向
    2. Bridgelnterceptor 负责向服务器发送请求数据,例如头消息、cookie 等等
    3. Cachelnterceptor: 读取缓存、更新缓存
    4. Connectlnterceptor:负责和服务器建立连接的。在这⾥,OkHttp 会创建出⽹络请求所需要的 TCP 连接(如果是 HTTP),或者是建⽴在 TCP 连接之上的 TLS 连接(如果是 HTTPS),并且会创建出对应的 HttpCodec 对象(⽤于编码解码 HTTP 请求)
    5. Networklnterceptor:从服务器读取响应数据,数据流拦截器,io 操作,报文封装和解析。
  • 每一个功能都只是一个 Interceptor,它们再连接成一个 Interceptor. Chain,最终完成一次网络请求。
  • 责任链模式,工厂模式,建造者模式

QQ截图20200404104435

unknown_filename.2|700

unknown_filename.1|700

其他类

  1. dispatcher :调度器,⽤于调度多线程发起⽹络请求。默认最大并发数 64,单域名最大并发 5。里面用到了线程池和队列,线程池核心数是0。可运行线程数通过队列的个数限制的(ArrayDeque<AsyncCall>),AsyncCall 是个 runable
1
2
3
4
5
//线程池,核心线程数为0,最大线程数为最大整数,线程空闲存活时间60s,//SynchronousQueue 直接提交策略
private static final Executor executor = new ThreadPoolExecutor(0,
Integer.MAX_VALUE , 60L , TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));

该线程池的核心线程数为 0,线程池最有能纳 Integer. MAX_VALUE 个线程,且线程的空闲存活时间为 60s(可以理解为 okhttp 随时可以创建新的线程来满足需要。可以保证网络的 I/O 任务有线程来处理,不被阻塞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
...
val host: String
get() = originalRequest.url.host
val request: Request //url/method/headers/body
get() = originalReques
...
fun executeOn(executorService: ExecutorService) {
...
try {
executorService.execute(this)
success = true

2. List<Protocol> protocols :⽀持的应⽤层协议,即 HTTP/1.1、HTTP/2 等
3. Cache cache :Cache 存储的配置。默认是没有,如果需要⽤,得⾃⼰配置出 Cache 存储的⽂件位置以及存储空间上限。

4. boolean followRedirects :遇到重定向的要求是,是否⾃动 follow。根据响应码判断是否是重定向(3开头3xx)。RetryAndFollowUpInterceptor:如果不需要重定向,那么 followUp 为空,会释放资源,返回 response。若为重定向就销毁旧连接,创建新连接,将重定向操作得到的新请求设置给 request。统计重定向次数,不能大于20

5. 连接、读取、写入超时
6. ConnectionPool :连接池默认是可以保持5个空闲的连接。这些空闲的连接如果超过5分钟不被使用,则将被连接池移除。

Exchange ,单个 Http 请求,传输交换数据实现类,封装上面结果,为后续网络读写工作提供 API
ExchangeFinder ,请求连接获取,请求编解码实例构建
ExchangeCodec,Http 连接 I/O 操作上层封装类,实际 I/O 工作逻辑

前置
response = realChain.proceed (request) 中置,传给下一个
后置

java. net 包下
最后也是 socket:socket.connect (address, connectTimeout)

其他

复用机制
Http 中添加了一种 KeepAlive 机制,当数据传输完毕后仍然保持连接,等待下一次请求时直接复用该连接。
ConnectionPool :取到的话复用,没有取到放到连接池中。
ConnectionPool 关键代码:

okhttp 重试逻辑

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void onFailure(Call call, IOException e) {
if (e instanceof SocketTimeoutException && isRetry && retryCount < MAX_RETRY_COUNT) {
retryCount++;
call = client.newCall(call.request());
call.enqueue(this);
} else {
NetWorkResultEntity resultEntity = new NetWorkResultEntity("", null);
resultEntity.setErrorMessage(e.getMessage());
stringAsyncEmitter.onNext(resultEntity);
}
}

addInterceptor 和 addNetworkInterceptor

addInterceptor 在 addNetworklnterceptor 之前,addInterceptor 一般是为了业务,添加头之类的信息,而 addNetworkInterceptor0) 一般是做网络调试

addInterceptor(应用拦截器)在前:

  1. 不需要担心中间过程的响应,如重定向和重试.
  2. 总是只调用一次,即使HTTP响应是从缓存中获取.
    原始数据,⽽不是没有加 Content-Length 的请求数据,或者 Body还没有被 gzip 解压的响应数据
  3. 观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match.
  4. 允许短路而不调用 Chain.proceed(),即中止调用
  5. 允许重试,使 Chain.proceed()调用多次
    刚开始的时候

addNetworkInterceptor(网络拦截器):

  1. 能够操作中间过程的响应,如重定向和重试执行多次
  2. 当网络短路而返回缓存响应时不被调用.
  3. 只观察在网络上传输的数据.
  4. 携带请求来访问连接
    最后:做网络调试

自定义一个拦截器
addNetworkInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();

long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));

Response response = chain.proceed(request);

long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));

return response;
}
}

缓存

Okhttp 是通过 CacheInterceptor 进行 Cache,它负责网络交互相关。它里面包含了一些复杂的 CRUD逻辑.我们只需要知道如何进行 http 参数配置 ,何时禁止网络,只使用缓存,什么时候忽略网络数据,他的核心还是通过 DiskLruCache 实现了缓存在磁盘中的 LRU 存储,然后通过 Cache-Control 进行更好的 https 协议的缓存的 header。

OKHTTP一般控制缓存有两种方式:
1、在request里面去设置cacheControl()策略
2、在header里面去添加cache-control

Android okhttp缓存真正正确的实现方式
有网时不走缓存,没网时获取缓存,通过Cache-Control和max-age

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
public class HttpCacheInterceptor implements Interceptor {

@Overridepublic Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetWorkHelper.isNetConnected(MainApplication.getContext())) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}

Response response = chain.proceed(request);

if (NetWorkHelper.isNetConnected(MainApplication.getContext())) {
int maxAge = 60 * 60; // 如果想要不缓存,直接时间设置为0,但是需要保存下来吧
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else {
int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
return response;
}}

//设置缓存100M
Cache cache = new Cache(new File(MainApplication.getContext().getCacheDir(),"httpCache"),1024 * 1024 * 100);
return new OkHttpClient.Builder()
.cache(cache)
.addNetworkInterceptor(new HttpCacheInterceptor())
.build();

整体流程

unknown_filename.3|600

unknown_filename|600

Retrofit 原理

用法

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
public interface netApi {
@GET("repos/{owner}/{repo}/contributors")
Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner, @Path("repo") String repo);
}


public void retrofitHttpRequest() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();

netApi repo = retrofit.create(netApi.class);

retrofit2.Call<ResponseBody> call = repo.contributorsBySimpleGetCall("userName", "path");
call.enqueue(new retrofit2.Callback<ResponseBody>() {
@Override
public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
//response
}

@Override
public void onFailure(retrofit2.Call<ResponseBody> call, Throwable t) {

}
});
}
}

源码

  • Retrofit 底层依赖OkHttp实现,也就是说Retrofit本质上就是对OkHttp的更进一步封装,还支持Rxjava。Retrofit和其它Http库最大区别在于使用注解简化Http请求(请求方式、请求参数)
1. Retrofit 对象创建:配置 “翻译规则”
1
2
3
4
5
6
7
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient()) // 底层请求引擎
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 响应适配器
.addConverterFactory(GsonConverterFactory.create()) // 数据转换器
.baseUrl("https://api.example.com/") // 基础URL
.build();

  • CallAdapterFactory:负责将 OkHttp 的 Call 转换为 RxJava 的 Observable、Flowable 等异步类型;
  • ConverterFactory:定义数据转换规则(如 Gson 将 JSON 转为对象);
  • OkHttpClient:实际执行 HTTP 请求的底层引擎
2. 动态代理:接口的 “魔法翻译”

当调用 retrofit.create(InfoApi.class) 时,Retrofit 通过 Java 动态代理生成接口的代理对象:这个过程构建了一个 ServiceMethod 对象。(扩展 apiservice 的功能)

1
2
3
4
5
6
7
8
9
10
11
12
13
public <T> T create(final Class<T> service) {
// 生成代理对象,拦截接口方法调用
return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
(proxy, method, args) -> {
// 解析方法注解(@GET、@Query等)
ServiceMethod serviceMethod = loadServiceMethod(method);
// 生成OkHttpCall对象
OkHttpCall okHttpCall = new OkHttpCall(serviceMethod, args);
// 通过CallAdapter转换为目标类型(如Observable)
return serviceMethod.adapt(okHttpCall);
});
}

关键步骤

  • ServiceMethod:解析方法注解,生成请求参数(URL、请求方式、参数);

  • OkHttpCall:封装 OkHttp 的 Call,处理请求发送;

  • 适配器模式:通过 CallAdapter 将 OkHttp 的 Call 转换为 RxJava 的 Observable。

  • 缓存:为了提高性能,解析过的 ServiceMethod 会被缓存起来,下次调用同一方法时直接使用。

  • loadServiceMethod (method) 方法:通过 Method. getAnnotations (); 是获取方法上面对应的注解。method. getParameterAnnotations (),解析注解获取请求方式,参数类型和参数注解拼接请求的链接,当一切都准备好之后会把数据添加到 Retrofit 的 RequestBuilder 中。 最终,一个 ServiceMethod 对象包含了创建一个 HTTP 请求所需的所有信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    private ServiceMethod<?> loadServiceMethod(Method method) {
    // 获取方法上的注解
    Annotation[] annotations = method.getAnnotations();

    // 创建 ServiceMethod.Builder 实例
    ServiceMethod.Builder<?> builder = new ServiceMethod.Builder<>(this, method);

    // 解析方法上的注解,构建 ServiceMethod.Builder 对象
    builder.parseAnnotations(annotations);

    // 解析方法的返回类型,获取适当的 CallAdapter 对象
    CallAdapter<?> callAdapter = builder.createCallAdapter();

    // 解析方法的返回类型,获取对应的 Type 对象
    Type responseType = builder.parseResponseType();

    // 构建 ServiceMethod 对象
    return builder.build(callAdapter, responseType);
    }
  • 其实 Retrofit 内部是通过动态代理,内部是通过反射实现的,大家都知道反射其实是很消耗性能的,为了避免这种高性能的消耗,反射成功以后就缓存起来,下次如果有用到就重用,如果不能重用则用反射构建出来,但是 Builder 是不能重用的。

    1
    2
    3
    4
    5
    6
    7
    Builder(Retrofit retrofit, Method method) {
    this.retrofit = retrofit;
    this.method = method;
    this.methodAnnotations = method.getAnnotations();
    this.parameterTypes = method.getGenericParameterTypes();
    this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
3. 请求发送:从接口到 HTTP 的 “翻译过程”
  • 然后当我们主动发起网络请求的时候会调用 okhttp 发起网络请求,okhttp 的配置包括请求方式,URL 等在 Retrofit 的 RequestBuilder 的 build () 方法中实现,并发起真正的网络请求。
    1
    new OkHttpCall<>(requestFactory, args, callFactory,responseConverter)
    调用接口方法
    • Call<User> call = service.getUser(123);
    • 触发动态代理的 invoke 方法。

unknown_filename.5

Method.getAnnotations(); 是获取方法上面对应的注解。
method.getParameterAnnotations();获取的是方法参数上面的注解,是一个二维数组,第一个维度代表的是方法参数对应的下标,比如,一个方法有3个参数,那0代表第一个参数,1代表第二个参数,2代表第三个参数。
method.getGenericParameterTypes();获取的是方法参数的类型,里面带有实际的参数类型。

1.Retrofit构建过程
建造者模式、工厂方法模式

2.创建网络请求接口实例过程
外观模式、代理模式、单例模式、策略模式、装饰模式(建造者模式)

3.生成并执行请求过程
适配器模式(代理模式、装饰模式)

自己写网络请求框架

  • volley,okHttp等,这类优秀的框架其底层的实现大部分也是基于系统的 线程池 和 httpClient 或 HttpUrlConnection的网络请求类框架,Android中是不能在主线程中(又称UI线程)进行网络操作的,那么框架中必不可少地要使用到子线程,可以使用简单的 Thread + Runnable + Handler或者重量级点的AsyncTask。
  • 处理好并发操作,一个应用中往往要进行多线程操作,而Java虚拟机对于一个线程的内存分配大约在1M左右,具体多少要看它执行的任务而定。所有就要使用线程池,例如newFixdThreadPool 可以控制并发数量,且在整个APP运行过程中有几个常驻线程在,避免使用时反复地new,退出时再销毁,而 newCacheThreadPool 则会在任务完成后,自动回收线程,它会帮你释放线程内存,也就不会有常驻线程。
  • 还要注意使接口分离,降低耦合,而且接口能够我们带来很大的方便。

应用中的网络层是怎么设计的
用现成的一些框架,然后根据项目需要自己再封装下,比如说你的交互数据是JSON格式的,你就可以用一个网络请求框架+gson,然后写一些Bean 在Work(主)线程把数据用gson直接解析成对象返回,最后对一些错误统一处理。我用的是(volley),封装了下常用的方法,get post 上传 下载 ,所有的请求我都是用的同步请求. 具体的用法一般都是和业务逻辑在一起,而我的业务逻辑是用异步去处理的

volley

为什么说Volley适合数据量小,通信频繁的网络操作

volley中为了提高请求处理的速度,采用了ByteArrayPool进行内存中的数据存储的,如果下载大量的数据,这个存储空间就会溢出,所以不适合大量的数据,但是由于他的这个存储空间是内存中分配的,当存储的时候优是从ByteArrayPool中取出一块已经分配的内存区域, 不必每次存数据都要进行内存分配,而是先查找缓冲池中有无适合的内存区域,如果有,直接拿来用,从而减少内存分配的次数 ,所以他比较适合据量小,通信量大网络数据交互情况。
用法:

  1. 创建一个RequestQueue对象。
  2. 创建一个StringRequest对象。
  3. 将StringRequest对象添加到RequestQueue里面。

主线程中调用RequestQueue的add()方法来添加一条网络请求,这条请求会先被加入到缓存队列当中,如果发现可以找到相应的缓存结果就直接读取缓存并解析,然后回调给主线程。如果在缓存中没有找到结果,则将这条请求加入到网络请求队列中,然后处理发送HTTP请求,解析响应结果,写入缓存,并回调主线程。

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
//volley第一步
RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
private void volleyStringRequest() {

//volley第二步
StringRequest stringRequest = new StringRequest("http://www.baidu.com",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("TAG", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
//volley第三步
mQueue.add(stringRequest);
}

private void volleyJsonRequest() {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://www.sina.com/sports/101010100.html", null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d("TAG", response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
mQueue.add(jsonObjectRequest);
}


网络库源码
http://peiniwan.github.io/2025/12/6700cce7f7bf.html
作者
六月的雨
发布于
2025年12月16日
许可协议