带你学开源项目:OkHttp-- 自己动手实现 okhttp

最新推荐文章于 2023-01-04 16:42:44 发布
安卓工匠 最新推荐文章于 2023-01-04 16:42:44 发布
阅读量599 收藏 2
点赞数
分类专栏: 架构设计 文章标签: 开源项目
架构设计 专栏收录该内容
8 篇文章
订阅专栏
本文详细剖析了OkHttp网络请求库的设计理念与实现细节,包括请求处理流程、重定向机制及拦截器等核心功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

私以为,阅读开源项目是与世界级技术大牛直接对话的最好方式。
此次来分享下 OkHttp 源码的分析。

一、开源项目 OkHttp

在 Android、Java 开发领域中,相信大家都听过或者在使用 Square 家大名鼎鼎的网络请求库——OkHttp——https://github.com/square/okhttp ,当前多数著名的开源项目如 Fresco、Glide、 Picasso、 Retrofit都在使用 OkHttp,这足以说明其质量,而且该项目仍处在 不断维护中。

二、问题

在分析 okhttp 源码之前,我想先提出一个问题,如果我们自己来设计一个网络请求库,这个库应该长什么样子?大致是什么结构呢?

下面我和大家一起来构建一个网络请求库,并在其中融入 okhttp 中核心的设计思想,希望借此让读者感受并学习到 okhttp 中的精华之处,而非仅限于了解其实现。

笔者相信,如果你能耐心阅读完本篇,不仅能对 http 协议有进一步理解,更能够学习到世界级项目的思维精华,提高自身思维方式。

三、思考

首先,我们假设要构建的的网络请求库叫做WingjayHttpClient,那么,作为一个网络请求库,它最基本功能是什么呢?

在我看来应该是:接收用户的请求 -> 发出请求 -> 接收响应结果并返回给用户。

那么从使用者角度而言,需要做的事是:

  1. 创建一个Request:在里面设置好目标 URL;请求 method 如 GET/POST 等;一些 header 如 Host、User-Agent 等;如果你在 POST 上传一个表单,那么还需要 body。
  2. 将创建好的 Request 传递给WingjayHttpClient。
  3. WingjayHttpClient去执行 Request,并把返回结果封装成一个Response 给用户。而一个 Response 里应该包括 statusCode 如 200,一些 header 如 content-type 等,可能还有 body

到此即为一次完整请求的雏形。那么下面我们来具体实现这三步。

四、雏形实现

下面我们先来实现一个 httpClient 的雏形,只具备最基本的功能。

1. 创建 Request 类

首先,我们要建立一个 Request 类,利用 Request 类用户可以把自己需要的参数传入进去,基本形式如下:

      
1
2
3
4
5
6
7
8
9
10
11
      
class Request {
String url;
String method;
Headers headers;
Body requestBody;
public Request(String url, String method, @Nullable Headers headers, @Nullable Body body) {
this.url = url;
...
}
}

2. 将 Request 对象传递给 WingjayHttpClient

我们可以设计 WingjayHttpClient 如下:

      
1
2
3
4
5
      
class WingjayHttpClient {
public Response sendRequest(Request request) {
return executeRequest(request);
}
}

3. 执行 Request,并把返回结果封装成一个Response 返回
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
      
class WingjayHttpClient {
...
private Response executeRequest(Request request) {
// 使用 socket 来进行访问
Socket socket = new Socket(request.getUrl(), 80);
ResponseData data = socket.connect().getResponseData();
return new Response(data);
}
...
}
class Response {
int statusCode;
Headers headers;
Body responseBody
...
}

五、功能扩展

利用上面的雏形,可以得到其使用方法如下:

      
1
2
3
4
      
Request request = new Request("http://wingjay.com");
WingjayHttpClient client = new WingjayHttpClient();
Response response = client.sendRequest(request);
handle(response);

然而,上面的雏形是远远不能胜任常规的应用需求的,因此,下面再来对它添加一些常用的功能模块。

1. 重新把简陋的 user Request 组装成一个规范的 http request

一般的 request 中,往往用户只会指定一个 URL 和 method,这个简单的 user request 是不足以成为一个 http request,我们还需要为它添加一些 header,如 Content-Length, Transfer-Encoding, User-Agent, Host, Connection, 和 Content-Type,如果这个 request 使用了 cookie,那我们还要将 cookie 添加到这个 request 中。

我们可以扩展上面的 sendRequest(request) 方法:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
      
[class WingjayHttpClient]
public Response sendRequest(Request userRequest) {
Request httpRequest = expandHeaders(userRequest);
return executeRequest(httpRequest);
}
private Request expandHeaders(Request userRequest) {
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
...
}

2. 支持自动重定向

有时我们请求的 URL 已经被移走了,此时 server 会返回 301 状态码和一个重定向的新 URL,此时我们要能够支持自动访问新 URL 而不是向用户报错。

对于重定向这里有一个测试性 URL:http://www.publicobject.com/helloworld.txt ,通过访问并抓包,可以看到如下信息:

因此,我们在接收到 Response 后要根据 status_code 是否为重定向,如果是,则要从 Response Header 里解析出新的 URL-Location并自动请求新 URL。那么,我们可以继续改写 sendRequest(request) 方法:

      
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
      
[class WingjayHttpClient]
private boolean allowRedirect = true;
// user can set redirect status when building WingjayHttpClient
public void setAllowRedirect(boolean allowRedirect) {
this.allowRedirect = allowRedirect;
}
public Response sendRequest(Request userRequest) {
Request httpRequest = expandHeaders(userRequest);
Response response = executeRequest(httpRequest);
switch (response.statusCode()) {
// 300: multi choice; 301: moven permanently;
// 302: moved temporarily; 303: see other;
// 307: redirect temporarily; 308: redirect permanently
case 300:
case 301:
case 302:
case 303:
case 307:
case 308:
return handleRedirect(response);
default:
return response;
}
}
// the max times of followup request
private static final int MAX_FOLLOW_UPS = 20;
private int followupCount = 0;
private Response handleRedirect(Response response) {
// Does the WingjayHttpClient allow redirect?
if (!client.allowRedirect()) {
return null;
}
// Get the redirecting url
String nextUrl = response.header( “Location”);
// Construct a redirecting request
Request followup = new Request(nextUrl);
// check the max followupCount
if (++followupCount > MAX_FOLLOW_UPS) {
throw new Exception( “Too many follow-up requests:” + followUpCount);
}
// not reach the max followup times, send followup request then.
return sendRequest(followup);
}

利用上面的代码,我们通过获取原始 userRequest 的返回结果,判断结果是否为重定向,并做出自动 followup 处理。

一些常用的状态码
100~199:指示信息,表示请求已接收,继续处理
200~299:请求成功,表示请求已被成功接收、理解、接受
300~399:重定向,要完成请求必须进行更进一步的操作
400~499:客户端错误,请求有语法错误或请求无法实现
500~599:服务器端错误,服务器未能实现合法的请求

3. 支持重试机制

所谓重试,和重定向非常类似,即通过判断 Response 状态,如果连接服务器失败等,那么可以尝试获取一个新的路径进行重新连接,大致的实现和重定向非常类似,此不赘述。

4. Request & Response 拦截机制

这是非常核心的部分。

通过上面的重新组装 request 和重定向机制,我们可以感受的,一个 request 从 user 创建出来后,会经过层层处理后,才真正发出去,而一个response,也会经过各种处理,最终返回给用户。

笔者认为这和网络协议栈非常相似,用户在应用层发出简单的数据,然后经过传输层、网络层等,层层封装后真正把请求从物理层发出去,当请求结果回来后又层层解析,最终把最直接的结果返回给用户使用。

最重要的是,每一层都是抽象的,互不相关的!

因此在我们设计时,也可以借鉴这个思想,通过设置 拦截器 Interceptor,每个拦截器会做两件事情:

  1. 接收上一层拦截器封装后的 request,然后自身对这个 request 进行处理,例如添加一些 header,处理后向下传递;
  2. 接收下一层拦截器传递回来的 response,然后自身对 response 进行处理,例如判断返回的 statusCode,然后进一步处理。

那么,我们可以为拦截器定义一个抽象接口,然后去实现具体的拦截器。

      
1
2
3
      
interface Interceptor {
Response intercept(Request request);
}

大家可以看下上面这个拦截器设计是否有问题?

我们想象这个拦截器能够接收一个 request,进行拦截处理,并返回结果。

但实际上,它无法返回结果,而且它在处理 request 后,并不能继续向下传递,因为它并不知道下一个 Interceptor 在哪里,也就无法继续向下传递。

那么,如何解决才能把所有 Interceptor 串在一起,并能够依次传递下去。

      
1
2
3
4
5
6
7
8
9
      
public interface Interceptor {
Response intercept(Chain chain);
interface Chain {
Request request();
Response proceed(Request request);
}
}

使用方法如下:假如我们现在有三个 Interceptor 需要依次拦截:

      
1
2
3
4
5
6
7
8
9
      
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.add( new MyInterceptor1());
interceptors.add( new MyInterceptor2());
interceptors.add( new MyInterceptor3());
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, 0, originalRequest);
chain.proceed(originalRequest);

里面的 RealInterceptorChain 的基本思想是:我们把所有 interceptors 传进去,然后 chain 去依次把 request 传入到每一个 interceptors 进行拦截即可。

通过下面的示意图可以明确看出拦截流程:

其中,RetryAndFollowupInterceptor是用来做自动重试和自动重定向的拦截器;BridgeInterceptor是用来扩展 request 的header的拦截器。这两个拦截器存在于 okhttp 里,实际上在 okhttp 里还有好几个拦截器,这里暂时不做深入分析。

  1. CacheInterceptor
    这是用来拦截请求并提供缓存的,当 request 进入这一层,它会自动去检查缓存,如果有,就直接返回缓存结果;否则的话才将 request 继续向下传递。而且,当下层把 response 返回到这一层,它会根据需求进行缓存处理;

  2. ConnectInterceptor
    这一层是用来与目标服务器建立连接

  3. CallServerInterceptor
    这一层位于最底层,直接向服务器发出请求,并接收服务器返回的 response,并向上层层传递。

上面几个都是 okhttp 自带的,也就是说需要在 WingjayHttpClient 自己实现的。除了这几个功能性的拦截器,我们还要支持用户 自定义拦截器,主要有以下两种(见图中非虚线框蓝色字部分):

  1. interceptors
    这里的拦截器是拦截用户最原始的 request。

  2. NetworkInterceptor
    这是最底层的 request 拦截器。

如何区分这两个呢?举个例子,我创建两个 LoggingInterceptor,分别放在interceptors 层和 NetworkInterceptor 层,然后访问一个会重定向的 URL_1,当访问完URL_1 后会再去访问重定向后的新地址 URL_2。对于这个过程,interceptors 层的拦截器只会拦截到 URL_1 的 request,而在 NetworkInterceptor 层的拦截器则会同时拦截到 URL_1 和URL_2两个 request。具体原因可以看上面的图。

5. 同步、异步 Request 池管理机制

这是非常核心的部分。

通过上面的工作,我们修改 WingjayHttpClient 后得到了下面的样子:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
      
class WingjayHttpClient {
public Response sendRequest(Request userRequest) {
Request httpRequest = expandHeaders(userRequest);
Response response = executeRequest(httpRequest);
switch (response.statusCode()) {
// 300: multi choice; 301: moven permanently;
// 302: moved temporarily; 303: see other;
// 307: redirect temporarily; 308: redirect permanently
case 300:
case 301:
case 302:
case 303:
case 307:
case 308:
return handleRedirect(response);
default:
return response;
}
}
private Request expandHeaders(Request userRequest) {...}
private Response executeRequest(Request httpRequest) {...}
private Response handleRedirect(Response response) {...}
}

也就是说,WingjayHttpClient现在能够 同步 地处理单个 Request 了。

然而,在实际应用中,一个 WingjayHttpClient 可能会被用于同时处理几十个用户 request,而且这些 request 里还分成了 同步 和异步 两种不同的请求方式,所以我们显然不能简单把一个 request 直接塞给WingjayHttpClient。

我们知道,一个 request 除了上面定义的 http 协议相关的内容,还应该要设置其处理方式 同步 和异步。那这些信息应该存在哪里呢?两种选择:

  1. 直接放入 Request
    从理论上来讲是可以的,但是却违背了初衷。我们最开始是希望用 Request 来构造符合 http 协议的一个请求,里面应该包含的是请求目标网址 URL,请求端口,请求方法等等信息,而 http 协议是不关心这个 request 是同步还是异步之类的信息

  2. 创建一个类,专门来管理 Request 的状态
    这是更为合适的,我们可以更好的拆分职责。

因此,这里选择创建两个类 SyncCall 和AsyncCall,用来区分 同步 和异步。

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
      
class SyncCall {
private Request userRequest;
public SyncCall(Request userRequest) {
this.userRequest = userRequest;
}
}
class AsyncCall {
private Request userRequest;
private Callback callback;
public AsyncCall(Request userRequest, Callback callback) {
this.userRequest = userRequest;
this.callback = callback;
}
interface Callback {
void onFailure(Call call, IOException e);
void onResponse(Call call, Response response) throws IOException;
}
}

基于上面两个类,我们的使用场景如下:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
      
WingjayHttpClient client = new WingjayHttpClient();
// Sync
Request syncRequest = new Request( "http://wingjay.com");
SyncCall syncCall = new SyncCall(request);
Response response = client.sendSyncCall(syncCall);
handle(response);
// Async
AsyncCall asyncCall = new AsyncCall(request, new CallBack() {
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {
handle(response);
}
});
client.equeueAsyncCall(asyncCall);

从上面的代码可以看到,WingjayHttpClient的职责发生了变化:以前是response = client.sendRequest(request);,而现在变成了

      
1
2
3
      
response = client.sendSyncCall(syncCall);
client.equeueAsyncCall(asyncCall);

那么,我们也需要对 WingjayHttpClient 进行改造,基本思路是在内部添加 请求池 来对所有 request 进行管理。那么这个 请求池 我们怎么来设计呢?有两个方法:

  1. 直接在 WingjayHttpClient 内部创建几个容器
    同样,从理论上而言是可行的。当用户把(a)syncCall 传给 client 后,client 自动把 call 存入对应的容器进行管理。

  2. 创建一个独立的类进行管理
    显然这样可以更好的分配职责。我们把 WingjayHttpClient 的职责定义为,接收一个 call,内部进行处理后返回结果。这就是 WingjayHttpClient 的任务,那么具体如何去管理这些 request 的执行顺序和生命周期,自然不需要由它来管。

因此,我们创建一个新的类:Dispatcher,这个类的作用是:

  1. 存储外界不断传入的 SyncCall 和AsyncCall,如果用户想取消则可以遍历所有的 call 进行 cancel 操作;
  2. 对于 SyncCall,由于它是即时运行的,因此Dispatcher 只需要在 SyncCall 运行前存储进来,在运行结束后移除即可;
  3. 对于 AsyncCall,Dispatcher 首先启动一个 ExecutorService,不断取出 AsyncCall 去进行执行,然后,我们设置最多执行的 request 数量为 64,如果已经有 64 个 request 在执行中,那么就将这个 asyncCall 存入等待区。

根据设计可以得到 Dispatcher 构造:

      
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
      
class Dispatcher {
// sync call
private final Deque<SyncCall> runningSyncCalls = new ArrayDeque<>();
// async call
private int maxRequests = 64;
private final Deque<AsyncCall> waitingAsyncCalls = new ArrayDeque<>();
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
private ExecutorService executorService;
// begin execute Sync call
public void startSyncCall(SyncCall syncCall) {
runningSyncCalls.add(syncCall);
}
// finish Sync call
public void finishSyncCall(SyncCall syncCall) {
runningSyncCalls.remove(syncCall);
}
// enqueue a new AsyncCall
public void enqueue(AsyncCall asyncCall) {
if (runningAsyncCalls.size() < 64) {
// run directly
runningAsyncCalls.add(asyncCall);
executorService.execute(asyncCall);
} else {
readyAsyncCalls.add(asyncCall);
}
}
// finish a AsyncCall
public void finishAsyncCall(AsyncCall asyncCall) {
runningAsyncCalls.remove(asyncCall);
}
}

有了这个 Dispatcher,那我们就可以去修改WingjayHttpClient 以实现

      
1
2
3
      
response = client.sendSyncCall(syncCall);
client.equeueAsyncCall(asyncCall);

这两个方法了。具体实现如下

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
      
[class WingjayHttpClient]
private Dispatcher dispatcher;
public Response sendSyncCall(SyncCall syncCall) {
try {
// store syncCall into dispatcher;
dispatcher.startSyncCall(syncCall);
// execute
return sendRequest(syncCall.getRequest());
} finally {
// remove syncCall from dispatcher
dispatcher.finishSyncCall(syncCall);
}
}
public void equeueAsyncCall(AsyncCall asyncCall) {
// store asyncCall into dispatcher;
dispatcher.enqueue(asyncCall);
// it will be removed when this asyncCall be executed
}

基于以上,我们能够很好的处理 同步 和异步 两种请求,使用场景如下:

      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
      
WingjayHttpClient client = new WingjayHttpClient();
// Sync
Request syncRequest = new Request( "http://wingjay.com");
SyncCall syncCall = new SyncCall(request);
Response response = client.sendSyncCall(syncCall);
handle(response);
// Async
AsyncCall asyncCall = new AsyncCall(request, new CallBack() {
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {
handle(response);
}
});
client.equeueAsyncCall(asyncCall);

六、总结

到此,我们基本把 okhttp 里核心的机制都讲解了一遍,相信读者对于 okhttp 的整体结构和核心机制都有了较为详细的了解。

如果有问题欢迎联系我。

谢谢!

wingjay

欢迎各位关注
我的 Github: https://github.com/wingjay
和
我的简书 : http://www.jianshu.com/users/da333fd63fe5/latest_articles
和
微博 iam_wingjay: http://weibo.com/u/1625892654
如果有问题,可以给我留言或发邮件yinjiesh@126.com

#Android #带你学开源项目

相关文章:

  • RxLifecycle 源码解析-当 Activity 被 destory 时自动停掉网络请求
  • 带你学开源项目:LeakCanary- 如何检测 Activity 是否泄漏
  • 带你学开源项目:Meizhi Android 之 RxJava & Retrofit 最佳实践
  • 30 条 Android 开发建议
  • 一种快速毛玻璃虚化效果实现
  • [干货] Glow Android 优化实践
  • 让普通 Java 类自动感知 Activity Lifecycle
  • 谈谈移动应用的安全性实践
RxLifecycle 源码解析-当 Activity 被 destory 时自动停掉网络请求
谈谈移动应用的安全性实践

评论

本站评论搭建在 Github Issue 上(就是这么神奇),请点击下面按钮进行评论。
Comment on Github Issue for this article(Yup I know it’s cool)! click below button!

评论
function loadComments(data) { var commentUrl = "window.open('https://github.com/wingjay/wingjay.github.io/issues/14')" $("#comments").append('
评论
') for (var i=0; i
确定要放弃本次机会?
福利倒计时
: :

立减 ¥

普通VIP年卡可用
立即使用
安卓工匠
关注 关注
  • 0
    点赞
  • 踩
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
  • 分享
    复制链接
    分享到 QQ
    分享到新浪微博
    扫一扫
  • 举报
    举报
专栏目录
OkHttp的实现
05-06
OkHttp的Get-Post网络请求,通过OkHttp实现文件的上传和断点续传
工具包有:okhttp-3.2.0.jar, okio-1.14.0.jar
09-20
使用OkHttp-3.2.0和Okio-1.14.0进行网络编程时,开发者需要注意以下几点: 1. **连接池管理**:OkHttp维护了一个连接池,可以复用已建立的TCP连接,减少因创建新连接带来的开销。 2. **缓存机制**:OkHttp支持本地...
参与评论 您还未登录,请先 登录 后发表或查看评论
带你学开源项目:OkHttp--自己动手实现OkHttp | wingjay
wingjay的博客
07-21 449
版权声明:本文原创发布于公众号 wingjay,转载请务必注明出处! https://www.jianshu.com/p/27bf1057f5aa 大幅提高自身技术实力最有效的途径之一就是学习世界级优秀开源项目的精髓,而本人的《带你学开源项目》系列文章将持续更新,对当前Android开发界最优秀的开源项目进行深入分析。 一、开源项目 OkHttp 在An...
带你学开源项目:OkHttp--自己动手实现okhttp
chengshuyuan_uestc的专栏
08-07 567
一、开源项目 OkHttp在Android、Java开发领域中,相信大家都听过或者在使用Square家大名鼎鼎的网络请求库——OkHttp——https://github.com/square/okhttp ,当前多数著名的开源项目如 Fresco、Glide、 Picasso、 Retrofit都在使用OkHttp,这足以说明其质量,而且该项目仍处在不断维护中。二、问题在分析okhttp源码之前,
腾讯T3大牛亲自讲解!自己动手实现OkHttp,吐血整理
clhcowboy的博客
02-19 306
背景 惯例,先简单陈述一下自己的,91年生人,164年三本毕业后在深圳工作,末流小公司,工资13k,无房,无车,无户口。 那时候感觉生活也还行,父母有退休金,我基本上不用太操心,女朋友在一起很久了,很体贴,没有怎么要求我。 本来生活就这样一帆风顺下去我就满足了,但是去年初,女朋友家里出了一些事情,一点积蓄全给她了,后面疫情来了,家里开始催婚了,我感觉到了压力。 目前的工资无法满足生活,虽然这些年来有一点点的提升,但是,房价物价涨的更快,于是我决定跳槽。 从去年年底开始瞎投简历,回顾了一下,一共投了33份简历
android之http工具: okhttp-master
03-26
本文将深入探讨OkHttp的基础知识、特性以及如何在Android项目中使用它。 OkHttp是由Square公司开发的一款开源HTTP客户端,其设计目标是提供更快速、更稳定、更省电的网络通信体验。OkHttp通过减少网络延迟、优化...
okhttp-3.4.1.jar
02-07
下载okhttp-3.4.1.jar之后,可以在Java或Android项目中将其添加到项目的构建路径中,然后即可使用okhttp提供的API进行网络通信。使用okhttp不需要进行复杂的配置,大多数情况下,直接使用默认配置即可满足需求。例如...
okhttp-3.8.1.jar中文文档.zip
最新发布
05-11
jar中文文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可...
掌握高效网络请求:okhttp-3.8.1核心功能解析
根据提供的文件信息,我们可以生成关于“okhttp-3.8.1.zip”压缩包的知识点,这个压缩包内包含okio和okhttp两个重要的Java网络通信库的版本。 ### 知识点一:OkHttp网络请求库概述 OkHttp是一个在Java和Kotlin中...
okhttp服务器测试接口项目
06-09
可供测试Post提交文件和post提交普通键值对,当然get也是可以的
Android-用kotlinokhttp3写的一个小小的新闻客户端项目
08-13
用kotlin okhttp3写的一个小小的新闻客户端项目
OkHttp学习(八) 手写OkHttp之网络请求实现
吓成一坨兔子
01-03 697
OkHttpClient2 public class OkHttpClient2 { Dispatcher2 dispatcher; boolean isCanceled; int recount; public boolean getCanceled() { return isCanceled; } public int ...
Android 项目总结(网络框架OKhttp)
轻描时光
01-15 840
Android 项目总结(网络框架OKhttp)
Android之OkHttp详解(非原创)
weixin_30247159的博客
04-03 410
文章大纲 一、OkHttp简介二、OkHttp简单使用三、OkHttp封装四、项目源码下载 一、OkHttp简介 1. 什么是OkHttp   一般在Java平台上,我们会使用Apache HttpClient作为Http客户端,用于发送 HTTP 请求,并对响应进行处理。比如可以使用http客户端与第三方服务(如SSO服务)进行集成,...
OkHttp3开发
Android小码农的博客
01-05 7596
OkHttp3开发三部曲: 1、创建OkHttpClient,添加配置 2、创建请求 3、执行请求 下面分别来说说这三个步骤: 一、创建OkHttpClient 一个最简单的OkHttpClient [java] view plain copy OkHttpClient okHttpClient=new OkHttpClient.Builder().bui
okhttp大概流程
zouwei_199388的博客
06-10 238
OkHttp网络请求流程浅析,纯个人学习记录。如有错误,希望大家指正。 实例化OkHttpClient 使用okhttp请求网络,首先得实例化一个OkHtppClient对象。 OkHttpClient对象是通过OkHttpClient中的构造器对象Builder创建。所以先实例化OkHttpClient中的Builder类。 OkHttpClient client; OkHttp...
okHttp各个module介绍
璐豪笔记
06-13 1394
上一篇我们简单介绍了okHttp,了解了OkHttp的基本用法,并下载了okhttp的源码。本篇我们将一起来看看okhttp源码的目录结构,来大概了解下okHttp源码下的各个module的作用。
OKHttp库入门到精通 - 1(全程实战演示,完整Android代码示例,持续更新)
yinminsumeng的博客
01-04 517
在Android开发中有很多三方库帮助我们提升开发效率,把项目中常用的库进行总结整理,便于项目中的开发同学学习参考。这些总结从基本用法切入,逐步扩展到高级工和原理分析,从满足基本的工作需要,到通过优秀代码学习、提升个人能力。
安卓工匠

博客等级

码龄14年
112
原创
240
点赞
626
收藏
124
粉丝
关注
私信

TA的精选

  • 新 AndroidStudio使用Maven私服二:在gradle中引入Maven私服

    1260 阅读

  • 新 Hbuilder增加vuejs、angularjs代码提示

    2030 阅读

  • 热 Android 调用so库全过程

    69759 阅读

  • 热 Linux新手入门:Unable to locate package错误解决办法

    36993 阅读

  • 热 Android Layout 布局属性全解

    27271 阅读

查看更多

2020年1篇
2019年84篇
2018年139篇
2017年78篇

大家在看

  • 爆款分享:AI提示工程版本控制的10个实用技巧 239
  • java-springboot中小型连锁超市智能化管理系统 Java Spring Boot 框架下的中小连锁超市智能运营系统基于 Java Spring Boot 的中小连锁超市智慧计算机毕业设计 836
  • 数据中台建设中的治理工具链 420
  • C语言运算符全解析
  • 第十九讲:C++11第一部分

分类专栏

  • 笔记
  • maven
    3篇
  • AndroidStudio中使用Nenus私服
    1篇
  • java
    4篇
  • ngnix
    12篇
  • idea
  • mybatis
    2篇
  • 日志log4j
    1篇
  • ubuntu
    22篇
  • springMVC
    1篇
  • FastDFS分布式文件服务器
    3篇
  • 图片服务器
    10篇
  • vsftp服务器
    5篇
  • Markdown
    1篇
  • VM虚拟机
    2篇
  • spring
    2篇
  • javaScript学习
    1篇
  • tomcat
    1篇
  • dubbo
    1篇
  • idea发布应用
    1篇
  • idean
  • 算法学习
    3篇
  • 安卓基础
    41篇
  • 学习基础语言
    2篇
  • 程序员的自我修养
    4篇
  • java基础
    13篇
  • 安卓JNI开发
    7篇
  • 开发规范
    1篇
  • java链式开发
    7篇
  • 安卓UI界面
    40篇
  • 开发工具
    9篇
  • 安卓性能优化
    1篇
  • 安卓图像处理
    13篇
  • android-studio
    4篇
  • c语言基础
    10篇
  • 安卓进阶
    9篇
  • Java进阶
    7篇
  • c++
    4篇
  • jni开发
    2篇
  • ffmpeg
    7篇
  • 安卓开源框架
    6篇
  • 安卓优化
    4篇
  • 安卓单元测试
    1篇
  • davilk虚拟机
    2篇
  • 设计模式
    2篇
  • 多线程
    5篇
  • 架构设计
    8篇
  • 音视频开发—x264
    3篇
  • 网络
    12篇
  • rtmp
    1篇
  • meteral design
    9篇
  • 安卓框架组件
    7篇
  • 版本适配
    1篇
  • 自定义控件
    4篇
  • 自定义控件基础知识
    3篇
  • 安卓系统跟进
    1篇
  • 安卓下载资源
  • 数据结构
    1篇
  • 版本管理
    10篇
  • 微信
    1篇
  • 开发技巧
    2篇
  • 安卓网络开发
    5篇
  • 前端
    7篇
  • 直播
    2篇
  • 安卓最基础知识
    3篇
  • 算法
    1篇
  • Retrofit
    1篇
  • RxJava
    1篇
  • http
    1篇
  • 数据库
    2篇

展开全部 收起

上一篇:
终于搞懂了回车与换行的区别
下一篇:
FFmpeg视音频同步简单介绍

目录

展开全部

收起

目录

展开全部

收起

上一篇:
终于搞懂了回车与换行的区别
下一篇:
FFmpeg视音频同步简单介绍

分类专栏

  • 笔记
  • maven
    3篇
  • AndroidStudio中使用Nenus私服
    1篇
  • java
    4篇
  • ngnix
    12篇
  • idea
  • mybatis
    2篇
  • 日志log4j
    1篇
  • ubuntu
    22篇
  • springMVC
    1篇
  • FastDFS分布式文件服务器
    3篇
  • 图片服务器
    10篇
  • vsftp服务器
    5篇
  • Markdown
    1篇
  • VM虚拟机
    2篇
  • spring
    2篇
  • javaScript学习
    1篇
  • tomcat
    1篇
  • dubbo
    1篇
  • idea发布应用
    1篇
  • idean
  • 算法学习
    3篇
  • 安卓基础
    41篇
  • 学习基础语言
    2篇
  • 程序员的自我修养
    4篇
  • java基础
    13篇
  • 安卓JNI开发
    7篇
  • 开发规范
    1篇
  • java链式开发
    7篇
  • 安卓UI界面
    40篇
  • 开发工具
    9篇
  • 安卓性能优化
    1篇
  • 安卓图像处理
    13篇
  • android-studio
    4篇
  • c语言基础
    10篇
  • 安卓进阶
    9篇
  • Java进阶
    7篇
  • c++
    4篇
  • jni开发
    2篇
  • ffmpeg
    7篇
  • 安卓开源框架
    6篇
  • 安卓优化
    4篇
  • 安卓单元测试
    1篇
  • davilk虚拟机
    2篇
  • 设计模式
    2篇
  • 多线程
    5篇
  • 架构设计
    8篇
  • 音视频开发—x264
    3篇
  • 网络
    12篇
  • rtmp
    1篇
  • meteral design
    9篇
  • 安卓框架组件
    7篇
  • 版本适配
    1篇
  • 自定义控件
    4篇
  • 自定义控件基础知识
    3篇
  • 安卓系统跟进
    1篇
  • 安卓下载资源
  • 数据结构
    1篇
  • 版本管理
    10篇
  • 微信
    1篇
  • 开发技巧
    2篇
  • 安卓网络开发
    5篇
  • 前端
    7篇
  • 直播
    2篇
  • 安卓最基础知识
    3篇
  • 算法
    1篇
  • Retrofit
    1篇
  • RxJava
    1篇
  • http
    1篇
  • 数据库
    2篇

展开全部 收起

目录

评论
被折叠的  条评论 为什么被折叠? 到【灌水乐园】发言
查看更多评论
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值